Compare commits

...

271 Commits

Author SHA1 Message Date
Ani 2cd7238e76 rpcs3_version: Bump to 0.0.24 2022-08-31 23:24:42 +01:00
Ani 21f0043b27 gui/themes: YoRHa by Ani (r3) (#11585) 2022-08-31 22:47:42 +01:00
Elad Ashkenazi b7599c0f30 Update Thread.cpp 2022-08-31 22:19:05 +03:00
Elad Ashkenazi d8494ef1ae Update Thread.cpp 2022-08-31 22:19:05 +03:00
Elad Ashkenazi 727b32e886 RawSPU: Do not assume GPU memory fault on SPU MMIO 2022-08-31 22:19:05 +03:00
Ani 8124efa505 cubeb: Update from 2022.04.13 to 2022.07.21 2022-08-31 17:59:35 +01:00
Ani a5cff4d797 glext: Update from 2021.04.20 to 2022.05.30 2022-08-31 17:59:35 +01:00
Ani d1e5828644 XAudio2: Update from 1.2.8 to 1.2.9 2022-08-31 17:59:35 +01:00
Ani 18d73d6d76 FAudio: Update from 22.07 to 22.08 2022-08-31 17:59:35 +01:00
Ani 4a3a24dcc1 7z: Update from 21.07 to 22.01 2022-08-31 17:59:35 +01:00
Eladash 591159c4a9 Debugger: Prevent possible "current instruction" duplication when the thread is running 2022-08-30 08:57:33 +02:00
Eladash 7baf959975 Avoid empty resume output when using PPU debug option 2022-08-30 08:57:33 +02:00
Eladash 3f02935844 Add some debugging information 2022-08-30 08:57:33 +02:00
RipleyTom f98251a05a Fixes -w leaking 2022-08-30 07:46:42 +02:00
Nekotekina 80f0741103 simd_builder: fix constant locations 2022-08-29 14:32:56 +03:00
Nekotekina e28707055b Implement simd_builder for x86
ASMJIT-based tool for building vectorized loops (such as ones in BufferUtils.cpp)
2022-08-28 18:38:52 +03:00
brian218 698c3415ea Fixed SIG: Thread terminated due to fatal error: Failed to read hex string: invalid argument
Fixed the error which occurred when a game attempted to read the VID/PID of an undefined VFS USB device:

SIG: Thread terminated due to fatal error: Failed to read hex string: invalid argument
(in file C:\Users\ContainerAdministrator\AppData\Local\Temp\cirrus-ci-build\rpcs3\Emu\Cell\lv2\sys_fs.cpp:1942[:25], in function sys_fs_fcntl)
2022-08-26 08:45:14 +02:00
Eladash daf43989fc Thread.h: Improve thread abort performance 2022-08-25 23:54:56 +03:00
Eladash c7586de280 Savestates/sys_fs: Fix saving encrypted and split-files file descriptors 2022-08-25 23:54:56 +03:00
Eladash 506b9deec5 Savestates/SPU LLVM: Improve saving performance 2022-08-25 23:54:56 +03:00
kd-11 1fc0191311 Fix build 2022-08-23 23:49:46 +03:00
kd-11 1f9e04f72d rsx/vk: Implement flushing surface cache blocks to linear mem 2022-08-23 23:49:46 +03:00
Eladash a71bdc761e SPU/Debugger: Improve debugging reservations
Simplify and make the debugging results more consistent.
2022-08-22 18:24:26 +03:00
Eladash 9d8f4d5cfa Bugfix after #12523 2022-08-22 18:24:26 +03:00
Eladash ee87fdc869 Fix GETLLAR spin detection 2022-08-22 08:33:22 +03:00
Eladash bf63a18c5f SPU Add ability to specify percentage of busy waiting 2022-08-21 15:02:01 +03:00
Eladash ddba30988b vm: Attempt to not remove cpu_flag::wait flag during access violation 2022-08-21 15:02:01 +03:00
Eladash b0e2c959eb SPU: Disable notification if no changes were made in PUTLLC 2022-08-21 15:02:01 +03:00
Eladash 28bec8e1bf SPU: Implement custom reservation condition in atomic wait 2022-08-21 15:02:01 +03:00
Eladash 86702186f2 PPU: Detect lwmutex reservation pattern and optimize it 2022-08-21 15:02:01 +03:00
Eladash 82b1a2bd7a SPU: add the concept of inaccurate reservations
Implement cellSpursRequestIdleSpu
2022-08-21 15:02:01 +03:00
Eladash d36cf71a2d Savestates: Fix PPU analysis when patches are applied 2022-08-21 15:02:01 +03:00
Eladash 1dd1062be1 PPU LLVM: Fix HLE function injection 2022-08-21 15:02:01 +03:00
Eladash c0e3b86064 SPU: Optimize spu_thread::get_events() 2022-08-21 15:02:01 +03:00
Eladash 6210a8491f SPU: Optimize and enable SPU GETLLAR Polling detection by default
* Make this setting guard all reservation waitings. (renamed)
* Revert atomic list usage: it's more expensive and not needed because it has a timeout and is not optimized for the rest of the waitables.
2022-08-21 15:02:01 +03:00
Eladash 33a4f05ffa SPU: Interleave loads/stores in reservation access utilities 2022-08-21 15:02:01 +03:00
Ani a74d3005f8 vk: Update FSR from 1.0 to 1.0.2 (#12538) 2022-08-20 17:37:42 +01:00
kd-11 bca833dad7 Fix surface reuse 2022-08-20 01:23:15 +03:00
kd-11 f981e05908 rsx: Do not lie about surface details 2022-08-20 01:23:15 +03:00
kd-11 b5abd777b0 rsx: Allow longer dispatch queues to accomodate games with high draw call count 2022-08-19 20:29:32 +03:00
Elad Ashkenazi b2c9add47e rsx: Fix semaphore timeout on boot
Allow semaphore timeout to be disabled again.
2022-08-19 15:40:20 +03:00
kd-11 a401a192b8 Fixup for dst_stage 2022-08-19 14:29:20 +03:00
kd-11 ad1b007dd1 Fix whitespace 2022-08-19 14:29:20 +03:00
kd-11 71e35c8b4d vk: Implement support for VK_EXT_attachment_feedback_loop_layout 2022-08-19 14:29:20 +03:00
kd-11 2e504b2dac rsx: Silence some warnings 2022-08-19 14:29:20 +03:00
Jan Beich 6e9df9e452 CI: upgrade FreeBSD to 13.1
FreeBSD doesn't support /latest and /quarterly package repos on EOL
versions. 13.0 reaches EOL on 2022-08-31, so avoid CI breakage.
2022-08-19 13:46:01 +03:00
Trevor Mack 8aa5899196 Update BUILDING.md
Updating the 'Configuring the Qt plugin' section with more details and for the new versions of the plugin.
2022-08-18 08:41:46 +02:00
Nekotekina b6608ad31d vm_native: rewrite win7 workaround 2022-08-15 12:26:17 +03:00
kd-11 edb39039dd Fix linux build 2022-08-14 23:53:50 +03:00
kd-11 bacf518189 rsx: Fix 2D intersection tests 2022-08-14 23:53:50 +03:00
kd-11 1244044647 util: Provide stack-trace utilities 2022-08-14 23:53:50 +03:00
kd-11 b960ce1426 vk: Align write length when pre-filling buffers with constant patterns 2022-08-14 23:53:50 +03:00
kd-11 c55a889c23 vk: Initialize buffer info blocks to avoid null descriptors 2022-08-14 23:53:50 +03:00
Megamouse 829af30568 evdev: add motion device 2022-08-14 11:04:57 +02:00
Eladash a3007e11ca SPU: Fix minor race in sys_spu_thread_receive_event
Check final cpu_state::state value for suspend state because that's the variable the thread waits on.
2022-08-12 15:20:48 +03:00
Eladash 9a981b5292 rsx_debugger: Fix memory leak 2022-08-12 15:20:48 +03:00
Eladash 4464a6c3f6 CG-Disasm: Name input/output vetex arrays 2022-08-12 15:20:48 +03:00
Eladash d30ac20fad Debugger: Fix and use centered PC by default
* Fix instruction-selection dependent functionality.
* Remove odd instruction position jumps in add/remove breakpoint.
* Make PC fixate at 1/3 of the frame because knowing future instructions is more important than knowing the previous.
2022-08-12 15:20:48 +03:00
Nekotekina 7bb2d94e53 vm: use sparse files to emulate overcommit memory
Fix shm::map_critical page flags.
2022-08-12 13:59:03 +03:00
Eladash 7ff4509858 sys_lwcond: Fix a loose notification in cond wait 2022-08-11 12:44:40 +03:00
Eladash f2997e4c00 LV2: Fix the most annoying race ever
Timeline of the race:
1. The PPU is in SLEEP state. state = suspend.
2. lv2_obj::awake is called on the traced thread and is now in ONPROC state, state = signal.
3. lv2_obj::awake is called by another thread externally with a priority higher than our traced thread and appends it to g_pending. state = suspend + signal.
4. lv2_obj::sleep/set_priority (higering priority) is called on any thread which is in ONPROC. Causing it to enter SLEEP or RUNNING state, while the traced thread is back in queue in ONPROC. state = suspend + signal.
5. The traced thread finally calls lv2_obj::awake on itself, g_pending decrements to 0 and we a have a rescheduling event, after XOR state = 0!!! (no signal)
6. In check_state: cpu_sleep_called is now true and remains this way.
7. Another thread with a higher prioty kicks in and appends the traced thread into g_pending. state = suspend.
8. The traced thread is at cpu_thread::cpu_wait(), and that's where it's gonna spend the rest of its life.
2022-08-11 12:44:40 +03:00
Eladash 133e9d4705 CPUThread.cpp: Fix cpu_flag::pending reset 2022-08-11 11:42:16 +03:00
Eladash 501ca4bcf5 logs: Prevent potential deadlock of the main thread in stack mode
If a thread logs faster than the GUI thread can print on screen.
2022-08-11 11:42:16 +03:00
Eladash 9212942724 kernel_explorer: Add info about ppu_thread::ack_suspend
The wording is bad but unless the emulation went downhill it shouldn't print on screen repeatedly.
2022-08-11 11:42:16 +03:00
Eladash 683c1a6512 LV2: Optimize sys_ppu_thread a bit 2022-08-11 11:42:16 +03:00
Eladash 9b0d33048c Savestates/LV2: Fix race on _sys_lwcond_queue_wait saving
cpu_flag::again modification requires exclusive access.
2022-08-11 11:42:16 +03:00
Eladash bc8bf2c329 sys_cond/lwcond: Fix waiting time in the debugger for mutex lock 2022-08-11 11:42:16 +03:00
Elad Ashkenazi f0002501f7 sys_mutex/lwmutex: Regression fixes after #12378 2022-08-11 11:42:16 +03:00
Elad Ashkenazi c4cc0154be LV2: Optimizations and fixes
Fix and optimize sys_ppu_thread_yield

Fix LV2 syscalls with timeout bug. (use ppu_thread::cancel_sleep instead)

Move timeout notification out of mutex scope

Allow g_waiting timeouts to be awaked in scope
2022-08-11 11:42:16 +03:00
shinra-electric cba4c3cdc4 [macOS] Update hid_pad_handler.cpp to prevent DS4 from disconnecting (#12493)
Prevents DS4 (and possibly DS3) from disconnecting from bluetooth after 15 minutes by using hidapi's non-exclusive mode. 
The most recent version of hidapi, 0.12 from two months ago, added "macOS-specific function(s) to open device(s) in non-exclusive mode".
2022-08-11 07:14:37 +02:00
kd-11 c51d3b5465 Workaround for msvc weirdness 2022-08-09 18:32:54 +03:00
kd-11 e179adc4a0 rsx: Refactor surface cache storage 2022-08-09 18:32:54 +03:00
Elad Ashkenazi 47f3740a70 System.cpp: Replace std::filesystem usage (#12474) 2022-08-08 05:46:32 +03:00
nastys 2880370047 Update MoltenVK (#12456) 2022-08-07 20:54:31 +01:00
kd-11 61a055a1c6 Tuning 2022-08-07 22:14:49 +03:00
kd-11 64b4cfa59f rsx: Erase surface background when reloading after a pitch mismatch 2022-08-07 22:14:49 +03:00
kd-11 c799ffd223 rsx: Stubs for pitch conversion 2022-08-07 22:14:49 +03:00
Eladash 2ec039365f shared_mutex: Optimize busy-waiting by detecting waiters and try to steal the notifying bit
Add an unused has_waiters() method.
2022-08-07 20:23:54 +03:00
Eladash 26e731b487 LV2: Re-add dropped optimization by previous commit
Currently only for lock-free syscalls.
2022-08-07 20:23:54 +03:00
Eladash 34bae90820 LV2: Move nearly all notifications out of all mutex scopes including IDM 2022-08-07 20:23:54 +03:00
Eladash b55a052a22 IdManager.h: Do not lock IDM mutex if the preliminary ID check failed
Optimizes common ESRCH failures. (lambda variants only atm)
2022-08-07 20:23:54 +03:00
Eladash 011aabe9ed LV2: Make sys_mutex and sys_lwmutex lock-free, add some busy waiting in sys_mutex_lock 2022-08-07 20:23:54 +03:00
Eladash a17a6527f6 LV2: Move memory unlocking outside of mutex ownership and make it conditional 2022-08-07 20:23:54 +03:00
Eladash 122c6256ca A fix of _spurs::add_workload 2022-08-07 20:23:54 +03:00
Eladash 73aaff1b29 LV2: allocation-free synchronization syscalls
* Show waiters' ID in kernel explorer.
* Remove deque dependency from sys_sync.h
2022-08-07 20:23:54 +03:00
Eladash c7fbc16357 SPU: Postpone notifications to afterward group mutex ownership 2022-08-07 20:23:54 +03:00
Eladash bc728db15b LV2: Add busy waiting before entering atomic wait 2022-08-07 20:23:54 +03:00
Eladash 2eebbd307d LV2: Minor optimization regarding signal flag 2022-08-07 20:23:54 +03:00
Eladash dc851a729e LV2: Postpone thread notifications to afterward mutex ownership(s) 2022-08-07 20:23:54 +03:00
Eladash 6007fd630f Do not use lv2_obj::g_waiting when not needed in sys_timer_usleep 2022-08-07 20:23:54 +03:00
Megamouse cb0ecb2afb cellPngEnc 2022-08-06 10:35:10 +02:00
sguo35 84a785ea67 arm64: implement pshufb intrinsic 2022-08-05 22:53:11 +02:00
sguo35 cc1e4d2194 ppu/arm64: don't restore rsp on ppu_gateway return
Don't restore rsp on ppu_gateway return, since it can be reentrant. Also
change some registers to align with the x86 version's 32bit/64bit
usages.
2022-08-05 22:53:11 +02:00
Megamouse b2d84dcebd Qt: Add save filtered log to menu of log viewer 2022-08-05 20:50:51 +02:00
Megamouse 958d77ba52 Qt: filter log viewer when opening a file 2022-08-05 20:50:51 +02:00
Megamouse c4b38869ab Input: fix pad initialization 2022-08-05 20:16:21 +02:00
Eladash fd1968dd22 Savestates: Save SFO directory path for PSN games 2022-08-05 19:19:16 +03:00
Elad Ashkenazi 88b80ebdf5 sys_net: Fix missing locks in some syscalls (#12459) 2022-08-04 21:31:34 +03:00
kd-11 2445ab8d8e Fix RSX capture playback 2022-08-04 19:01:45 +03:00
Elad Ashkenazi 0e1931f178 sys_lwcond: Fix rare race on mode 3 signal (#12413) 2022-08-04 14:13:51 +03:00
kd-11 3e923b4993 rsx: Optimize VTX_FMT_SNORM16 decoding
- Cuts down SNORM16 overhead by ~65%
2022-08-03 23:33:31 +03:00
kd-11 8181498d86 gl: Alias UBO/SSBO slots to avoid exceeding the available number of binding slots.
- The sets are different anyway and should not overwrite each other in a proper driver.
2022-08-03 23:33:31 +03:00
kd-11 57dd611111 gl: Fix incomplete stencil view of depth-stencil texture
- Samplers must use point sampling for stencil views
2022-08-03 23:33:31 +03:00
Eladash b3162bd41c rsx/vp: Fix SNORM16 vertex decoding 2022-08-03 18:11:46 +03:00
Elad Ashkenazi cd2adbad9a Update rsx_methods.cpp 2022-08-03 17:15:59 +03:00
Elad Ashkenazi 99730ac4f9 Update rsx_methods.cpp 2022-08-03 17:15:59 +03:00
Elad Ashkenazi d2ab3383ad Update rsx_methods.cpp 2022-08-03 17:15:59 +03:00
Elad Ashkenazi 3b15a6b39e Update rsx_methods.cpp 2022-08-03 17:15:59 +03:00
Elad Ashkenazi 651e58f443 rsx: Trivial optimization 2022-08-03 17:15:59 +03:00
Eladash 769f9e33e9 Savestates/RSX: Fix fifo_control::restore_state 2022-08-03 15:35:41 +03:00
Eladash cf77bb7cfc Savestates/MSVC: Replace bugged std::unique (compiler bug) 2022-08-03 15:35:41 +03:00
Megamouse c75b76ddf5 cellPhotoImport: fallback to extension if sub_type is unknown 2022-08-02 01:43:23 +02:00
Megamouse 4dec8d7925 cellPhotoImport: allow dev_hdd1 2022-08-02 01:43:23 +02:00
Megamouse 934e7b614c cellPhotoExport/cellVideoExport: fix filenames 2022-08-02 01:43:23 +02:00
Megamouse 416b14de72 cellPhotoExport: first implementation 2022-08-01 19:11:14 +02:00
Megamouse b87099c790 cellVideoExport: fix some copy pasta errors and prevent file overwrite 2022-08-01 19:11:14 +02:00
kd-11 052725fdc7 rsx: Do not require ZCULL buffer binding to enable ZPASS counting
- ZPASS data is still accessible in unbuffered mode.
  The only thing that buffered ZCULL enables is something closer to early-Z where large blocks of pixels can be dicarded earlier.
  It is strictly a performance optimization and not required for ZPASS to work.
- Update ZCULL stat calculations to take into account unbuffered Z
2022-08-01 00:23:54 +03:00
Megamouse f90b79791f HLE: fix file not found errors in media functions 2022-07-31 16:45:05 +02:00
RipleyTom 4c32c95b33 Enable wolfssl fast-math option (#12427) 2022-07-30 18:19:19 +01:00
Megamouse c00d47cc99 Update wolfssl to v5.4.0-stable 2022-07-30 14:33:14 +02:00
Megamouse ea53c7dc85 Update curl to curl-7_84_0 2022-07-30 14:33:14 +02:00
Megamouse 2b923c681e Update FAudio to 22.07 2022-07-30 14:33:14 +02:00
Elad Ashkenazi 7a8e918519 Update System.cpp 2022-07-30 08:33:09 +02:00
Elad Ashkenazi d99ae3809d Update keyboard_pad_handler.cpp 2022-07-29 13:45:19 +02:00
Malcolm Jestadt 51e6d0a336 SPU LLVM: Add integer compare optimization for FCMGT 2022-07-29 11:59:59 +03:00
Megamouse 228844c017 overlays: fix line wrapping and position of lines
- Fix off by one issue when we wrapping a line caused by unnecessary zeroed whitespaces.
- Fix centering of lines that end with carriage return caused by overzealous reset of counters.
- Remove fabs where there shouldn't be any
2022-07-29 09:26:45 +02:00
Megamouse 87e628a9e9 cellPhotoDecode: optimize get_scaled_image 2022-07-29 08:05:35 +02:00
Megamouse e6885e25b5 Qt: fix image ratio in screenshot_manager_dialog 2022-07-29 03:14:29 +02:00
Megamouse f31ffc4596 Implement cellPhotoDecode 2022-07-28 09:06:48 +02:00
Megamouse 4114f00c05 cellPhotoImport: fix vfs path and add some logging 2022-07-28 09:06:48 +02:00
Megamouse 8f7209f886 cellMusicSelectionContext: fix log message facepalm 2022-07-27 07:39:55 +02:00
Megamouse d80146c704 cellMusic: Fix resume, fast forward and rewind
- Sadly rewind does not work with the QMediaPlayer on windows
2022-07-27 07:39:55 +02:00
Megamouse 577f379a12 implement cellPhotoImport 2022-07-26 17:27:35 +02:00
Megamouse 3d73915358 cellSearch: improve populate_photo_info, read image headers 2022-07-26 17:27:35 +02:00
Megamouse a5317d50a5 cellMusicSelectionContext: log playlist 2022-07-26 17:27:35 +02:00
MSuih 143bd6cc80 Fix typo in tooltips.h 2022-07-25 21:28:24 +02:00
MSuih 5cc8f970c0 Fix minor issues with issue templates 2022-07-25 21:28:24 +02:00
kd-11 c9058280e0 vk: Fix a potential deadlock 2022-07-25 21:05:31 +03:00
kd-11 5af50cfd55 vk: Handle corner cases
- Fix up flush sequence in DMA handling (WCB)
- Do not request resource sharing if queue family is not different!
2022-07-25 21:05:31 +03:00
kd-11 d846142f0c vk: Reimplement compliant async texture streaming
- Use CONCURRENT queue access instead of fighting with queue acquire/release via submit chains.
  The minor benefits of forcing EXCLUSIVE mode are buried under the huge penalty of multiple vkQueueSubmit.
  Batching submits does not help alleviate this situation. We simply must avoid interrupting execution.
2022-07-25 21:05:31 +03:00
Elad Ashkenazi 0ba0f9d2b9 Disable Disc update redirection for manual ELF loader (#12381) 2022-07-22 20:35:15 +02:00
Elad Ashkenazi f4f9c1865e Savestates: Implement compatibility with disc Demon's Souls (#12379)
* Savestates: Implement compatibility with virtual /dev_bdvd/PS3_GAME
2022-07-22 19:23:23 +03:00
Megamouse c40439ae6b cellMusic/Decode: implement playlist shuffle and repeat 2022-07-22 08:42:43 +02:00
Megamouse 683fa2a392 Mount custom dev_bdvd if a disc game was mounted from dev_hdd0 2022-07-22 07:36:49 +02:00
kd-11 246bf1df64 Use C++17 ctor for string_view 2022-07-21 22:29:40 +03:00
kd-11 9a868e9239 gl: Silence compiler warning 2022-07-21 22:29:40 +03:00
kd-11 ab3cde1939 gl: Do some macro patching for intel driver 2022-07-21 22:29:40 +03:00
kd-11 bec3e156fb vk: Disable robust buffer access for ANV
- Robust access is nice, but we don't actually need it
2022-07-21 22:29:40 +03:00
Megamouse 086afbbaa5 overlays: implement back and focus in media_list_dialog 2022-07-21 01:36:33 +02:00
Megamouse cd4d73114c overlays: fix media_list_dialog title 2022-07-21 01:36:33 +02:00
Megamouse 14ca506774 Qt/savedata: use rsx overlays selection_code 2022-07-21 01:36:33 +02:00
MSuih 984bf67d84 Replace issue templates with issue forms (#12373) 2022-07-20 20:22:12 +01:00
Eladash 2b3ae8b638 Revert another change 2022-07-20 18:11:52 +03:00
Eladash bb9ae18c71 Savestates: Fix sys_rwlock_wunlock saving 2022-07-20 18:11:52 +03:00
Eladash ce01457510 Fix futile wait regression in sys_lwcond_signal 2022-07-20 18:11:52 +03:00
Megamouse d1a46b491e cellMusic: add some error checks and copy context to callback 2022-07-20 07:09:38 +02:00
Elad Ashkenazi 31df99f7d6 Logs: Flush unreported errors when turning off Stack Cell errors (#12366) 2022-07-19 17:05:51 +02:00
Eladash 5e11b2b056 log_frame: Minor fix for stack mode 2022-07-19 10:43:51 +03:00
Eladash d106934336 Savestates: Fix saving sys_mutex_unlock 2022-07-19 10:43:51 +03:00
Eladash 68718361a7 Allow non-existing paths in vfs::retrieve 2022-07-19 10:43:51 +03:00
Eladash c887865d54 Perform cleanup after Emulator::Load failures 2022-07-19 10:43:51 +03:00
Eladash dc48944979 Prefer BDVD over HDD0 in vfs::retrieve 2022-07-19 10:43:51 +03:00
Eladash d22b8b1d3a Savestates: Do not restart after Ctrl+S for now 2022-07-19 10:43:51 +03:00
Eladash 56619b20cf Savestates: savestate reload fix 2022-07-19 10:43:51 +03:00
Eladash 12ebf77e22 Savestates: save sys_spu_initialize settings 2022-07-19 10:43:51 +03:00
Megamouse 845af8bd96 cellGifDec: random stuff because I was bored 2022-07-18 22:23:25 +02:00
kd-11 680f08c2b8 gl: Destroy barrier signals correctly 2022-07-18 18:58:22 +03:00
kd-11 82bac4173e gl: Reuse scratch images 2022-07-18 18:58:22 +03:00
kd-11 8a8fda3e02 gl: Combine RGBA8/D24S8 readback and byteswap into one operation 2022-07-18 18:58:22 +03:00
kd-11 1c5b685398 gl: Only toggle state settings that are relevant to the current RSX state 2022-07-18 18:58:22 +03:00
kd-11 e95084f138 gl: Use DSA for imageview configuration and avoid needless bind operations 2022-07-18 18:58:22 +03:00
kd-11 e12d268662 gl: Implement support for texture1D decode 2022-07-18 18:58:22 +03:00
kd-11 6a3f17cd36 gl: Fix compute invocation counts for format handling code 2022-07-18 18:58:22 +03:00
Erik S. V. Jansson db0774a81b Fix spirv/glslang.vcxproj build to support spaces.
e.g. cloning into D:\Source Code\C++\rpcs3 will cause spirv and glslang
to error during MSBuild. All other parts of rpcs3 compile fine so these
are probably the only places which fail when the project is placed in a
path with spaces in it. I've at least been able to compile + run rpcs3.
2022-07-18 01:27:56 +03:00
Megamouse 34615d3a74 cellMic: slow down if no mics are configured 2022-07-17 19:31:05 +02:00
Zion Nimchuk 189499c10b Fix shellcheck errors 2022-07-17 15:14:02 +03:00
Zion Nimchuk de613a8a98 Docker version bump 2022-07-17 15:14:02 +03:00
Eladash c0369b2e10 sys_net: Implement sys_net_abort 2022-07-17 14:53:03 +03:00
Megamouse 07ebbb6c84 cellSysutilAvc2SetStreamingTarget: remove ensure 2022-07-16 20:09:13 +02:00
sguo35 73ed657e00 spu/arm64: fix 16 byte branch patch alignment 2022-07-15 12:37:33 +03:00
sguo35 b37217b181 windows: fix build for asmjit update 2022-07-15 12:37:33 +03:00
sguo35 b02e6e222f arm64: enable fma and "avx" on Apple and Cortex CPUs 2022-07-15 12:37:33 +03:00
sguo35 c52abed4d3 spu: implement ubertrampoline generator for arm64
Implement the ubertrampoline generator for arm64. It generally follows
the x86 version, but uses asmjit to generate code instead of writing raw
opcodes to memory, trading memory usage for readability. Currently the
trampoline implementation is fairly inefficient in terms of instruction
size and is substantially larger than the x86 version.
2022-07-15 12:37:33 +03:00
sguo35 9e57efe82c spu: implement assembly functions for arm64 2022-07-15 12:37:33 +03:00
sguo35 77ab872bec spu: remove rotqby C++ impl
rotqby C++ implementation is broken, since replacing it with the
intrinsic version reliably fixes spurs test. A conditional branch
immediately after a rotqby instruction will fail using the C++ version
but succeed using the intrinsic.
2022-07-15 12:37:33 +03:00
sguo35 27acebc5f5 spu: use portable llvm recompiler on arm64
Since there is not yet an arm64 version of the assembly (fast) version.
2022-07-15 12:37:33 +03:00
sguo35 2755d9941c ppu: fix a bug where arm64 sp wasn't being saved
ASMJIT can silently fail and drop instructions when invalid operations
are performed (e.g. loading/storing sp). Explicitly move sp to a gp
register before doing loads/stores to fix this.
2022-07-15 12:37:33 +03:00
sguo35 488982f424 spu: external function calls should be marked non-tail
Mark external function calls as non-tail, since they aren't tail calls
and assuming they are will cause returns to fail in Arm64 GHC CC.
2022-07-15 12:37:33 +03:00
sguo35 086e12c6ca mac: mmap calls should initialize with r/w page perms
Mac/Arm64 pages should be R/W by default due to 16k page
incompatibility. Without this there will be segfaults due to invalid
permissions.
2022-07-15 12:37:33 +03:00
sguo35 9b19f16698 arm64: add optimized 16byte ld/st for armv8.4a+
16B ldp/stp are atomic on v8.4a+. See Arm Architecture Reference Manual,
"Changes to single-copy atomicity in Armv8.4". Add load/release atomic
impls for this instruction and add detection for 8.4a+ capability.
2022-07-15 12:37:33 +03:00
sguo35 b13fd68848 [3rdparty] update asmjit dependency 2022-07-15 12:37:33 +03:00
Eladash 3e51426379 Savestates/SPU: Kill emulation when its safe to save SPU state 2022-07-15 09:30:53 +03:00
Megamouse 105781fa76 overlays: properly align lines with leading or trailing whitespace 2022-07-14 23:32:20 +02:00
Megamouse d2be12bb07 overlays: find missing characters lost during wrapped rendering 2022-07-14 23:32:20 +02:00
Megamouse fdc15e12c4 overlays: properly calculate offsets for wrapped text 2022-07-14 23:32:20 +02:00
Eladash e548743cbf Fixup rsx cpatures 2022-07-14 18:50:31 +03:00
Megamouse 7499f875a6 cellCrossControllerInitialize: add dialog 2022-07-13 19:12:46 +02:00
kd-11 cdef752a9c gl: Fix 2D->3D splat in CopyBufferToImage 2022-07-13 02:09:58 +03:00
kd-11 1483941bea gl: Implement row alignment in CopyBufferToImage routines 2022-07-13 02:09:58 +03:00
kd-11 9cc15be7f2 Fix linux build 2022-07-13 02:09:58 +03:00
kd-11 453e1bfaec gl: Silence compiler warning 2022-07-13 02:09:58 +03:00
kd-11 82439327fa gl: Support loading data from SSBO using compute shaders
- Gives better performance than using raw draw calls.
- Does not work with all formats. The draw call version is still used when needed.
2022-07-13 02:09:58 +03:00
kd-11 f60002e87d gl: Optimize memory barriers a bit
- Move waits to server side
- Increase the scratch buffer size to avoid waiting on barriers
2022-07-13 02:09:58 +03:00
kd-11 9fc6382909 gl: Finalize BGRA storage format internals
- Performance is terrible but it works properly now
2022-07-13 02:09:58 +03:00
kd-11 ebad08aa97 gl: Fix image creation for virtual formats 2022-07-13 02:09:58 +03:00
kd-11 599f1dd157 gl: Properly match BGRA RTT formats 2022-07-13 02:09:58 +03:00
kd-11 bb5ce67d57 gl: Handle corner cases for CopyBufferToImage
- Handle 3D textures and cubemaps
- Handle writing to mip > 0
2022-07-13 02:09:58 +03:00
kd-11 f948ce399e gl: Implement CopyBufferToImage in software
- Overrides the drivers CopyBufferToImage handling where possible
2022-07-13 02:09:58 +03:00
kd-11 954c60947d gl: Avoid calling gl functions without a context even if the object is GL_NONE
- While calling glDestroyXXXX with GL_NONE is a no-op, calling it without a context will crash some drivers.
2022-07-13 02:09:58 +03:00
kd-11 98b6783c05 gl: Fix image views broken after refactor 2022-07-13 02:09:58 +03:00
kd-11 0894d2886a Fix build 2022-07-13 02:09:58 +03:00
kd-11 4995b4abe3 gl: Do not use raw GL image copy command for RSX data 2022-07-13 02:09:58 +03:00
kd-11 35ef19cfc8 gl: Refactor the rest of GLHelpers 2022-07-13 02:09:58 +03:00
kd-11 09824a718f gl: Separate BGRA8 storage from RGBA8 2022-07-13 02:09:58 +03:00
Megamouse f4e85af53c sceNpUtil: implement skeleton bandwidth test 2022-07-12 22:45:40 +02:00
Eladash 74a909cfbc logs: Batch log messages (optimization) 2022-07-12 21:37:06 +02:00
Eladash 0bfdfd8433 Savestates: Implement Ctrl+R to reload the most recent savestate
Ctrl+R no longer means Resume emulation, this functionality has been transferred to Ctrl+P which is also capable of pausing the emulation. (so it's now a toggle)
2022-07-12 15:15:42 +03:00
Eladash 1f5cf776b2 Savestates: Fix "Suspend Emulation Savestate Mode" setting 2022-07-12 15:15:42 +03:00
Eladash cdd6840826 Savestates/SPU: Complete fix for saving sys_spu_thread_receive_event 2022-07-12 15:15:42 +03:00
Eladash ab27ee4cf4 Savestates/RSX: Save NV406E semaphore waiting 2022-07-12 15:15:42 +03:00
Eladash 6211295155 atomic.hpp: Truncation warning fix 2022-07-12 15:15:42 +03:00
Eladash befd7ceb89 Savestates/sys_spu: Minor fix in saving sys_spu_thread_receive_event 2022-07-10 14:19:59 +03:00
Eladash 2cead6f328 Savestates/SPU: Fix saving sys_spu_thread_send_event 2022-07-10 14:19:59 +03:00
Eladash 4e3d58f75a Savestates: Fix crash when savestating before PPU/SPU compilation finishes
Unintentional concurrent access to m_ar ptr itself.
2022-07-10 14:19:59 +03:00
Eladash 24fddf1ded rsx: Fix emu stopping crash when using multi-threaded rsx
FXO signaled abort before it completed its work, leading to unsignalled vk::fence and deadlock. Fix it by deregistering it from FXO.
2022-07-10 14:19:59 +03:00
Eladash 87cd65ff03 Savestates: support game collections 2022-07-10 14:19:59 +03:00
Eladash 219e3d61f8 Minor fix in shm::map_critical 2022-07-10 14:19:59 +03:00
Eladash 4ade06f36f Savestates/RSX: Restore the ZCULL control state
And fix the ZCULL control state at the initial state of RSX.
2022-07-10 14:19:59 +03:00
Nekotekina 786510a937 Use gettid() on Linux, don't use std::thread::id
pthread_self() returns a large opaque pointer which is harder to use.
2022-07-10 12:22:28 +03:00
Megamouse 0c6df39a45 cellSearch: more error checks and shuffle 2022-07-09 16:39:10 +02:00
Vestrel 648ad0cd90 cellAudio: fix downmix if device doesn't support multichannel (#12328)
* cellAudio: fix downmix if device doesn't support multichannel
* Downmix in 2 stages
2022-07-09 15:25:28 +02:00
Megamouse cd0d43382f Qt: fix custom settings audio device corruption 2022-07-08 23:56:10 +02:00
Vestrel 98b730c806 Audio: device switching and channel count detection (#12246) 2022-07-08 17:13:38 +02:00
Nekotekina 4b787b22c8 Implement FN (lambda shortener)
Useful for some higher order functions.
Allows to make short lambdas even shorter.
2022-07-08 14:47:41 +03:00
Nekotekina 5c6f5a1610 Cleanse LOG_CONSTINIT macro
Should be unnecessary.
2022-07-08 14:47:41 +03:00
Eladash 0b8d5f7b98 Savestates: Fix argv 2022-07-08 12:57:43 +03:00
Eladash 314d481dcd Savestates: Fix typo in page size setting 2022-07-08 12:57:43 +03:00
Eladash 0980c68e6f Savestates: Increase timeout for emulation stopping 2022-07-08 12:57:43 +03:00
Eladash 983bfa2088 Savestates: Decryption of executable fix 2022-07-08 12:57:43 +03:00
Eladash 3a9928de65 Savestates: Log file descriptor information 2022-07-08 12:57:43 +03:00
Eladash 1fd5fb2f7e Savestates: Fix minor race with LV2 timers 2022-07-08 12:57:43 +03:00
Eladash 4ac88fa8d3 Savestates/RSX: Save drawing context 2022-07-08 12:57:43 +03:00
Eladash 5f8f9e33f1 RSX/Savestates: Replace GCM hack with a proper fix 2022-07-08 12:57:43 +03:00
Eladash f0c71ae2ae Savestates: Fix saving sys_event_queue_destroy 2022-07-08 12:57:43 +03:00
Megamouse 0c7ecdc954 cellMusicDecode: add more error checks and silence some log messages 2022-07-08 07:55:46 +02:00
Elad Ashkenazi 73a687df7d Savestates/Not Savestates: Fix exitspawn to HDD0 executable with mounted BDVD 2022-07-07 21:59:39 +02:00
Megamouse b683110e72 cellGem/overlays: show cursor if necessary 2022-07-07 12:40:23 +02:00
Megamouse 3840015e7d cellGem: implement basic ds3 cursor movement 2022-07-06 21:49:31 +02:00
Megamouse 639b8112b6 Input: make move handler dynamic 2022-07-06 21:49:31 +02:00
Megamouse 2bf17f5d3c input: fix horrible pad hacks 2022-07-06 21:49:31 +02:00
Megamouse 4823d4c32a input: add background input option
Adds an option to disable background input to the IO tab in the settings dialog.
This will disable pad input as well as ps move and overlays input when the window is unfocused.
2022-07-06 21:49:31 +02:00
Eladash c8c035eeef Regression fix 2022-07-06 20:07:09 +02:00
Eladash 155bd09fd0 Savestates: Cleanup v128 usage
It's compatible with bitwise serialization so it is faster to load/save it this way.
2022-07-06 19:43:25 +03:00
Eladash f546e5a8ef VM/Savestates: Replace bugged read-only block optimization 2022-07-06 19:43:25 +03:00
Eladash 2ccb0c8f42 SPU LLVM/Savestates: Remove unneeded store insurance and add related fix 2022-07-06 19:43:25 +03:00
Eladash 05303ce6c6 GUI: regression fix
Improperly resolved git conflict when rebasing savestates.
2022-07-06 19:43:25 +03:00
Eladash d8f3029952 Log LV2 periodic timers 2022-07-06 19:43:25 +03:00
Eladash 09c28f75b3 Savestates: Fix LV2 timers (fixes audio in some games) 2022-07-06 19:43:25 +03:00
Eladash 57671e267d Savestates: save cellAudioOut 2022-07-06 19:43:25 +03:00
Eladash b692108f1e Savestates: HLE state saving POC in sys_lwmutex 2022-07-06 19:43:25 +03:00
Eladash 2815aecd0c Savestates: Save SPU decrementer state 2022-07-06 19:43:25 +03:00
Megamouse b9f2b1e7c6 Qt: add UUID reset button 2022-07-05 09:09:00 +02:00
Eladash bd9ba7ef1f Remove incorrect Emu.IsStopped() checks 2022-07-05 08:25:36 +02:00
Eladash a015bd1ebd Savestates: Add missing position placement 2022-07-05 08:25:36 +02:00
Eladash d6cf167d52 Savestates: Minor optimization of size 2022-07-05 08:25:36 +02:00
Eladash 3fc2194fd5 Savestates: Fix zombie PPU threads saving 2022-07-05 08:25:36 +02:00
Eladash 72c14994f0 Savestates: Fix memory containers initialization (invalidates old savestates) 2022-07-05 08:25:36 +02:00
kd-11 fddb6a31a7 Use utils::c_page_size 2022-07-04 22:35:05 +03:00
kd-11 278ae7763a Use better macro notation 2022-07-04 22:35:05 +03:00
kd-11 5cafaef0a9 Aarch64 fixes for RSX 2022-07-04 22:35:05 +03:00
Elad Ashkenazi fcd297ffb2 Savestates Support For PS3 Emulation (#10478) 2022-07-04 16:02:17 +03:00
440 changed files with 22915 additions and 8686 deletions
+11 -11
View File
@@ -1,9 +1,9 @@
#!/bin/sh -ex
# Setup Qt variables
export QT_BASE_DIR=/opt/qt${QTVERMIN}
export PATH=$QT_BASE_DIR/bin:$PATH
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib
export QT_BASE_DIR=/opt/qt"${QTVERMIN}"
export PATH="$QT_BASE_DIR"/bin:"$PATH"
export LD_LIBRARY_PATH="$QT_BASE_DIR"/lib/x86_64-linux-gnu:"$QT_BASE_DIR"/lib
if [ -z "$CIRRUS_CI" ]; then
cd rpcs3 || exit 1
@@ -23,19 +23,19 @@ mkdir build && cd build || exit 1
if [ "$COMPILER" = "gcc" ]; then
# These are set in the dockerfile
export CC=${GCC_BINARY}
export CXX=${GXX_BINARY}
export CC="${GCC_BINARY}"
export CXX="${GXX_BINARY}"
export LINKER=gold
# We need to set the following variables for LTO to link properly
export AR=/usr/bin/gcc-ar-$GCCVER
export RANLIB=/usr/bin/gcc-ranlib-$GCCVER
export AR=/usr/bin/gcc-ar-"$GCCVER"
export RANLIB=/usr/bin/gcc-ranlib-"$GCCVER"
export CFLAGS="-fuse-linker-plugin"
else
export CC=${CLANG_BINARY}
export CXX=${CLANGXX_BINARY}
export CC="${CLANG_BINARY}"
export CXX="${CLANGXX_BINARY}"
export LINKER=lld
export AR=/usr/bin/llvm-ar-$LLVMVER
export RANLIB=/usr/bin/llvm-ranlib-$LLVMVER
export AR=/usr/bin/llvm-ar-"$LLVMVER"
export RANLIB=/usr/bin/llvm-ranlib-"$LLVMVER"
fi
export CFLAGS="$CFLAGS -fuse-ld=${LINKER}"
+1 -1
View File
@@ -13,7 +13,7 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
ls ./appdir/usr/lib/
rm -r ./appdir/usr/share/doc
cp "$(readlink -f /lib/x86_64-linux-gnu/libnsl.so.1)" ./appdir/usr/lib/libnsl.so.1
export PATH=/rpcs3/build/squashfs-root/usr/bin/:${PATH}
export PATH=/rpcs3/build/squashfs-root/usr/bin/:"${PATH}"
# Embed newer libstdc++ for distros that don't come with it (ubuntu 16.04)
mkdir -p appdir/usr/optional/ ; mkdir -p appdir/usr/optional/libstdc++/
+2 -2
View File
@@ -61,7 +61,7 @@ windows_task:
linux_task:
container:
image: rpcs3/rpcs3-ci-bionic:1.3
image: rpcs3/rpcs3-ci-bionic:1.5
cpu: 4
memory: 16G
env:
@@ -108,7 +108,7 @@ freebsd_task:
matrix:
- name: Cirrus FreeBSD
freebsd_instance:
image_family: freebsd-13-0
image_family: freebsd-13-1
cpu: 8
memory: 8G
env:
@@ -1,66 +0,0 @@
---
name: Regression report
about: If something used to work before, but now it doesn't
title: ''
labels: ''
assignees: ''
---
## Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
## Quick summary
Please briefly describe what has stopped working correctly.
## Details
Please describe the regression as accurately as possible.
#### 0. Make sure you're running with settings as close to default as possible
* **Do NOT enable any emulator game patches when reporting issues**
* Only change settings that are required for the game to work
#### 1. Please provide _exact_ build (or commit) information that introduced the regression you're reporting.
* Please see [How to find the build that caused a regression](https://wiki.rpcs3.net/index.php?title=Help:Using_different_versions_of_RPCS3#How_to_find_the_build_that_caused_a_regression.3F) in our wiki.
* You can find [History of RPCS3 builds](https://rpcs3.net/compatibility?b) here.
#### 2. Please attach TWO logs:
* One for the build with regression.
* One for the build that works as expected.
##### How to obtain RPCS3's log:
* Run the game until you find the regression.
* Completely close RPCS3, or move to the next step in case it has crashed.
* Locate RPCS3's log file:
+ ```RPCS3.log``` (It can show up as just RPCS3 and have a notepad icon)
or
+ ```RPCS3.log.gz``` (It can show up as RPCS3.log and have a zip or rar icon)
![image](https://user-images.githubusercontent.com/44116740/84433247-aa15fa80-ac36-11ea-913e-6fe25a1d040e.png)
On Linux it will be in ```~/.cache/rpcs3/```.
On Windows it will be near the executable.
* Attach the log:
+ Drag and drop your log file into the issue.
+ Or upload it to the cloud, such as Dropbox, Mega etc.
#### 3. If you describe graphical regression, please provide an RSX capture and a RenderDoc capture that demonstrate it.
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
Captures will be stored in RPCS3 folder → captures.
+ Compress your capture with 7z, Rar etc.
And drag and drop it into the issue.
+ Or upload it to the cloud, such as Dropbox, Mega etc.
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
#### 4. Please attach screenshots of your problem.
* Enable performance overlay with at least medium level of detail.
You can find it in _Emulator_ tab in _Settings_.
#### 5. Please provide comparison with real PS3.
#### 6. Please provide your system configuration:
* OS
* CPU
* GPU
* Driver version
* etc.
#### Please include.
* Anything else you deem to be important
@@ -0,0 +1,93 @@
name: Regression report
description: If something used to work before, but now it doesn't
title: "[Regression] Enter a title here"
labels: []
body:
- type: markdown
attributes:
value: |
# Summary
Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
- type: textarea
id: quick-summary
attributes:
label: Quick summary
description: Please briefly describe what has stopped working correctly.
validations:
required: true
- type: textarea
id: details
attributes:
label: Details
description: Please describe the regression as accurately as possible. Include screenshots if neccessary.
validations:
required: false
- type: markdown
attributes:
value: |
# Identify the regressed build
Please provide the _exact_ build (or commit) information that introduced the regression you're reporting.
* Please see [How to find the build that caused a regression](https://wiki.rpcs3.net/index.php?title=Help:Using_different_versions_of_RPCS3#How_to_find_the_build_that_caused_a_regression.3F) in our wiki.
* You can find [History of RPCS3 builds](https://rpcs3.net/compatibility?b) here.
Make sure you're running with settings as close to default as possible
* **Do NOT enable any emulator game patches when reporting issues**
* Only change settings that are required for the game to work
- type: input
id: regressed-build
attributes:
label: Build with regression
description: Provide _exact_ build (or commit) information that introduced the regression you're reporting.
placeholder: v0.0.23-13948-31df99f7
validations:
required: true
- type: markdown
attributes:
value: |
# Log files
Obtaining the log file:
* Run the game until you find the regression.
* Completely close RPCS3 and locate the log file.
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
* On Windows it will be in the RPCS3 directory near the executable
* On Linux it will be in ```~/.cache/rpcs3/```
* On MacOS it will be in ```~/Library/Caches/rpcs3```. If you're unable to locate it copy paste the path in Spotlight and hit enter.
- type: textarea
id: logs
attributes:
label: Attach two log files
description: |
Attach one file for the build with regression and another for a build that works.
Drag & drop the files into this input field, or upload them to another service (f.ex. Dropbox, Mega) and provide a link.
validations:
required: true
- type: markdown
attributes:
value: |
# Other details
If you describe a graphical regression, please provide an RSX capture and/or a RenderDoc capture that demonstrate it.
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
* Captures will be stored in RPCS3 folder → captures.
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
- type: textarea
id: captures
attributes:
label: Attach capture files for visual issues
description: Compress your capture with 7z, Rar etc. and attach it here, or upload it to the cloud (Dropbox, Mega etc) and add a link to it.
validations:
required: false
- type: textarea
id: system-info
attributes:
label: System configuration
description: Provide information about your system, such as operating system, CPU and GPU model, GPU driver version and other information that describes your system configuration.
validations:
required: false
- type: textarea
id: other-details
attributes:
label: Other details
description: Include anything else you deem to be important.
validations:
required: false
-59
View File
@@ -1,59 +0,0 @@
---
name: Bug report
about: If something doesn't work correctly in RPCS3
title: ''
labels: ''
assignees: ''
---
## Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
## Quick summary
Please briefly describe what is not working correctly.
## Details
Please describe the problem as accurately as possible.
#### 0. Make sure you're running with settings as close to default as possible
* **Do NOT enable any emulator game patches when reporting issues**
* Only change settings that are required for the game to work
#### 1. Please attach RPCS3's log.
* Run the game until you find the issue.
* Completely close RPCS3, or move to the next step in case it has crashed.
* Locate RPCS3's log file:
+ ```RPCS3.log``` (It can show up as just RPCS3 and have a notepad icon)
or
+ ```RPCS3.log.gz``` (It can show up as RPCS3.log and have a zip or rar icon)
![image](https://user-images.githubusercontent.com/44116740/84433247-aa15fa80-ac36-11ea-913e-6fe25a1d040e.png)
On Linux it will be in ```~/.cache/rpcs3/```.
On Windows it will be near the executable.
* Attach the log:
+ Drag and drop your log file into the issue.
+ Or upload it to the cloud, such as Dropbox, Mega etc.
#### 2. If you describe graphical issue, please provide an RSX capture and a RenderDoc capture that demonstrate it.
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
Captures will be stored in RPCS3 folder → captures.
+ Compress your capture with 7z, Rar etc.
And drag and drop it into the issue.
+ Or upload it to the cloud, such as Dropbox, Mega etc.
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
#### 3. Please attach screenshots of your problem.
* Enable performance overlay with at least medium level of detail.
You can find it in _Emulator_ tab in _Settings_.
#### 4. Please provide comparison with real PS3.
#### 5. Please provide your system configuration:
* OS
* CPU
* GPU
* Driver version
* etc.
#### Please include.
* Anything else you deem to be important
+78
View File
@@ -0,0 +1,78 @@
name: Bug report
description: If something doesn't work correctly in RPCS3
title: "Enter a title here"
labels: []
body:
- type: markdown
attributes:
value: |
# Summary
Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
- type: textarea
id: quick-summary
attributes:
label: Quick summary
description: Please briefly describe what is not working correctly.
validations:
required: true
- type: textarea
id: details
attributes:
label: Details
description: |
Please describe the problem as accurately as possible.
Provide a comparison with a real PS3, if possible.
validations:
required: false
- type: markdown
attributes:
value: |
# Log files
Provide a log file that includes the bug you're reporting.
Obtaining the log file:
* Run the game until you find the regression.
* Completely close RPCS3 and locate the log file.
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
* On Windows it will be in the RPCS3 directory near the executable
* On Linux it will be in ```~/.cache/rpcs3/```
* On MacOS it will be in ```~/Library/Caches/rpcs3```. If you're unable to locate it copy paste the path in Spotlight and hit enter.
- type: textarea
id: logs
attributes:
label: Attach a log file
description: |
Drag & drop the files into this input field, or upload them to another service (f.ex. Dropbox, Mega) and provide a link.
validations:
required: true
- type: markdown
attributes:
value: |
# Other details
If you describe a graphical issue, please provide an RSX capture and/or a RenderDoc capture that demonstrate it.
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
* Captures will be stored in RPCS3 folder → captures.
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
- type: textarea
id: captures
attributes:
label: Attach capture files for visual issues
description: Compress your capture with 7z, Rar etc. and attach it here, or upload it to the cloud (Dropbox, Mega etc) and add a link to it.
validations:
required: false
- type: textarea
id: system-info
attributes:
label: System configuration
description: Provide information about your system, such as operating system, CPU and GPU model, GPU driver version and other information that describes your system configuration.
validations:
required: false
- type: textarea
id: other-details
attributes:
label: Other details
description: Include anything else you deem to be important.
validations:
required: false
@@ -1,41 +0,0 @@
---
name: Feature Request
about: If RPCS3 lacks a feature you would like to see
title: '[Feature Request] Enter Feature Title Here'
labels: ''
assignees: ''
---
## Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
## Quick summary
Please briefly describe what feature you would like RPCS3 to have.
## Details
Please describe the feature as accurately as possible.
#### 1. Please describe, what part of RPCS3 would be affected by your feature:
* Gameplay
* Debugging
* UI
* Patches
* Installation
* Github
* CI
* etc.
#### 2. Please tell us, why your feature is important to RPCS3.
#### 3. Please attach screenshots of the feature implemented in other projects.
#### 4. Please provide your system configuration:
* OS
* CPU
* GPU
* Driver version
* etc.
#### Please include.
* Anything else you deem to be important
@@ -0,0 +1,36 @@
name: Feature request
description: If RPCS3 lacks a feature you would like to see
title: "[Feature request] Enter a title here"
labels: []
body:
- type: markdown
attributes:
value: |
Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
- type: textarea
id: quick-summary
attributes:
label: Quick summary
description: Please briefly describe what feature you would like RPCS3 to have.
validations:
required: true
- type: textarea
id: details
attributes:
label: Details
description: Please describe the feature as accurately as possible.
validations:
required: false
- type: markdown
attributes:
value: |
Please include the following information:
* What part of RPCS3 would be affected by your feature? (Gameplay, Debugging, UI, Patches, Installation, CI etc.)
* Why your feature is important to RPCS3.
* If the feature is implemented in other projects, attach screenshots.
* If this feature is something that a game is trying to use, upload a log file for it.
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
* On Windows it will be in the RPCS3 directory near the executable
* On Linux it will be in ```~/.cache/rpcs3/```
* On MacOS it will be in ```~/Library/Caches/rpcs3```. If you're unable to locate it copy paste the path in Spotlight and hit enter.
+14
View File
@@ -0,0 +1,14 @@
---
name: Advanced
about: For developers and experienced users only
title: ''
labels: ''
assignees: ''
---
## Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
You're using the advanced template. You're expected to know what to write in order to fill in all the required information for proper report.
If you're unsure on what to do, please return back to the issue type selection and choose different category.
+9 -5
View File
@@ -1,5 +1,5 @@
/* 7zTypes.h -- Basic types
2021-12-25 : Igor Pavlov : Public domain */
2022-04-01 : Igor Pavlov : Public domain */
#ifndef __7Z_TYPES_H
#define __7Z_TYPES_H
@@ -133,10 +133,6 @@ typedef int WRes;
#define MY__E_ERROR_NEGATIVE_SEEK MY_HRESULT_FROM_errno_CONST_ERROR(EINVAL)
*/
// gcc / clang : (sizeof(long) == sizeof(void*)) in 32/64 bits
typedef long INT_PTR;
typedef unsigned long UINT_PTR;
#define TEXT(quote) quote
#define FILE_ATTRIBUTE_READONLY 0x0001
@@ -520,6 +516,14 @@ struct ISzAlloc
#endif
#define k_PropVar_TimePrec_0 0
#define k_PropVar_TimePrec_Unix 1
#define k_PropVar_TimePrec_DOS 2
#define k_PropVar_TimePrec_HighPrec 3
#define k_PropVar_TimePrec_Base 16
#define k_PropVar_TimePrec_100ns (k_PropVar_TimePrec_Base + 7)
#define k_PropVar_TimePrec_1ns (k_PropVar_TimePrec_Base + 9)
EXTERN_C_END
#endif
+5 -5
View File
@@ -1,7 +1,7 @@
#define MY_VER_MAJOR 21
#define MY_VER_MINOR 07
#define MY_VER_MAJOR 22
#define MY_VER_MINOR 01
#define MY_VER_BUILD 0
#define MY_VERSION_NUMBERS "21.07"
#define MY_VERSION_NUMBERS "22.01"
#define MY_VERSION MY_VERSION_NUMBERS
#ifdef MY_CPU_NAME
@@ -10,12 +10,12 @@
#define MY_VERSION_CPU MY_VERSION
#endif
#define MY_DATE "2021-12-26"
#define MY_DATE "2022-07-15"
#undef MY_COPYRIGHT
#undef MY_VERSION_COPYRIGHT_DATE
#define MY_AUTHOR_NAME "Igor Pavlov"
#define MY_COPYRIGHT_PD "Igor Pavlov : Public domain"
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2021 Igor Pavlov"
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2022 Igor Pavlov"
#ifdef USE_COPYRIGHT_CR
#define MY_COPYRIGHT MY_COPYRIGHT_CR
+10 -7
View File
@@ -1,5 +1,5 @@
/* CpuArch.h -- CPU specific code
2021-07-13 : Igor Pavlov : Public domain */
2022-07-15 : Igor Pavlov : Public domain */
#ifndef __CPU_ARCH_H
#define __CPU_ARCH_H
@@ -123,12 +123,15 @@ MY_CPU_64BIT means that processor can work with 64-bit registers.
#endif
#if defined(__sparc64__)
#define MY_CPU_NAME "sparc64"
#define MY_CPU_64BIT
#elif defined(__sparc__)
#define MY_CPU_NAME "sparc"
/* #define MY_CPU_32BIT */
#if defined(__riscv) \
|| defined(__riscv__)
#if __riscv_xlen == 32
#define MY_CPU_NAME "riscv32"
#elif __riscv_xlen == 64
#define MY_CPU_NAME "riscv64"
#else
#define MY_CPU_NAME "riscv"
#endif
#endif
+6 -2
View File
@@ -1,5 +1,5 @@
/* DllSecur.c -- DLL loading security
2021-12-25 : Igor Pavlov : Public domain */
2022-07-15 : Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -11,6 +11,10 @@
#ifndef UNDER_CE
#if defined(__GNUC__) && (__GNUC__ >= 8)
#pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
typedef BOOL (WINAPI *Func_SetDefaultDllDirectories)(DWORD DirectoryFlags);
#define MY_LOAD_LIBRARY_SEARCH_USER_DIRS 0x400
@@ -34,7 +38,7 @@ static const char * const g_Dlls =
#endif
// #define MY_CAST_FUNC (void(*)())
#define MY_CAST_FUNC
#define MY_CAST_FUNC
void My_SetDefaultDllDirectories()
{
+4 -7
View File
@@ -1,5 +1,5 @@
/* LzmaEnc.c -- LZMA Encoder
2021-11-18: Igor Pavlov : Public domain */
2022-07-15: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -2970,6 +2970,7 @@ const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
}
// (desiredPackSize == 0) is not allowed
SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, BoolInt reInit,
Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
{
@@ -2990,14 +2991,10 @@ SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, BoolInt reInit,
if (reInit)
LzmaEnc_Init(p);
LzmaEnc_InitPrices(p);
nowPos64 = p->nowPos64;
RangeEnc_Init(&p->rc);
p->rc.outStream = &outStream.vt;
if (desiredPackSize == 0)
return SZ_ERROR_OUTPUT_EOF;
nowPos64 = p->nowPos64;
res = LzmaEnc_CodeOneBlock(p, desiredPackSize, *unpackSize);
*unpackSize = (UInt32)(p->nowPos64 - nowPos64);
+24 -3
View File
@@ -153,6 +153,20 @@ typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
@@ -235,14 +249,21 @@ typedef unsigned short int khronos_uint16_t;
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
+36 -2
View File
@@ -32,9 +32,9 @@ extern "C" {
#define GLAPI extern
#endif
#define GL_GLEXT_VERSION 20210420
#define GL_GLEXT_VERSION 20220530
#include "khrplatform.h"
#include <KHR/khrplatform.h>
/* Generated C header for:
* API: gl
@@ -8214,6 +8214,10 @@ GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers);
#define GL_EXT_shader_integer_mix 1
#endif /* GL_EXT_shader_integer_mix */
#ifndef GL_EXT_shader_samples_identical
#define GL_EXT_shader_samples_identical 1
#endif /* GL_EXT_shader_samples_identical */
#ifndef GL_EXT_shadow_funcs
#define GL_EXT_shadow_funcs 1
#endif /* GL_EXT_shadow_funcs */
@@ -8625,6 +8629,36 @@ GLAPI void APIENTRY glTextureNormalEXT (GLenum mode);
#define GL_RGBA_SNORM 0x8F93
#endif /* GL_EXT_texture_snorm */
#ifndef GL_EXT_texture_storage
#define GL_EXT_texture_storage 1
#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F
#define GL_RGBA32F_EXT 0x8814
#define GL_RGB32F_EXT 0x8815
#define GL_ALPHA32F_EXT 0x8816
#define GL_LUMINANCE32F_EXT 0x8818
#define GL_LUMINANCE_ALPHA32F_EXT 0x8819
#define GL_RGBA16F_EXT 0x881A
#define GL_RGB16F_EXT 0x881B
#define GL_ALPHA16F_EXT 0x881C
#define GL_LUMINANCE16F_EXT 0x881E
#define GL_LUMINANCE_ALPHA16F_EXT 0x881F
#define GL_BGRA8_EXT 0x93A1
#define GL_R8_EXT 0x8229
#define GL_RG8_EXT 0x822B
#define GL_R32F_EXT 0x822E
#define GL_RG32F_EXT 0x8230
#define GL_R16F_EXT 0x822D
#define GL_RG16F_EXT 0x822F
typedef void (APIENTRYP PFNGLTEXSTORAGE1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);
typedef void (APIENTRYP PFNGLTEXSTORAGE2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
typedef void (APIENTRYP PFNGLTEXSTORAGE3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glTexStorage1DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);
GLAPI void APIENTRY glTexStorage2DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
GLAPI void APIENTRY glTexStorage3DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
#endif
#endif /* GL_EXT_texture_storage */
#ifndef GL_EXT_texture_swizzle
#define GL_EXT_texture_swizzle 1
#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42
+18 -18
View File
@@ -747,12 +747,12 @@ AF1 sharpness){
// Immediate constants for peak range.
AF2 peakC=AF2(1.0,-1.0*4.0);
// Limiters, these need to be high precision RCPs.
AF1 hitMinR=mn4R*ARcpF1(AF1_(4.0)*mx4R);
AF1 hitMinG=mn4G*ARcpF1(AF1_(4.0)*mx4G);
AF1 hitMinB=mn4B*ARcpF1(AF1_(4.0)*mx4B);
AF1 hitMaxR=(peakC.x-mx4R)*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
AF1 hitMaxG=(peakC.x-mx4G)*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
AF1 hitMaxB=(peakC.x-mx4B)*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R);
AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G);
AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B);
AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
AF1 lobeR=max(-hitMinR,hitMaxR);
AF1 lobeG=max(-hitMinG,hitMaxG);
AF1 lobeB=max(-hitMinB,hitMaxB);
@@ -845,12 +845,12 @@ AF1 sharpness){
// Immediate constants for peak range.
AH2 peakC=AH2(1.0,-1.0*4.0);
// Limiters, these need to be high precision RCPs.
AH1 hitMinR=mn4R*ARcpH1(AH1_(4.0)*mx4R);
AH1 hitMinG=mn4G*ARcpH1(AH1_(4.0)*mx4G);
AH1 hitMinB=mn4B*ARcpH1(AH1_(4.0)*mx4B);
AH1 hitMaxR=(peakC.x-mx4R)*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
AH1 hitMaxG=(peakC.x-mx4G)*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
AH1 hitMaxB=(peakC.x-mx4B)*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R);
AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G);
AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B);
AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
AH1 lobeR=max(-hitMinR,hitMaxR);
AH1 lobeG=max(-hitMinG,hitMaxG);
AH1 lobeB=max(-hitMinB,hitMaxB);
@@ -963,12 +963,12 @@ AF1 sharpness){
// Immediate constants for peak range.
AH2 peakC=AH2(1.0,-1.0*4.0);
// Limiters, these need to be high precision RCPs.
AH2 hitMinR=mn4R*ARcpH2(AH2_(4.0)*mx4R);
AH2 hitMinG=mn4G*ARcpH2(AH2_(4.0)*mx4G);
AH2 hitMinB=mn4B*ARcpH2(AH2_(4.0)*mx4B);
AH2 hitMaxR=(peakC.x-mx4R)*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
AH2 hitMaxG=(peakC.x-mx4G)*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
AH2 hitMaxB=(peakC.x-mx4B)*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R);
AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G);
AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B);
AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
AH2 lobeR=max(-hitMinR,hitMaxR);
AH2 lobeG=max(-hitMinG,hitMaxG);
AH2 lobeB=max(-hitMinB,hitMaxB);
+1 -1
View File
@@ -4,7 +4,7 @@ include(ExternalProject)
ExternalProject_Add(moltenvk
GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git
GIT_TAG 1236d2f
GIT_TAG 1112005
BUILD_IN_SOURCE 1
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK
CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos
-7
View File
@@ -1,7 +0,0 @@
diff --git a/ExternalRevisions/SPIRV-Cross_repo_revision b/ExternalRevisions/SPIRV-Cross_repo_revision
index 777346a0..c1971b45 100644
--- a/ExternalRevisions/SPIRV-Cross_repo_revision
+++ b/ExternalRevisions/SPIRV-Cross_repo_revision
@@ -1 +1 @@
-0d4ce028bf8b8a94d325dc1e1c20446153ba19c4
+44c3333a1c315ead00c24f7aef5fa8a7ccf49299
+9 -9
View File
@@ -39,28 +39,28 @@
<PropertyGroup Label="UserMacros">
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers').Replace('\', '/')) SPIRV-Tools -B build</CmakeReleaseCLI>
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers').Replace('\', '/'))" SPIRV-Tools -B build</CmakeReleaseCLI>
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers').Replace('\', '/')) -S SPIRV-Tools -B build</CmakeDebugCLI>
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers').Replace('\', '/'))" -S SPIRV-Tools -B build</CmakeDebugCLI>
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<NMakeBuildCommandLine>$(CmakeReleaseCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeBuildCommandLine>
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>$(CmakeReleaseCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeReBuildCommandLine>
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>$(CmakeReleaseCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeCleanCommandLine>
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeCleanCommandLine>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<NMakeBuildCommandLine>$(CmakeDebugCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>$(CmakeDebugCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>$(CmakeDebugCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
</NMakeCleanCommandLine>
</PropertyGroup>
<ItemDefinitionGroup>
@@ -68,4 +68,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
+1 -1
View File
@@ -27,7 +27,7 @@ target_compile_definitions(soundtouch PUBLIC
SOUNDTOUCH_FLOAT_SAMPLES;
)
target_compile_options(soundtouch PUBLIC "-w")
target_compile_options(soundtouch PRIVATE "-w")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|X86|amd64|AMD64|em64t|EM64T)")
target_compile_definitions(soundtouch PUBLIC
File diff suppressed because it is too large Load Diff
Binary file not shown.
+5 -3
View File
@@ -62,12 +62,13 @@
<ClCompile Include="asmjit\src\asmjit\arm\a64builder.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64compiler.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64emithelper.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64formatter.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64func.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64instapi.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64instdb.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64operand.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64rapass.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\armformatter.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\armfunc.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="asmjit\src\asmjit\core\api-build_p.h" />
@@ -134,11 +135,14 @@
<ClInclude Include="asmjit\src\asmjit\x86\x86opcode_p.h" />
<ClInclude Include="asmjit\src\asmjit\x86\x86operand.h" />
<ClInclude Include="asmjit\src\asmjit\x86\x86rapass_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64archtraits_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64assembler.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64builder.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64compiler.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64emithelper_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64emitter.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64formatter_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64func_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64globals.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64instapi_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64instdb.h" />
@@ -146,9 +150,7 @@
<ClInclude Include="asmjit\src\asmjit\arm\a64operand.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64rapass_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64utils.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armarchtraits_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armformatter_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armfunc_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armglobals.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armoperand.h" />
</ItemGroup>
+5 -3
View File
@@ -52,12 +52,13 @@
<ClCompile Include="asmjit\src\asmjit\arm\a64builder.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64compiler.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64emithelper.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64formatter.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64func.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64instapi.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64instdb.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64operand.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\a64rapass.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\armformatter.cpp" />
<ClCompile Include="asmjit\src\asmjit\arm\armfunc.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="asmjit\src\asmjit\core\api-build_p.h" />
@@ -124,11 +125,14 @@
<ClInclude Include="asmjit\src\asmjit\x86\x86opcode_p.h" />
<ClInclude Include="asmjit\src\asmjit\x86\x86operand.h" />
<ClInclude Include="asmjit\src\asmjit\x86\x86rapass_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64archtraits_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64assembler.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64builder.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64compiler.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64emithelper_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64emitter.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64formatter_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64func_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64globals.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64instapi_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64instdb.h" />
@@ -136,9 +140,7 @@
<ClInclude Include="asmjit\src\asmjit\arm\a64operand.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64rapass_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\a64utils.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armarchtraits_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armformatter_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armfunc_p.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armglobals.h" />
<ClInclude Include="asmjit\src\asmjit\arm\armoperand.h" />
</ItemGroup>
+1
View File
@@ -12,6 +12,7 @@ else()
option(CURL_USE_WOLFSSL "enable wolfSSL for SSL/TLS" ON)
option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" ON)
option(CURL_ZLIB "Set to ON to enable building curl with zlib support." OFF)
option(USE_LIBIDN2 "Use libidn2 for IDN support" OFF) # Disabled because MacOs CI doesn't work otherwise
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
if(USE_MSVC_STATIC_CRT)
set(CURL_STATIC_CRT ON CACHE BOOL "Use static crt to build curl" FORCE)
+3
View File
@@ -98,6 +98,7 @@
<ClCompile Include="curl\lib\escape.c" />
<ClCompile Include="curl\lib\file.c" />
<ClCompile Include="curl\lib\fileinfo.c" />
<ClCompile Include="curl\lib\fopen.c" />
<ClCompile Include="curl\lib\formdata.c" />
<ClCompile Include="curl\lib\ftp.c" />
<ClCompile Include="curl\lib\ftplistparser.c" />
@@ -279,9 +280,11 @@
<ClInclude Include="curl\lib\dynbuf.h" />
<ClInclude Include="curl\lib\easyif.h" />
<ClInclude Include="curl\lib\easyoptions.h" />
<ClInclude Include="curl\lib\easy_lock.h" />
<ClInclude Include="curl\lib\escape.h" />
<ClInclude Include="curl\lib\file.h" />
<ClInclude Include="curl\lib\fileinfo.h" />
<ClInclude Include="curl\lib\fopen.h" />
<ClInclude Include="curl\lib\formdata.h" />
<ClInclude Include="curl\lib\ftp.h" />
<ClInclude Include="curl\lib\ftplistparser.h" />
+9
View File
@@ -480,6 +480,9 @@
<ClCompile Include="curl\lib\timediff.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\fopen.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="curl\include\curl\curl.h">
@@ -965,6 +968,12 @@
<ClInclude Include="curl\lib\timediff.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\easy_lock.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\fopen.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="curl\lib\libcurl.rc">
+7 -7
View File
@@ -46,21 +46,21 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<NMakeBuildCommandLine>$(CmakeReleaseCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeBuildCommandLine>
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>$(CmakeReleaseCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeReBuildCommandLine>
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>$(CmakeReleaseCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeCleanCommandLine>
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeCleanCommandLine>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<NMakeBuildCommandLine>$(CmakeDebugCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>$(CmakeDebugCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>$(CmakeDebugCLI)
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
</NMakeCleanCommandLine>
</PropertyGroup>
<ItemDefinitionGroup>
@@ -68,4 +68,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
+1 -1
View File
@@ -19,7 +19,7 @@ else()
set(WOLFSSL_CHACHA "yes" CACHE INTERNAL "")
set(WOLFSSL_FILESYSTEM "yes" CACHE INTERNAL "")
set(WOLFSSL_PWDBASED "yes" CACHE INTERNAL "")
set(WOLFSSL_FAST_MATH "no" CACHE INTERNAL "")
set(WOLFSSL_FAST_MATH "yes" CACHE INTERNAL "")
set(WOLFSSL_EXAMPLES "no" CACHE INTERNAL "")
set(WOLFSSL_CRYPT_TESTS "no" CACHE INTERNAL "")
set(WOLFSSL_ASYNC_THREADS "no" CACHE INTERNAL "")
+2
View File
@@ -43,6 +43,8 @@
#define HAVE_ECC
#define ECC_SHAMIR
#define ECC_TIMING_RESISTANT
#define USE_FAST_MATH
#define FP_MAX_BITS 8192
/* UTF-8 aware filesystem functions for Windows */
#define WOLFSSL_USER_FILESYSTEM
+4 -3
View File
@@ -102,9 +102,10 @@ git submodule update --init
#### Configuring the Qt plugin (if used)
1) Go to the Qt5 menu and edit Qt5 options.
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.15.2\msvc2019_64`.
3) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
1) Go to `Extensions->Qt VS Tools->Qt Versions`.
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.15.2\msvc2019_64`, version will fill in automatically.
3) Go to `Extensions->Qt VS Tools->Options->Legacy Project Format`.
4) Set `Build: Run pre-build setup` to `true`.
#### Building the projects
+33 -3
View File
@@ -71,6 +71,10 @@ namespace fs
s64 atime;
s64 mtime;
s64 ctime;
using enable_bitcopy = std::true_type;
constexpr bool operator==(const stat_t&) const = default;
};
// Helper, layout is equal to iovec struct
@@ -105,6 +109,8 @@ namespace fs
: stat_t{}
{
}
using enable_bitcopy = std::false_type;
};
// Directory handle base
@@ -681,9 +687,10 @@ namespace fs
T obj;
u64 pos;
container_stream(T&& obj)
container_stream(T&& obj, const stat_t& init_stat = {})
: obj(std::forward<T>(obj))
, pos(0)
, m_stat(init_stat)
{
}
@@ -694,6 +701,7 @@ namespace fs
bool trunc(u64 length) override
{
obj.resize(length);
update_time(true);
return true;
}
@@ -708,6 +716,7 @@ namespace fs
{
std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max, static_cast<value_type*>(buffer));
pos = pos + max;
update_time();
return max;
}
}
@@ -743,6 +752,7 @@ namespace fs
obj.insert(obj.end(), src + overlap, src + size);
pos += size;
if (size) update_time(true);
return size;
}
@@ -767,13 +777,33 @@ namespace fs
{
return obj.size();
}
stat_t stat() override
{
return m_stat;
}
private:
stat_t m_stat{};
void update_time(bool write = false)
{
// TODO: Accurate timestamps
m_stat.atime++;
if (write)
{
m_stat.mtime = std::max(m_stat.atime, ++m_stat.mtime);
m_stat.ctime = m_stat.mtime;
}
}
};
template <typename T>
file make_stream(T&& container = T{})
file make_stream(T&& container = T{}, const stat_t& stat = stat_t{})
{
file result;
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container)));
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container), stat));
return result;
}
+436
View File
@@ -7,6 +7,8 @@
#include "mutex.h"
#include "util/vm.hpp"
#include "util/asm.hpp"
#include "util/v128.hpp"
#include "util/simd.hpp"
#include <charconv>
#include <zlib.h>
@@ -351,6 +353,435 @@ asmjit::inline_runtime::~inline_runtime()
utils::memory_protect(m_data, m_size, utils::protection::rx);
}
#if defined(ARCH_X64)
asmjit::simd_builder::simd_builder(CodeHolder* ch) noexcept
: native_asm(ch)
{
_init(true);
consts[~v128()] = this->newLabel();
}
asmjit::simd_builder::~simd_builder()
{
}
void asmjit::simd_builder::_init(bool full)
{
if (full && utils::has_avx512_icl())
{
v0 = x86::zmm0;
v1 = x86::zmm1;
v2 = x86::zmm2;
v3 = x86::zmm3;
v4 = x86::zmm4;
v5 = x86::zmm5;
vsize = 64;
}
else if (full && utils::has_avx2())
{
v0 = x86::ymm0;
v1 = x86::ymm1;
v2 = x86::ymm2;
v3 = x86::ymm3;
v4 = x86::ymm4;
v5 = x86::ymm5;
vsize = 32;
}
else
{
v0 = x86::xmm0;
v1 = x86::xmm1;
v2 = x86::xmm2;
v3 = x86::xmm3;
v4 = x86::xmm4;
v5 = x86::xmm5;
vsize = 16;
}
if (full && utils::has_avx512())
{
vmask = -1;
}
else
{
vmask = 0;
}
}
void asmjit::simd_builder::operator()() noexcept
{
for (auto&& [x, y] : consts)
{
this->align(AlignMode::kData, 16);
this->bind(y);
this->embed(&x, 16);
}
}
void asmjit::simd_builder::vec_cleanup_ret()
{
if (utils::has_avx() && vsize > 16)
this->vzeroupper();
this->ret();
}
void asmjit::simd_builder::vec_set_all_zeros(const Operand& v)
{
x86::Xmm reg(v.id());
if (utils::has_avx())
this->vpxor(reg, reg, reg);
else
this->xorps(reg, reg);
}
void asmjit::simd_builder::vec_set_all_ones(const Operand& v)
{
x86::Xmm reg(v.id());
if (x86::Zmm zr(v.id()); zr == v)
this->vpternlogd(zr, zr, zr, 0xff);
else if (x86::Ymm yr(v.id()); yr == v)
this->vpcmpeqd(yr, yr, yr);
else if (utils::has_avx())
this->vpcmpeqd(reg, reg, reg);
else
this->pcmpeqd(reg, reg);
}
void asmjit::simd_builder::vec_set_const(const Operand& v, const v128& val)
{
if (!val._u)
return vec_set_all_zeros(v);
if (!~val._u)
return vec_set_all_ones(v);
else
{
Label co = consts[val];
if (!co.isValid())
co = consts[val] = this->newLabel();
if (x86::Zmm zr(v.id()); zr == v)
this->vbroadcasti32x4(zr, x86::oword_ptr(co));
else if (x86::Ymm yr(v.id()); yr == v)
this->vbroadcasti128(yr, x86::oword_ptr(co));
else if (utils::has_avx())
this->vmovaps(x86::Xmm(v.id()), x86::oword_ptr(co));
else
this->movaps(x86::Xmm(v.id()), x86::oword_ptr(co));
}
}
void asmjit::simd_builder::vec_clobbering_test(u32 esize, const Operand& v, const Operand& rhs)
{
if (esize == 64)
{
this->emit(x86::Inst::kIdVptestmd, x86::k0, v, rhs);
this->ktestw(x86::k0, x86::k0);
}
else if (esize == 32)
{
this->emit(x86::Inst::kIdVptest, v, rhs);
}
else if (esize == 16 && utils::has_sse41())
{
this->emit(x86::Inst::kIdPtest, v, rhs);
}
else
{
if (v != rhs)
this->emit(x86::Inst::kIdPand, v, rhs);
if (esize == 16)
this->emit(x86::Inst::kIdPacksswb, v, v);
this->emit(x86::Inst::kIdMovq, x86::rax, v);
if (esize == 16 || esize == 8)
this->test(x86::rax, x86::rax);
else if (esize == 4)
this->test(x86::eax, x86::eax);
else if (esize == 2)
this->test(x86::ax, x86::ax);
else if (esize == 1)
this->test(x86::al, x86::al);
else
fmt::throw_exception("Unimplemented");
}
}
asmjit::x86::Mem asmjit::simd_builder::ptr_scale_for_vec(u32 esize, const x86::Gp& base, const x86::Gp& index)
{
switch (ensure(esize))
{
case 1: return x86::ptr(base, index, 0, 0);
case 2: return x86::ptr(base, index, 1, 0);
case 4: return x86::ptr(base, index, 2, 0);
case 8: return x86::ptr(base, index, 3, 0);
default: fmt::throw_exception("Bad esize");
}
}
void asmjit::simd_builder::vec_load_unaligned(u32 esize, const Operand& v, const x86::Mem& src)
{
ensure(std::has_single_bit(esize));
ensure(std::has_single_bit(vsize));
if (esize == 2)
{
ensure(vsize >= 2);
if (vsize == 2)
vec_set_all_zeros(v);
if (vsize == 2 && utils::has_avx())
this->emit(x86::Inst::kIdVpinsrw, x86::Xmm(v.id()), x86::Xmm(v.id()), src, Imm(0));
else if (vsize == 2)
this->emit(x86::Inst::kIdPinsrw, v, src, Imm(0));
else if (vmask && vmask < 8)
this->emit(x86::Inst::kIdVmovdqu16, v, src);
else
return vec_load_unaligned(vsize, v, src);
}
else if (esize == 4)
{
ensure(vsize >= 4);
if (vsize == 4 && utils::has_avx())
this->emit(x86::Inst::kIdVmovd, x86::Xmm(v.id()), src);
else if (vsize == 4)
this->emit(x86::Inst::kIdMovd, v, src);
else if (vmask && vmask < 8)
this->emit(x86::Inst::kIdVmovdqu32, v, src);
else
return vec_load_unaligned(vsize, v, src);
}
else if (esize == 8)
{
ensure(vsize >= 8);
if (vsize == 8 && utils::has_avx())
this->emit(x86::Inst::kIdVmovq, x86::Xmm(v.id()), src);
else if (vsize == 8)
this->emit(x86::Inst::kIdMovq, v, src);
else if (vmask && vmask < 8)
this->emit(x86::Inst::kIdVmovdqu64, v, src);
else
return vec_load_unaligned(vsize, v, src);
}
else if (esize >= 16)
{
ensure(vsize >= 16);
if (utils::has_avx())
this->emit(x86::Inst::kIdVmovdqu, v, src);
else
this->emit(x86::Inst::kIdMovups, v, src);
}
else
{
fmt::throw_exception("Unimplemented");
}
}
void asmjit::simd_builder::vec_store_unaligned(u32 esize, const Operand& v, const x86::Mem& dst)
{
ensure(std::has_single_bit(esize));
ensure(std::has_single_bit(vsize));
if (esize == 2)
{
ensure(vsize >= 2);
if (vsize == 2 && utils::has_avx())
this->emit(x86::Inst::kIdVpextrw, dst, x86::Xmm(v.id()), Imm(0));
else if (vsize == 2 && utils::has_sse41())
this->emit(x86::Inst::kIdPextrw, dst, v, Imm(0));
else if (vsize == 2)
this->push(x86::rax), this->pextrw(x86::eax, x86::Xmm(v.id()), 0), this->mov(dst, x86::ax), this->pop(x86::rax);
else if ((vmask && vmask < 8) || vsize >= 64)
this->emit(x86::Inst::kIdVmovdqu16, dst, v);
else
return vec_store_unaligned(vsize, v, dst);
}
else if (esize == 4)
{
ensure(vsize >= 4);
if (vsize == 4 && utils::has_avx())
this->emit(x86::Inst::kIdVmovd, dst, x86::Xmm(v.id()));
else if (vsize == 4)
this->emit(x86::Inst::kIdMovd, dst, v);
else if ((vmask && vmask < 8) || vsize >= 64)
this->emit(x86::Inst::kIdVmovdqu32, dst, v);
else
return vec_store_unaligned(vsize, v, dst);
}
else if (esize == 8)
{
ensure(vsize >= 8);
if (vsize == 8 && utils::has_avx())
this->emit(x86::Inst::kIdVmovq, dst, x86::Xmm(v.id()));
else if (vsize == 8)
this->emit(x86::Inst::kIdMovq, dst, v);
else if ((vmask && vmask < 8) || vsize >= 64)
this->emit(x86::Inst::kIdVmovdqu64, dst, v);
else
return vec_store_unaligned(vsize, v, dst);
}
else if (esize >= 16)
{
ensure(vsize >= 16);
if ((vmask && vmask < 8) || vsize >= 64)
this->emit(x86::Inst::kIdVmovdqu64, dst, v); // Not really needed
else if (utils::has_avx())
this->emit(x86::Inst::kIdVmovdqu, dst, v);
else
this->emit(x86::Inst::kIdMovups, dst, v);
}
else
{
fmt::throw_exception("Unimplemented");
}
}
void asmjit::simd_builder::_vec_binary_op(x86::Inst::Id sse_op, x86::Inst::Id vex_op, x86::Inst::Id evex_op, const Operand& dst, const Operand& lhs, const Operand& rhs)
{
if (utils::has_avx())
{
if (vex_op == x86::Inst::kIdNone || this->_extraReg.isReg())
{
this->evex().emit(evex_op, dst, lhs, rhs);
}
else
{
this->emit(vex_op, dst, lhs, rhs);
}
}
else if (dst == lhs)
{
this->emit(sse_op, dst, rhs);
}
else if (dst == rhs)
{
fmt::throw_exception("Unimplemented");
}
else
{
this->emit(x86::Inst::kIdMovaps, dst, lhs);
this->emit(sse_op, dst, rhs);
}
}
void asmjit::simd_builder::vec_umin(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs)
{
using enum x86::Inst::Id;
if (esize == 2)
{
if (utils::has_sse41())
return _vec_binary_op(kIdPminuw, kIdVpminuw, kIdVpminuw, dst, lhs, rhs);
}
else if (esize == 4)
{
if (utils::has_sse41())
return _vec_binary_op(kIdPminud, kIdVpminud, kIdVpminud, dst, lhs, rhs);
}
fmt::throw_exception("Unimplemented");
}
void asmjit::simd_builder::vec_umax(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs)
{
using enum x86::Inst::Id;
if (esize == 2)
{
if (utils::has_sse41())
return _vec_binary_op(kIdPmaxuw, kIdVpmaxuw, kIdVpmaxuw, dst, lhs, rhs);
}
else if (esize == 4)
{
if (utils::has_sse41())
return _vec_binary_op(kIdPmaxud, kIdVpmaxud, kIdVpmaxud, dst, lhs, rhs);
}
fmt::throw_exception("Unimplemented");
}
void asmjit::simd_builder::vec_umin_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp)
{
using enum x86::Inst::Id;
if (!utils::has_sse41())
{
fmt::throw_exception("Unimplemented");
}
ensure(src != tmp);
if (esize == 2)
{
this->emit(utils::has_avx() ? kIdVphminposuw : kIdPhminposuw, x86::Xmm(tmp.id()), x86::Xmm(src.id()));
this->emit(utils::has_avx() ? kIdVpextrw : kIdPextrw, dst, x86::Xmm(tmp.id()), Imm(0));
}
else if (esize == 4)
{
if (utils::has_avx())
{
this->vpsrldq(x86::Xmm(tmp.id()), x86::Xmm(src.id()), 8);
this->vpminud(x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
this->vpsrldq(x86::Xmm(src.id()), x86::Xmm(tmp.id()), 4);
this->vpminud(x86::Xmm(src.id()), x86::Xmm(src.id()), x86::Xmm(tmp.id()));
this->vmovd(dst.r32(), x86::Xmm(src.id()));
}
else
{
this->movdqa(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
this->psrldq(x86::Xmm(tmp.id()), 8);
this->pminud(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
this->movdqa(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
this->psrldq(x86::Xmm(src.id()), 4);
this->pminud(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
this->movd(dst.r32(), x86::Xmm(src.id()));
}
}
else
{
fmt::throw_exception("Unimplemented");
}
}
void asmjit::simd_builder::vec_umax_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp)
{
using enum x86::Inst::Id;
if (!utils::has_sse41())
{
fmt::throw_exception("Unimplemented");
}
ensure(src != tmp);
if (esize == 2)
{
vec_set_all_ones(x86::Xmm(tmp.id()));
vec_xor(esize, x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
this->emit(utils::has_avx() ? kIdVphminposuw : kIdPhminposuw, x86::Xmm(tmp.id()), x86::Xmm(tmp.id()));
this->emit(utils::has_avx() ? kIdVpextrw : kIdPextrw, dst, x86::Xmm(tmp.id()), Imm(0));
this->not_(dst.r16());
}
else if (esize == 4)
{
if (utils::has_avx())
{
this->vpsrldq(x86::Xmm(tmp.id()), x86::Xmm(src.id()), 8);
this->vpmaxud(x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
this->vpsrldq(x86::Xmm(src.id()), x86::Xmm(tmp.id()), 4);
this->vpmaxud(x86::Xmm(src.id()), x86::Xmm(src.id()), x86::Xmm(tmp.id()));
this->vmovd(dst.r32(), x86::Xmm(src.id()));
}
else
{
this->movdqa(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
this->psrldq(x86::Xmm(tmp.id()), 8);
this->pmaxud(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
this->movdqa(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
this->psrldq(x86::Xmm(src.id()), 4);
this->pmaxud(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
this->movd(dst.r32(), x86::Xmm(src.id()));
}
}
else
{
fmt::throw_exception("Unimplemented");
}
}
#endif /* X86 */
#ifdef LLVM_AVAILABLE
#include <unordered_map>
@@ -996,6 +1427,11 @@ bool jit_compiler::check(const std::string& path)
return false;
}
void jit_compiler::update_global_mapping(const std::string& name, u64 addr)
{
m_engine->updateGlobalMapping(name, addr);
}
void jit_compiler::fin()
{
m_engine->finalizeObject();
+161 -34
View File
@@ -42,6 +42,7 @@
#include <string>
#include <string_view>
#include <unordered_map>
#include <util/v128.hpp>
#if defined(ARCH_X64)
using native_asm = asmjit::x86::Assembler;
@@ -211,40 +212,136 @@ namespace asmjit
}
#if defined(ARCH_X64)
template <uint Size>
struct native_vec;
template <>
struct native_vec<16> { using type = x86::Xmm; };
template <>
struct native_vec<32> { using type = x86::Ymm; };
template <>
struct native_vec<64> { using type = x86::Zmm; };
template <uint Size>
using native_vec_t = typename native_vec<Size>::type;
// if (count > step) { for (; ctr < (count - step); ctr += step) {...} count -= ctr; }
inline void build_incomplete_loop(native_asm& c, auto ctr, auto count, u32 step, auto&& build)
struct simd_builder : native_asm
{
asmjit::Label body = c.newLabel();
asmjit::Label exit = c.newLabel();
std::unordered_map<v128, Label> consts;
ensure((step & (step - 1)) == 0);
c.cmp(count, step);
c.jbe(exit);
c.sub(count, step);
c.align(asmjit::AlignMode::kCode, 16);
c.bind(body);
build();
c.add(ctr, step);
c.sub(count, step);
c.ja(body);
c.add(count, step);
c.bind(exit);
}
Operand v0, v1, v2, v3, v4, v5;
uint vsize = 16;
uint vmask = 0;
simd_builder(CodeHolder* ch) noexcept;
~simd_builder();
void operator()() noexcept;
void _init(bool full);
void vec_cleanup_ret();
void vec_set_all_zeros(const Operand& v);
void vec_set_all_ones(const Operand& v);
void vec_set_const(const Operand& v, const v128& value);
void vec_clobbering_test(u32 esize, const Operand& v, const Operand& rhs);
// return x86::ptr(base, ctr, X, 0) where X is set for esize accordingly
x86::Mem ptr_scale_for_vec(u32 esize, const x86::Gp& base, const x86::Gp& index);
void vec_load_unaligned(u32 esize, const Operand& v, const x86::Mem& src);
void vec_store_unaligned(u32 esize, const Operand& v, const x86::Mem& dst);
void vec_partial_move(u32 esize, const Operand& dst, const Operand& src);
void _vec_binary_op(x86::Inst::Id sse_op, x86::Inst::Id vex_op, x86::Inst::Id evex_op, const Operand& dst, const Operand& lhs, const Operand& rhs);
void vec_shuffle_xi8(const Operand& dst, const Operand& lhs, const Operand& rhs)
{
using enum x86::Inst::Id;
_vec_binary_op(kIdPshufb, kIdVpshufb, kIdVpshufb, dst, lhs, rhs);
}
void vec_xor(u32, const Operand& dst, const Operand& lhs, const Operand& rhs)
{
using enum x86::Inst::Id;
_vec_binary_op(kIdPxor, kIdVpxor, kIdVpxord, dst, lhs, rhs);
}
void vec_or(u32, const Operand& dst, const Operand& lhs, const Operand& rhs)
{
using enum x86::Inst::Id;
_vec_binary_op(kIdPor, kIdVpor, kIdVpord, dst, lhs, rhs);
}
void vec_umin(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs);
void vec_umax(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs);
void vec_umin_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp);
void vec_umax_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp);
simd_builder& keep_if_not_masked()
{
if (vmask && vmask < 8)
{
this->k(x86::KReg(vmask));
}
return *this;
}
simd_builder& zero_if_not_masked()
{
if (vmask && vmask < 8)
{
this->k(x86::KReg(vmask));
this->z();
}
return *this;
}
void build_loop(u32 esize, auto reg_ctr, auto reg_cnt, auto&& build, auto&& reduce)
{
ensure((esize & (esize - 1)) == 0);
ensure(esize <= vsize);
Label body = this->newLabel();
Label next = this->newLabel();
Label exit = this->newLabel();
const u32 step = vsize / esize;
this->xor_(reg_ctr.r32(), reg_ctr.r32()); // Reset counter reg
this->sub(reg_cnt, step);
this->jb(next); // If count < step, skip main loop body
this->align(AlignMode::kCode, 16);
this->bind(body);
build();
this->add(reg_ctr, step);
this->sub(reg_cnt, step);
this->ja(body);
this->bind(next);
if (!vmask)
reduce();
this->add(reg_cnt, step);
this->jz(exit);
if (vmask)
{
// Build single last iteration (masked)
this->bzhi(reg_cnt, x86::Mem(consts[~u128()], 0), reg_cnt);
this->kmovq(x86::k7, reg_cnt);
vmask = 7;
build();
vmask = -1;
reduce();
}
else
{
// Build tail loop (reduced vector width)
Label body = this->newLabel();
this->align(AlignMode::kCode, 16);
this->bind(body);
const uint vsz = vsize / step;
this->_init(false);
vsize = vsz;
build();
this->_init(true);
this->inc(reg_ctr);
this->sub(reg_cnt, 1);
this->ja(body);
}
this->bind(exit);
}
};
// for (; count > 0; ctr++, count--)
inline void build_loop(native_asm& c, auto ctr, auto count, auto&& build)
@@ -262,19 +359,40 @@ namespace asmjit
c.ja(body);
c.bind(exit);
}
inline void maybe_flush_lbr(native_asm& c, uint count = 2)
{
// Workaround for bad LBR callstacks which happen in some situations (mainly TSX) - execute additional RETs
Label next = c.newLabel();
c.lea(x86::rcx, x86::qword_ptr(next));
for (u32 i = 0; i < count; i++)
{
c.push(x86::rcx);
c.sub(x86::rcx, 16);
}
for (u32 i = 0; i < count; i++)
{
c.ret();
c.align(asmjit::AlignMode::kCode, 16);
}
c.bind(next);
}
#endif
}
// Build runtime function with asmjit::X86Assembler
template <typename FT, typename Asm = native_asm, typename F>
inline FT build_function_asm(std::string_view name, F&& builder)
inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime* custom_runtime = nullptr)
{
#ifdef __APPLE__
pthread_jit_write_protect_np(false);
#endif
using namespace asmjit;
auto& rt = get_global_runtime();
auto& rt = custom_runtime ? *custom_runtime : get_global_runtime();
CodeHolder code;
code.init(rt.environment());
@@ -312,6 +430,12 @@ inline FT build_function_asm(std::string_view name, F&& builder)
builder(compiler, args);
}
if constexpr (std::is_invocable_r_v<void, Asm>)
{
// Finalization
compiler();
}
const auto result = rt._add(&code);
jit_announce(result, code.codeSize(), name);
return reinterpret_cast<FT>(uptr(result));
@@ -362,6 +486,9 @@ public:
// Add object (path to obj file)
void add(const std::string& path);
// Update global mapping for a single value
void update_global_mapping(const std::string& name, u64 addr);
// Check object file
static bool check(const std::string& path);
+19 -3
View File
@@ -5,6 +5,8 @@
#include "util/logs.hpp"
#include "util/v128.hpp"
#include <locale>
#include <codecvt>
#include <algorithm>
#include <string_view>
#include "Thread.h"
@@ -13,8 +15,6 @@
#include <Windows.h>
#else
#include <errno.h>
#include <locale>
#include <codecvt>
#endif
std::string wchar_to_utf8(std::wstring_view src)
@@ -26,11 +26,17 @@ std::string wchar_to_utf8(std::wstring_view src)
WideCharToMultiByte(CP_UTF8, 0, src.data(), src.size(), utf8_string.data(), tmp_size, nullptr, nullptr);
return utf8_string;
#else
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter{};
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter{};
return converter.to_bytes(src.data());
#endif
}
std::string utf16_to_utf8(std::u16string_view src)
{
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter{};
return converter.to_bytes(src.data());
}
std::wstring utf8_to_wchar(std::string_view src)
{
#ifdef _WIN32
@@ -609,3 +615,13 @@ bool fmt::match(const std::string& source, const std::string& mask)
return true;
}
std::string get_file_extension(const std::string& file_path)
{
if (usz dotpos = file_path.find_last_of('.'); dotpos != std::string::npos && dotpos + 1 < file_path.size())
{
return file_path.substr(dotpos + 1);
}
return {};
}
+4
View File
@@ -10,6 +10,7 @@
std::wstring utf8_to_wchar(std::string_view src);
std::string wchar_to_utf8(std::wstring_view src);
std::string utf16_to_utf8(std::u16string_view src);
// Copy null-terminated string from a std::string or a char array to a char array with truncation
template <typename D, typename T>
@@ -26,6 +27,9 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max);
// Convert string to unsigned integer
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max);
// Get the file extension of a file path ("png", "jpg", etc.)
std::string get_file_extension(const std::string& file_path);
namespace fmt
{
std::string replace_all(std::string_view src, std::string_view from, std::string_view to, usz count = -1);
+12 -16
View File
@@ -8,7 +8,6 @@
#include "Thread.h"
#include "Utilities/JIT.h"
#include <thread>
#include <sstream>
#include <cfenv>
#ifdef _WIN32
@@ -17,6 +16,7 @@
#include <process.h>
#include <sysinfoapi.h>
#else
#define _GNU_SOURCE
#ifdef __APPLE__
#define _XOPEN_SOURCE
#define __USE_GNU
@@ -96,14 +96,6 @@ extern thread_local std::string(*g_tls_log_prefix)();
// Report error and call std::abort(), defined in main.cpp
[[noreturn]] void report_fatal_error(std::string_view);
template <>
void fmt_class_string<std::thread::id>::format(std::string& out, u64 arg)
{
std::ostringstream ss;
ss << get_object(arg);
out += ss.str();
}
std::string dump_useful_thread_info()
{
thread_local volatile bool guard = false;
@@ -1248,16 +1240,18 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
const auto cpu = get_current_cpu_thread();
if (rsx::g_access_violation_handler)
if (addr < RAW_SPU_BASE_ADDR && vm::check_addr(addr) && rsx::g_access_violation_handler)
{
bool state_changed = false;
if (cpu)
{
vm::temporary_unlock(*cpu);
state_changed = vm::temporary_unlock(*cpu);
}
bool handled = rsx::g_access_violation_handler(addr, is_writing);
if (cpu && (cpu->state += cpu_flag::temp, cpu->test_stopped()))
if (state_changed && (cpu->state += cpu_flag::temp, cpu->test_stopped()))
{
//
}
@@ -1536,14 +1530,14 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
// If we fail due to being busy, wait a bit and try again.
while (static_cast<u32>(sending_error) == CELL_EBUSY)
{
thread_ctrl::wait_for(1000);
sending_error = sys_event_port_send(pf_port_id, data1, data2, data3);
if (cpu->is_stopped())
{
sending_error = {};
break;
}
thread_ctrl::wait_for(1000);
sending_error = sys_event_port_send(pf_port_id, data1, data2, data3);
}
if (sending_error)
@@ -1644,7 +1638,7 @@ static void append_thread_name(std::string& msg)
}
else
{
fmt::append(msg, "Thread id = %s.\n", std::this_thread::get_id());
fmt::append(msg, "Thread id = %u.\n", thread_ctrl::get_tid());
}
}
@@ -3139,6 +3133,8 @@ u64 thread_ctrl::get_tid()
{
#ifdef _WIN32
return GetCurrentThreadId();
#elif defined(__linux__)
return syscall(SYS_gettid);
#else
return reinterpret_cast<u64>(pthread_self());
#endif
+9 -1
View File
@@ -635,13 +635,21 @@ public:
// Join thread by thread_state::finished
named_thread& operator=(thread_state s)
{
bool notify_sync = false;
if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
{
notify_sync = true;
}
if constexpr (std::is_assignable_v<Context&, thread_state>)
{
static_cast<Context&>(*this) = s;
}
if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
if (notify_sync)
{
// Notify after context abortion has been made so all conditions for wake-up be satisfied by the time of notification
thread::m_sync.notify_one(1);
}
+10 -11
View File
@@ -1,37 +1,36 @@
#pragma once
#include "util/types.hpp"
#include "util/vm.hpp"
#include "StrFmt.h"
#include <vector>
#include <algorithm>
namespace utils
{
class address_range_vector;
/**
* Constexprs
* Helpers
*/
constexpr inline u32 page_start(u32 addr)
static inline u32 page_start(u32 addr)
{
return addr & ~4095u;
return addr & ~(c_page_size - 1);
}
constexpr inline u32 next_page(u32 addr)
static inline u32 next_page(u32 addr)
{
return page_start(addr + 4096u);
return page_start(addr) + c_page_size;
}
constexpr inline u32 page_end(u32 addr)
static inline u32 page_end(u32 addr)
{
return next_page(addr) - 1;
}
constexpr inline u32 is_page_aligned(u32 addr)
static inline u32 is_page_aligned(u32 val)
{
return page_start(addr) == addr;
return (val & (c_page_size - 1)) == 0;
}
@@ -186,7 +185,7 @@ namespace utils
bool is_page_range() const
{
return (valid() && start % 4096u == 0 && length() % 4096u == 0);
return (valid() && is_page_aligned(start) && is_page_aligned(length()));
}
address_range to_page_range() const
+2 -2
View File
@@ -371,12 +371,12 @@ public:
bool bit_test_reset(uint bit) = delete;
bool bit_test_invert(uint bit) = delete;
bool all_of(bs_t arg)
bool all_of(bs_t arg) const
{
return base::load().all_of(arg);
}
bool none_of(bs_t arg)
bool none_of(bs_t arg) const
{
return base::load().none_of(arg);
}
+6
View File
@@ -700,6 +700,12 @@ template<typename T>
struct size3_base
{
T width, height, depth;
template<typename NT>
explicit constexpr operator size3_base<NT>() const
{
return{ static_cast<NT>(width), static_cast<NT>(height), static_cast<NT>(depth) };
}
};
template<typename T>
+29 -3
View File
@@ -6,14 +6,27 @@ void shared_mutex::imp_lock_shared(u32 val)
{
ensure(val < c_err); // "shared_mutex underflow"
// Try to steal the notification bit
if (val & c_sig && m_value.compare_exchange(val, val - c_sig + 1))
{
return;
}
for (int i = 0; i < 10; i++)
{
busy_wait();
if (try_lock_shared())
{
return;
}
const u32 old = m_value;
if (old & c_sig && m_value.compare_and_swap_test(old, old - c_sig + 1))
{
return;
}
busy_wait();
}
// Acquire writer lock and downgrade
@@ -75,11 +88,24 @@ void shared_mutex::imp_lock(u32 val)
{
ensure(val < c_err); // "shared_mutex underflow"
// Try to steal the notification bit
if (val & c_sig && m_value.compare_exchange(val, val - c_sig + c_one))
{
return;
}
for (int i = 0; i < 10; i++)
{
busy_wait();
if (!m_value && try_lock())
const u32 old = m_value;
if (!old && try_lock())
{
return;
}
if (old & c_sig && m_value.compare_and_swap_test(old, old - c_sig + c_one))
{
return;
}
+5
View File
@@ -171,6 +171,11 @@ public:
{
return m_value.load() < c_one - 1;
}
bool has_waiters() const
{
return m_value.load() > c_one;
}
};
// Simplified shared (reader) lock implementation.
+144
View File
@@ -0,0 +1,144 @@
#include "stdafx.h"
#include "stack_trace.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#define DBGHELP_TRANSLATE_TCHAR
#include <DbgHelp.h>
#include <codecvt>
#else
#include <execinfo.h>
#endif
namespace utils
{
#ifdef _WIN32
std::string wstr_to_utf8(LPWSTR data, int str_len)
{
if (!str_len)
{
return {};
}
// Calculate size
const auto length = WideCharToMultiByte(CP_UTF8, 0, data, str_len, NULL, 0, NULL, NULL);
// Convert
std::vector<char> out(length + 1, 0);
WideCharToMultiByte(CP_UTF8, 0, data, str_len, out.data(), length, NULL, NULL);
return out.data();
}
std::vector<void*> get_backtrace(int max_depth)
{
std::vector<void*> result = {};
const auto hProcess = ::GetCurrentProcess();
const auto hThread = ::GetCurrentThread();
CONTEXT context{};
RtlCaptureContext(&context);
STACKFRAME64 stack = {};
stack.AddrPC.Offset = context.Rip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = context.Rsp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = context.Rbp;
stack.AddrFrame.Mode = AddrModeFlat;
while (max_depth--)
{
if (!StackWalk64(
IMAGE_FILE_MACHINE_AMD64,
hProcess,
hThread,
&stack,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL))
{
break;
}
result.push_back(reinterpret_cast<void*>(stack.AddrPC.Offset));
}
return result;
}
std::vector<std::string> get_backtrace_symbols(const std::vector<void*>& stack)
{
std::vector<std::string> result = {};
std::vector<u8> symbol_buf(sizeof(SYMBOL_INFO) + sizeof(TCHAR) * 256);
const auto hProcess = ::GetCurrentProcess();
auto sym = reinterpret_cast<SYMBOL_INFO*>(symbol_buf.data());
sym->SizeOfStruct = sizeof(SYMBOL_INFO);
sym->MaxNameLen = 256;
IMAGEHLP_LINEW64 line_info{};
line_info.SizeOfStruct = sizeof(IMAGEHLP_LINEW64);
SymInitialize(hProcess, NULL, TRUE);
SymSetOptions(SYMOPT_LOAD_LINES);
for (const auto& pointer : stack)
{
DWORD64 unused;
SymFromAddr(hProcess, reinterpret_cast<DWORD64>(pointer), &unused, sym);
if (sym->NameLen)
{
const auto function_name = wstr_to_utf8(sym->Name, static_cast<int>(sym->NameLen));
// Attempt to get file and line information if available
DWORD unused2;
if (SymGetLineFromAddrW(hProcess, reinterpret_cast<DWORD64>(pointer), &unused2, &line_info))
{
const auto full_path = fmt::format("%s:%u %s", wstr_to_utf8(line_info.FileName, -1), line_info.LineNumber, function_name);
result.push_back(full_path);
}
else
{
result.push_back(function_name);
}
}
else
{
result.push_back(fmt::format("rpcs3@0xp", pointer));
}
}
return result;
}
#else
std::vector<void*> get_backtrace(int max_depth)
{
std::vector<void*> result(max_depth);
int depth = backtrace(result.data(), max_depth);
result.resize(depth);
return result;
}
std::vector<std::string> get_backtrace_symbols(const std::vector<void*>& stack)
{
std::vector<std::string> result;
result.reserve(stack.size());
const auto symbols = backtrace_symbols(stack.data(), static_cast<int>(stack.size()));
for (usz i = 0; i < stack.size(); ++i)
{
result.push_back(symbols[i]);
}
free(symbols);
return result;
}
#endif
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <util/types.hpp>
#include <util/logs.hpp>
namespace utils
{
std::vector<void*> get_backtrace(int max_depth = 255);
std::vector<std::string> get_backtrace_symbols(const std::vector<void*>& stack);
FORCE_INLINE void print_trace(logs::channel& logger, int max_depth = 255)
{
const auto trace = get_backtrace(max_depth);
const auto lines = get_backtrace_symbols(trace);
for (const auto& line : lines)
{
logger.error("%s", line);
}
}
}
+2 -2
View File
@@ -99,13 +99,13 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
if (!timeout)
{
rec.cv.wait(lock, [&] { return !rec.mask; });
rec.cv.wait(lock, FN(!rec.mask));
}
else if (futex_op == FUTEX_WAIT)
{
const auto nsec = std::chrono::nanoseconds(timeout->tv_nsec + timeout->tv_sec * 1000000000ull);
if (!rec.cv.wait_for(lock, nsec, [&] { return !rec.mask; }))
if (!rec.cv.wait_for(lock, nsec, FN(!rec.mask)))
{
res = -1;
errno = ETIMEDOUT;
+64 -41
View File
@@ -3,6 +3,7 @@ YoRHa Theme for RPCS3
by Ani @ https://github.com/AniLeo
r1 (2018.02.27)
r2 (2021.08.28)
r3 (2022.08.20)
*/
/*
@@ -26,7 +27,7 @@ aea993
/* Every widget */
QWidget {
font-family: SCE-PS3 Rodin LATIN;
font-family: "SCE-PS3 Rodin LATIN";
font-size: 9.00pt;
color: #292929;
@@ -98,6 +99,9 @@ QToolTip {
}
/* Table headers */
QHeaderView {
color: #b3ac98;
}
QHeaderView::section {
text-transform: uppercase;
background: #4d4940;
@@ -109,22 +113,32 @@ QHeaderView::section {
border: none;
}
/* All other Tabs */
/* All Tabs */
QTabBar {
border-bottom: 0.05em solid #4d4940;
text-transform: uppercase;
}
QTabBar::tab {
background: transparent;
width: 4.80em;
padding-left: 0.50em;
padding-right: 0.50em;
padding-top: 0.25em;
padding-bottom: 0.25em;
padding-top: 0.65em;
padding-bottom: 0.65em;
margin-right: 0.25em;
font-size: 10.5pt;
font-weight: 550;
}
QTabBar::tab:last {
margin-right: 0em;
}
QTabBar::tab:!selected:hover {
background: transparent;
color: #4d4940;
}
QTabBar::tab::selected {
background: #4d4940;
color: #aea993;
border-bottom-style: solid;
}
/* Checkboxes */
@@ -153,29 +167,35 @@ QCheckBox::indicator::disabled, QRadioButton::indicator::disabled {
background-color: #828790; /* Gray */
}
/* Combo Boxes */
QComboBox {
/* Combo Boxes, Datetime dropdown */
QComboBox, QDateTimeEdit, QLineEdit {
background: transparent;
color: #4d4940;
border: 0.05em solid #1e1e1e;
border-radius: 0.15em;
padding-bottom: 0.2em;
padding-left: 0.4em;
padding-top: 0.1em;
padding-bottom: 0.1em;
}
QComboBox QAbstractItemView {
background: #b3ac98;
}
QComboBox::disabled {
QComboBox::disabled, QDateTimeEdit::disabled, QLineEdit::disabled {
background: #828790;
color: #4d4940;
}
/* Text boxes need less left padding than combo boxes */
QComboBox {
padding-left: 0.4em;
}
QLineEdit {
padding-left: 0.2em;
}
/* Group Boxes (Settings Dialog) */
QGroupBox {
margin-top: 1em;
border: 0.05em solid #4d4940;
text-transform: uppercase;
font-size: 9.00pt;
}
QGroupBox::title {
subcontrol-origin: margin;
@@ -236,6 +256,10 @@ QDockWidget::close-button, QDockWidget::float-button {
QTabWidget::pane {
border: 0em solid #4d4940;
}
/* Center tabs */
QTabWidget::tab-bar {
alignment: center;
}
/* Top menu bar */
QMenuBar {
@@ -284,6 +308,12 @@ QToolButton::hover {
background-color: #b3ac98;
}
/* Calendar widget */
QCalendarWidget QWidget{
background: #b3ac98;
color: #4d4940;
}
/*** RPCS3 Specifics ***/
@@ -303,40 +333,24 @@ QWidget#trophy_manager, QWidget#cg_disasm, QWidget#log_viewer, QMainWindow#main_
border: 0.05em solid #4d4940;
color: #292929;
font-family: none;
font-size: 9.50pt;
font-size: 9.5pt;
}
/* Pad Settings, Emulator Settings Dialogs: Tabs */
#pad_settings_dialog QTabWidget::tab-bar {
alignment: center;
/* Align the log widget tabs to the left */
QTabWidget#tab_widget_log::tab-bar {
alignment: left;
}
QTabBar#tab_bar_settings, #pad_settings_dialog QTabBar {
border-bottom: 0.05em solid #4d4940;
text-transform: uppercase;
/* Restore log section QTabBar to small sizes */
QTabBar#tab_bar_log {
border-bottom: 0;
}
QTabBar::tab#tab_bar_settings, #pad_settings_dialog QTabBar::tab {
background: transparent;
width: 5.20em;
padding-left: 0.50em;
padding-right: 0.50em;
padding-top: 0.65em;
padding-bottom: 0.65em;
margin-right: 0.25em;
font-size: 10.5pt;
font-weight: 550;
}
QTabBar::tab:last#tab_bar_settings, #pad_settings_dialog QTabBar::tab:last {
margin-right: 0em;
}
QTabBar::tab:!selected:hover#tab_bar_settings, #pad_settings_dialog QTabBar::tab:!selected:hover {
background: transparent;
color: #4d4940;
}
QTabBar::tab::selected#tab_bar_settings, #pad_settings_dialog QTabBar::tab::selected {
background: #4d4940;
color: #aea993;
border-bottom-style: solid;
margin-top: 0.15em;
QTabBar#tab_bar_log::tab {
width: 2.00em;
padding: 0.25em;
font-size: 9.0pt;
font-weight: 400;
margin-right: 0;
}
/* Pad Settings: Controller Image */
@@ -362,6 +376,7 @@ QLineEdit#mw_searchbar {
margin-left: 0.7em;
color: #4d4940;
font-size: 10.25pt;
border: 0em;
}
/* Set Theme UI colors */
@@ -440,3 +455,11 @@ QWidget#trophy_notification_frame {
QSlider#sizeSlider::groove:horizontal {
height: 1.5em;
}
/*
On Patch Creator, it's the patch validation label
since we can't avoid repainting color, let's bold it instead
*/
QLabel#validLabel {
font-weight: 600;
}
Binary file not shown.
+8 -2
View File
@@ -26,11 +26,17 @@ struct loaded_npdrm_keys
}
// TODO: Check if correct for ELF files usage
u128 last_key() const
u128 last_key(usz backwards = 0) const
{
backwards++;
const usz pos = dec_keys_pos;
return pos ? dec_keys[(pos - 1) % std::size(dec_keys)].load() : u128{};
return pos >= backwards ? dec_keys[(pos - backwards) % std::size(dec_keys)].load() : u128{};
}
SAVESTATE_INIT_POS(2);
loaded_npdrm_keys() = default;
loaded_npdrm_keys(utils::serial& ar);
void save(utils::serial& ar);
};
struct NPD_HEADER
+3 -1
View File
@@ -53,11 +53,13 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\Audio\Cubeb\CubebBackend.h" />
<ClInclude Include="Emu\Audio\Cubeb\cubeb_enumerator.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Emu\Audio\Cubeb\CubebBackend.cpp" />
<ClCompile Include="Emu\Audio\Cubeb\cubeb_enumerator.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
+6
View File
@@ -10,10 +10,16 @@
<ClCompile Include="Emu\Audio\Cubeb\CubebBackend.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\Audio\Cubeb\cubeb_enumerator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\Audio\Cubeb\CubebBackend.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Emu\Audio\Cubeb\cubeb_enumerator.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+30 -3
View File
@@ -6,10 +6,16 @@
AudioBackend::AudioBackend() {}
void AudioBackend::SetErrorCallback(std::function<void()> cb)
void AudioBackend::SetWriteCallback(std::function<u32(u32 /* byte_cnt */, void* /* buffer */)> cb)
{
std::lock_guard lock(m_error_cb_mutex);
m_error_callback = cb;
std::lock_guard lock(m_cb_mutex);
m_write_callback = cb;
}
void AudioBackend::SetStateCallback(std::function<void(AudioStateEvent)> cb)
{
std::lock_guard lock(m_state_cb_mutex);
m_state_callback = cb;
}
/*
@@ -134,3 +140,24 @@ AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index)
return count;
}
AudioChannelCnt AudioBackend::convert_channel_count(u64 raw)
{
switch (raw)
{
default:
case 8:
return AudioChannelCnt::SURROUND_7_1;
case 7:
case 6:
return AudioChannelCnt::SURROUND_5_1;
case 5:
case 4:
case 3:
case 2:
case 1:
return AudioChannelCnt::STEREO;
case 0:
fmt::throw_exception("Usupported channel count");
}
}
+27 -6
View File
@@ -37,6 +37,12 @@ enum class AudioChannelCnt : u32
SURROUND_7_1 = 8,
};
enum class AudioStateEvent : u32
{
UNSPECIFIED_ERROR,
DEFAULT_DEVICE_CHANGED,
};
class AudioBackend
{
public:
@@ -60,19 +66,21 @@ public:
virtual std::string_view GetName() const = 0;
// (Re)create output stream with new parameters. Blocks until data callback returns.
// If dev_id is empty, then default device will be selected.
// May override channel count if device has smaller number of channels.
// Should return 'true' on success.
virtual bool Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) = 0;
virtual bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) = 0;
// Reset backend state. Blocks until data callback returns.
virtual void Close() = 0;
// Sets write callback. It's called when backend requests new data to be sent.
// Callback should return number of submitted bytes. Calling other backend functions from callback is unsafe.
virtual void SetWriteCallback(std::function<u32(u32 /* byte_cnt */, void* /* buffer */)> cb) = 0;
virtual void SetWriteCallback(std::function<u32(u32 /* byte_cnt */, void* /* buffer */)> cb);
// Sets error callback. It's called when backend detects uncorrectable error condition in audio chain.
// Sets error callback. It's called when backend detects event in audio chain that needs immediate attention.
// Calling other backend functions from callback is unsafe.
virtual void SetErrorCallback(std::function<void()> cb);
virtual void SetStateCallback(std::function<void(AudioStateEvent)> cb);
/*
* All functions below require that Open() was called prior.
@@ -101,6 +109,11 @@ public:
*/
virtual bool Operational() { return true; }
/*
* This virtual method should be reimplemented if backend can report device changes
*/
virtual bool DefaultDeviceChanged() { return false; }
/*
* Helper methods
*/
@@ -145,6 +158,11 @@ public:
*/
static AudioChannelCnt get_max_channel_count(u32 device_index);
/*
* Converts raw channel count to value usable by backends
*/
static AudioChannelCnt convert_channel_count(u64 raw);
/*
* Downmix audio stream.
*/
@@ -208,8 +226,11 @@ protected:
AudioFreq m_sampling_rate = AudioFreq::FREQ_48K;
AudioChannelCnt m_channels = AudioChannelCnt::STEREO;
shared_mutex m_error_cb_mutex{};
std::function<void()> m_error_callback{};
shared_mutex m_cb_mutex{};
std::function<u32(u32, void *)> m_write_callback{};
shared_mutex m_state_cb_mutex{};
std::function<void(AudioStateEvent)> m_state_callback{};
bool m_playing = false;
+239 -37
View File
@@ -2,6 +2,7 @@
#include <algorithm>
#include "util/logs.hpp"
#include "Emu/Audio/audio_device_enumerator.h"
#ifdef _WIN32
#include <Windows.h>
@@ -15,8 +16,7 @@ CubebBackend::CubebBackend()
{
#ifdef _WIN32
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
if (HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); SUCCEEDED(hr))
{
m_com_init_success = true;
}
@@ -24,11 +24,20 @@ CubebBackend::CubebBackend()
if (int err = cubeb_init(&m_ctx, "RPCS3", nullptr))
{
Cubeb.error("cubeb_init() failed: 0x%08x", err);
Cubeb.error("cubeb_init() failed: %i", err);
m_ctx = nullptr;
return;
}
if (int err = cubeb_register_device_collection_changed(m_ctx, CUBEB_DEVICE_TYPE_OUTPUT, device_collection_changed_cb, this))
{
Cubeb.error("cubeb_register_device_collection_changed() failed: %i", err);
}
else
{
m_dev_collection_cb_enabled = true;
}
Cubeb.notice("Using backend %s", cubeb_get_backend_id(m_ctx));
}
@@ -36,6 +45,14 @@ CubebBackend::~CubebBackend()
{
Close();
if (m_dev_collection_cb_enabled)
{
if (int err = cubeb_register_device_collection_changed(m_ctx, CUBEB_DEVICE_TYPE_OUTPUT, nullptr, nullptr))
{
Cubeb.error("cubeb_register_device_collection_changed() failed: %i", err);
}
}
if (m_ctx)
{
cubeb_destroy(m_ctx);
@@ -56,11 +73,16 @@ bool CubebBackend::Initialized()
bool CubebBackend::Operational()
{
std::lock_guard lock(m_error_cb_mutex);
return m_stream != nullptr && !m_reset_req;
return m_stream != nullptr && !m_reset_req.observe();
}
bool CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
bool CubebBackend::DefaultDeviceChanged()
{
std::lock_guard lock{m_dev_sw_mutex};
return !m_reset_req.observe() && m_default_dev_changed;
}
bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
{
if (!Initialized())
{
@@ -69,11 +91,39 @@ bool CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChanne
}
std::lock_guard lock(m_cb_mutex);
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
CloseUnlocked();
const bool use_default_device = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
auto [dev_handle, dev_ident, dev_ch_cnt] = GetDevice(use_default_device ? "" : dev_id);
if (!dev_handle)
{
if (use_default_device)
{
std::tie(dev_handle, dev_ident, dev_ch_cnt) = GetDefaultDeviceAlt(freq, sample_size, ch_cnt);
if (!dev_handle)
{
Cubeb.error("Cannot detect default device. Channel count detection unavailable.");
}
}
else
{
Cubeb.error("Device with id=%s not found", dev_id);
return false;
}
}
if (dev_ch_cnt == 0)
{
Cubeb.error("Device reported invalid channel count, using stereo instead");
dev_ch_cnt = 2;
}
m_sampling_rate = freq;
m_sample_size = sample_size;
m_channels = ch_cnt;
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(dev_ch_cnt)), static_cast<u32>(ch_cnt)));
full_sample_size = get_channels() * get_sample_size();
cubeb_stream_params stream_param{};
@@ -82,46 +132,50 @@ bool CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChanne
stream_param.channels = get_channels();
stream_param.layout = [&]()
{
switch (ch_cnt)
switch (m_channels)
{
case AudioChannelCnt::STEREO: return CUBEB_LAYOUT_STEREO;
case AudioChannelCnt::SURROUND_5_1: return CUBEB_LAYOUT_3F2_LFE;
case AudioChannelCnt::SURROUND_7_1: return CUBEB_LAYOUT_3F4_LFE;
default:
ensure(false);
return CUBEB_LAYOUT_UNDEFINED;
fmt::throw_exception("Invalid audio channel count");
}
}();
stream_param.prefs = m_dev_collection_cb_enabled && dev_handle ? CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING : CUBEB_STREAM_PREF_NONE;
u32 min_latency{};
if (int err = cubeb_get_min_latency(m_ctx, &stream_param, &min_latency))
{
Cubeb.error("cubeb_get_min_latency() failed: 0x%08x", err);
Cubeb.error("cubeb_get_min_latency() failed: %i", err);
min_latency = 0;
}
const u32 stream_latency = std::max(static_cast<u32>(AUDIO_MIN_LATENCY * get_sampling_rate()), min_latency);
if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, nullptr, &stream_param, stream_latency, data_cb, state_cb, this))
if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, dev_handle, &stream_param, stream_latency, data_cb, state_cb, this))
{
Cubeb.error("cubeb_stream_init() failed: 0x%08x", err);
Cubeb.error("cubeb_stream_init() failed: %i", err);
m_stream = nullptr;
}
else if (int err = cubeb_stream_start(m_stream))
{
Cubeb.error("cubeb_stream_start() failed: 0x%08x", err);
CloseUnlocked();
}
else if (int err = cubeb_stream_set_volume(m_stream, 1.0))
{
Cubeb.error("cubeb_stream_set_volume() failed: 0x%08x", err);
return false;
}
if (m_stream == nullptr)
if (int err = cubeb_stream_start(m_stream))
{
Cubeb.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
Cubeb.error("cubeb_stream_start() failed: %i", err);
CloseUnlocked();
return false;
}
if (int err = cubeb_stream_set_volume(m_stream, 1.0))
{
Cubeb.error("cubeb_stream_set_volume() failed: %i", err);
}
if (use_default_device)
{
m_current_device = dev_ident;
}
return true;
}
@@ -131,7 +185,7 @@ void CubebBackend::CloseUnlocked()
{
if (int err = cubeb_stream_stop(m_stream))
{
Cubeb.error("cubeb_stream_stop() failed: 0x%08x", err);
Cubeb.error("cubeb_stream_stop() failed: %i", err);
}
cubeb_stream_destroy(m_stream);
@@ -140,11 +194,15 @@ void CubebBackend::CloseUnlocked()
m_playing = false;
m_last_sample.fill(0);
m_default_dev_changed = false;
m_current_device.clear();
}
void CubebBackend::Close()
{
std::lock_guard lock(m_cb_mutex);
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
CloseUnlocked();
}
@@ -177,12 +235,6 @@ void CubebBackend::Pause()
m_last_sample.fill(0);
}
void CubebBackend::SetWriteCallback(std::function<u32(u32, void *)> cb)
{
std::lock_guard lock(m_cb_mutex);
m_write_callback = cb;
}
f64 CubebBackend::GetCallbackFrameLen()
{
if (m_stream == nullptr)
@@ -194,19 +246,130 @@ f64 CubebBackend::GetCallbackFrameLen()
u32 stream_latency{};
if (int err = cubeb_stream_get_latency(m_stream, &stream_latency))
{
Cubeb.error("cubeb_stream_get_latency() failed: 0x%08x", err);
Cubeb.error("cubeb_stream_get_latency() failed: %i", err);
stream_latency = 0;
}
return std::max<f64>(AUDIO_MIN_LATENCY, static_cast<f64>(stream_latency) / get_sampling_rate());
}
std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDevice(std::string_view dev_id)
{
const bool default_dev = dev_id.empty();
cubeb_device_collection dev_collection{};
if (int err = cubeb_enumerate_devices(m_ctx, CUBEB_DEVICE_TYPE_OUTPUT, &dev_collection))
{
Cubeb.error("cubeb_enumerate_devices() failed: %i", err);
return {};
}
if (dev_collection.count == 0)
{
Cubeb.error("No output devices available");
if (int err = cubeb_device_collection_destroy(m_ctx, &dev_collection))
{
Cubeb.error("cubeb_device_collection_destroy() failed: %i", err);
}
return {};
}
std::tuple<cubeb_devid, std::string, u32> result{};
for (u64 dev_idx = 0; dev_idx < dev_collection.count; dev_idx++)
{
const cubeb_device_info& dev_info = dev_collection.device[dev_idx];
const std::string dev_ident{dev_info.device_id};
if (dev_ident.empty())
{
Cubeb.error("device_id is missing from device");
continue;
}
if (default_dev)
{
if (dev_info.preferred & CUBEB_DEVICE_PREF_MULTIMEDIA)
{
result = {dev_info.devid, dev_ident, dev_info.max_channels};
break;
}
}
else if (dev_ident == dev_id)
{
result = {dev_info.devid, dev_ident, dev_info.max_channels};
break;
}
}
if (int err = cubeb_device_collection_destroy(m_ctx, &dev_collection))
{
Cubeb.error("cubeb_device_collection_destroy() failed: %i", err);
}
return result;
};
std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
{
cubeb_stream_params param =
{
.format = sample_size == AudioSampleSize::S16 ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE,
.rate = static_cast<u32>(freq),
.channels = static_cast<u32>(ch_cnt),
.layout = CUBEB_LAYOUT_UNDEFINED,
.prefs = CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING
};
u32 min_latency{};
if (int err = cubeb_get_min_latency(m_ctx, &param, &min_latency))
{
Cubeb.error("cubeb_get_min_latency() failed: %i", err);
min_latency = 100;
}
cubeb_stream* tmp_stream{};
static auto dummy_data_cb = [](cubeb_stream*, void*, void const*, void*, long) -> long { return 0; };
static auto dummy_state_cb = [](cubeb_stream*, void*, cubeb_state) {};
if (int err = cubeb_stream_init(m_ctx, &tmp_stream, "Default device detector", nullptr, nullptr, nullptr, &param, min_latency, dummy_data_cb, dummy_state_cb, nullptr))
{
Cubeb.error("cubeb_stream_init() failed: %i", err);
return {};
}
cubeb_device* crnt_dev{};
if (int err = cubeb_stream_get_current_device(tmp_stream, &crnt_dev))
{
Cubeb.error("cubeb_stream_get_current_device() failed: %i", err);
cubeb_stream_destroy(tmp_stream);
return {};
}
const std::string out_dev_name{crnt_dev->output_name};
if (int err = cubeb_stream_device_destroy(tmp_stream, crnt_dev))
{
Cubeb.error("cubeb_stream_device_destroy() failed: %i", err);
}
cubeb_stream_destroy(tmp_stream);
if (out_dev_name.empty())
{
return {};
}
return GetDevice(out_dev_name);
}
long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes)
{
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
std::unique_lock lock(cubeb->m_cb_mutex, std::defer_lock);
if (nframes && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing)
if (nframes && !cubeb->m_reset_req.observe() && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing)
{
const u32 sample_size = cubeb->full_sample_size.observe();
const u32 bytes_req = nframes * sample_size;
@@ -241,12 +404,51 @@ void CubebBackend::state_cb(cubeb_stream* /* stream */, void* user_ptr, cubeb_st
{
Cubeb.error("Stream entered error state");
std::lock_guard lock(cubeb->m_error_cb_mutex);
cubeb->m_reset_req = true;
std::lock_guard lock(cubeb->m_state_cb_mutex);
if (cubeb->m_error_callback)
if (!cubeb->m_reset_req.test_and_set() && cubeb->m_state_callback)
{
cubeb->m_error_callback();
cubeb->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
}
}
}
void CubebBackend::device_collection_changed_cb(cubeb* /* context */, void* user_ptr)
{
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
Cubeb.notice("Device collection changed");
std::lock_guard lock{cubeb->m_dev_sw_mutex};
// Non default device is used (or default device cannot be detected)
if (cubeb->m_current_device.empty())
{
return;
}
auto [handle, dev_id, ch_cnt] = cubeb->GetDevice();
if (!handle)
{
std::tie(handle, dev_id, ch_cnt) = cubeb->GetDefaultDeviceAlt(cubeb->m_sampling_rate, cubeb->m_sample_size, cubeb->m_channels);
}
std::lock_guard cb_lock{cubeb->m_state_cb_mutex};
if (!handle)
{
// No devices available
if (!cubeb->m_reset_req.test_and_set() && cubeb->m_state_callback)
{
cubeb->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
}
}
else if (!cubeb->m_reset_req.observe() && dev_id != cubeb->m_current_device)
{
cubeb->m_default_dev_changed = true;
if (cubeb->m_state_callback)
{
cubeb->m_state_callback(AudioStateEvent::DEFAULT_DEVICE_CHANGED);
}
}
}
+13 -5
View File
@@ -20,11 +20,11 @@ public:
bool Initialized() override;
bool Operational() override;
bool DefaultDeviceChanged() override;
bool Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
void Close() override;
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
f64 GetCallbackFrameLen() override;
void Play() override;
@@ -39,16 +39,24 @@ private:
bool m_com_init_success = false;
#endif
shared_mutex m_cb_mutex{};
std::function<u32(u32, void *)> m_write_callback{};
std::array<u8, sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)> m_last_sample{};
atomic_t<u8> full_sample_size = 0;
bool m_reset_req = false;
atomic_t<bool> m_reset_req = false;
shared_mutex m_dev_sw_mutex{};
std::string m_current_device{};
bool m_default_dev_changed = false;
bool m_dev_collection_cb_enabled = false;
// Cubeb callbacks
static long data_cb(cubeb_stream* stream, void* user_ptr, void const* input_buffer, void* output_buffer, long nframes);
static void state_cb(cubeb_stream* stream, void* user_ptr, cubeb_state state);
static void device_collection_changed_cb(cubeb* context, void* user_ptr);
void CloseUnlocked();
std::tuple<cubeb_devid, std::string /* dev_ident */, u32 /* ch_cnt */> GetDevice(std::string_view dev_id = "");
std::tuple<cubeb_devid, std::string /* dev_ident */, u32 /* ch_cnt */> GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt);
};
+113
View File
@@ -0,0 +1,113 @@
#include "Emu/Audio/Cubeb/cubeb_enumerator.h"
#include "util/logs.hpp"
#include <algorithm>
#ifdef _WIN32
#include <Windows.h>
#include <system_error>
#endif
LOG_CHANNEL(cubeb_dev_enum);
cubeb_enumerator::cubeb_enumerator() : audio_device_enumerator()
{
#ifdef _WIN32
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
{
com_init_success = true;
}
#endif
if (int err = cubeb_init(&ctx, "RPCS3 device enumeration", nullptr))
{
cubeb_dev_enum.error("cubeb_init() failed: %i", err);
ctx = nullptr;
}
}
cubeb_enumerator::~cubeb_enumerator()
{
if (ctx)
{
cubeb_destroy(ctx);
}
#ifdef _WIN32
if (com_init_success)
{
CoUninitialize();
}
#endif
}
std::vector<audio_device_enumerator::audio_device> cubeb_enumerator::get_output_devices()
{
if (ctx == nullptr)
{
return {};
}
cubeb_device_collection dev_collection{};
if (int err = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &dev_collection))
{
cubeb_dev_enum.error("cubeb_enumerate_devices() failed: %i", err);
return {};
}
if (dev_collection.count == 0)
{
cubeb_dev_enum.error("No output devices available");
if (int err = cubeb_device_collection_destroy(ctx, &dev_collection))
{
cubeb_dev_enum.error("cubeb_device_collection_destroy() failed: %i", err);
}
return {};
}
std::vector<audio_device> device_list{};
for (u64 dev_idx = 0; dev_idx < dev_collection.count; dev_idx++)
{
const cubeb_device_info& dev_info = dev_collection.device[dev_idx];
if (dev_info.state == CUBEB_DEVICE_STATE_UNPLUGGED)
{
continue;
}
audio_device dev =
{
.id = std::string{dev_info.device_id},
.name = std::string{dev_info.friendly_name},
.max_ch = dev_info.max_channels
};
if (dev.id.empty())
{
cubeb_dev_enum.warning("Empty device id - skipping");
continue;
}
if (dev.name.empty())
{
dev.name = dev.id;
}
cubeb_dev_enum.notice("Found device: id=%s, name=%s, max_ch=%d", dev.id, dev.name, dev.max_ch);
device_list.emplace_back(dev);
}
if (int err = cubeb_device_collection_destroy(ctx, &dev_collection))
{
cubeb_dev_enum.error("cubeb_device_collection_destroy() failed: %i", err);
}
std::sort(device_list.begin(), device_list.end(), [](audio_device_enumerator::audio_device a, audio_device_enumerator::audio_device b)
{
return a.name < b.name;
});
return device_list;
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "Emu/Audio/audio_device_enumerator.h"
#include "cubeb/cubeb.h"
class cubeb_enumerator final : public audio_device_enumerator
{
public:
cubeb_enumerator();
~cubeb_enumerator() override;
std::vector<audio_device> get_output_devices() override;
private:
cubeb* ctx{};
#ifdef _WIN32
bool com_init_success = false;
#endif
};
+45 -30
View File
@@ -6,6 +6,8 @@
#include "FAudioBackend.h"
#include "Emu/system_config.h"
#include "Emu/System.h"
#include "Emu/Audio/audio_device_enumerator.h"
#include "Utilities/StrUtil.h"
LOG_CHANNEL(FAudio_, "FAudio");
@@ -117,11 +119,10 @@ bool FAudioBackend::Initialized()
bool FAudioBackend::Operational()
{
std::lock_guard lock(m_error_cb_mutex);
return m_source_voice != nullptr && !m_reset_req;
return m_source_voice != nullptr && !m_reset_req.observe();
}
bool FAudioBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
{
if (!Initialized())
{
@@ -132,9 +133,37 @@ bool FAudioBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChann
std::lock_guard lock(m_cb_mutex);
CloseUnlocked();
const bool use_default_dev = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
u64 devid{};
if (!use_default_dev)
{
if (!try_to_uint64(&devid, dev_id, 0, UINT32_MAX))
{
FAudio_.error("Invalid device id - %s", dev_id);
return false;
}
}
if (u32 res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, static_cast<u32>(devid), nullptr))
{
FAudio_.error("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
m_master_voice = nullptr;
return false;
}
FAudioVoiceDetails vd{};
FAudioVoice_GetVoiceDetails(m_master_voice, &vd);
if (vd.InputChannels == 0)
{
FAudio_.error("Channel count of 0 is invalid");
CloseUnlocked();
return false;
}
m_sampling_rate = freq;
m_sample_size = sample_size;
m_channels = ch_cnt;
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(vd.InputChannels)), static_cast<u32>(ch_cnt)));;
FAudioWaveFormatEx waveformatex;
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
@@ -153,43 +182,30 @@ bool FAudioBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChann
OnLoopEnd = nullptr;
OnVoiceError = nullptr;
if (u32 res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, 0, nullptr))
{
FAudio_.error("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
m_master_voice = nullptr;
}
else if (u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))
if (u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))
{
FAudio_.error("FAudio_CreateSourceVoice() failed(0x%08x)", res);
CloseUnlocked();
return false;
}
else if (u32 res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW))
if (u32 res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW))
{
FAudio_.error("FAudioSourceVoice_Start() failed(0x%08x)", res);
CloseUnlocked();
return false;
}
else if (u32 res = FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW))
if (u32 res = FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW))
{
FAudio_.error("FAudioVoice_SetVolume() failed(0x%08x)", res);
}
if (m_source_voice == nullptr)
{
FAudio_.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
return false;
}
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS * static_cast<u32>(FAUDIO_DEFAULT_FREQ_RATIO) / 1000);
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS / 1000);
return true;
}
void FAudioBackend::SetWriteCallback(std::function<u32(u32, void *)> cb)
{
std::lock_guard lock(m_cb_mutex);
m_write_callback = cb;
}
f64 FAudioBackend::GetCallbackFrameLen()
{
constexpr f64 _10ms = 0.01;
@@ -218,7 +234,7 @@ void FAudioBackend::OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj,
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
std::unique_lock lock(faudio->m_cb_mutex, std::defer_lock);
if (BytesRequired && lock.try_lock() && faudio->m_write_callback && faudio->m_playing)
if (BytesRequired && !faudio->m_reset_req.observe() && lock.try_lock() && faudio->m_write_callback && faudio->m_playing)
{
ensure(BytesRequired <= faudio->m_data_buf.size(), "FAudio internal buffer is too small. Report to developers!");
@@ -250,11 +266,10 @@ void FAudioBackend::OnCriticalError_func(FAudioEngineCallback *cb_obj, u32 Error
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
std::lock_guard lock(faudio->m_error_cb_mutex);
faudio->m_reset_req = true;
std::lock_guard lock(faudio->m_state_cb_mutex);
if (faudio->m_error_callback)
if (!faudio->m_reset_req.test_and_set() && faudio->m_state_callback)
{
faudio->m_error_callback();
faudio->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
}
}
+2 -5
View File
@@ -24,10 +24,9 @@ public:
bool Initialized() override;
bool Operational() override;
bool Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
void Close() override;
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
f64 GetCallbackFrameLen() override;
void Play() override;
@@ -40,12 +39,10 @@ private:
FAudioMasteringVoice* m_master_voice{};
FAudioSourceVoice* m_source_voice{};
shared_mutex m_cb_mutex{};
std::function<u32(u32, void *)> m_write_callback{};
std::vector<u8> m_data_buf{};
std::array<u8, sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)> m_last_sample{};
bool m_reset_req = false;
atomic_t<bool> m_reset_req = false;
// FAudio voice callbacks
static void OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj, u32 BytesRequired);
@@ -0,0 +1,90 @@
#ifndef HAVE_FAUDIO
#error "FAudio support disabled but still being built."
#endif
#include "Emu/Audio/FAudio/faudio_enumerator.h"
#include <array>
#include <algorithm>
#include "Utilities/StrUtil.h"
#include "util/logs.hpp"
LOG_CHANNEL(faudio_dev_enum);
faudio_enumerator::faudio_enumerator() : audio_device_enumerator()
{
FAudio *tmp{};
if (u32 res = FAudioCreate(&tmp, 0, FAUDIO_DEFAULT_PROCESSOR))
{
faudio_dev_enum.error("FAudioCreate() failed(0x%08x)", res);
return;
}
// All succeeded, "commit"
instance = tmp;
}
faudio_enumerator::~faudio_enumerator()
{
if (instance != nullptr)
{
FAudio_StopEngine(instance);
FAudio_Release(instance);
}
}
std::vector<audio_device_enumerator::audio_device> faudio_enumerator::get_output_devices()
{
if (!instance)
{
return {};
}
u32 dev_cnt{};
if (u32 res = FAudio_GetDeviceCount(instance, &dev_cnt))
{
faudio_dev_enum.error("FAudio_GetDeviceCount() failed(0x%08x)", res);
return {};
}
if (dev_cnt == 0)
{
faudio_dev_enum.warning("No devices available");
return {};
}
std::vector<audio_device> device_list{};
for (u32 dev_idx = 0; dev_idx < dev_cnt; dev_idx++)
{
FAudioDeviceDetails dev_info{};
if (u32 res = FAudio_GetDeviceDetails(instance, dev_idx, &dev_info))
{
faudio_dev_enum.error("FAudio_GetDeviceDetails() failed(0x%08x)", res);
continue;
}
audio_device dev =
{
.id = std::to_string(dev_idx),
.name = utf16_to_utf8(std::bit_cast<char16_t*>(&dev_info.DisplayName[0])),
.max_ch = dev_info.OutputFormat.Format.nChannels
};
if (dev.name.empty())
{
dev.name = "Device " + dev.id;
}
faudio_dev_enum.notice("Found device: id=%s, name=%s, max_ch=%d", dev.id, dev.name, dev.max_ch);
device_list.emplace_back(dev);
}
std::sort(device_list.begin(), device_list.end(), [](audio_device_enumerator::audio_device a, audio_device_enumerator::audio_device b)
{
return a.name < b.name;
});
return device_list;
}
@@ -0,0 +1,22 @@
#pragma once
#ifndef HAVE_FAUDIO
#error "FAudio support disabled but still being built."
#endif
#include "Emu/Audio/audio_device_enumerator.h"
#include "FAudio.h"
class faudio_enumerator final : public audio_device_enumerator
{
public:
faudio_enumerator();
~faudio_enumerator() override;
std::vector<audio_device> get_output_devices() override;
private:
FAudio* instance{};
};
+1 -4
View File
@@ -10,18 +10,15 @@ public:
std::string_view GetName() const override { return "Null"sv; }
bool Open(AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */) override
bool Open(std::string_view /* dev_id */, AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */) override
{
Close();
return true;
}
void Close() override { m_playing = false; }
void SetWriteCallback(std::function<u32(u32, void *)> /* cb */) override {};
f64 GetCallbackFrameLen() override { return 0.01; };
void SetErrorCallback(std::function<void()> /* cb */) override {};
void Play() override { m_playing = true; }
void Pause() override { m_playing = false; }
bool IsPlaying() override { return m_playing; }
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "Emu/Audio/audio_device_enumerator.h"
class null_enumerator final : public audio_device_enumerator
{
public:
null_enumerator() {};
~null_enumerator() override {};
std::vector<audio_device> get_output_devices() override { return {}; }
};
+179 -46
View File
@@ -5,6 +5,8 @@
#include <algorithm>
#include "util/logs.hpp"
#include "Emu/System.h"
#include "Emu/Audio/audio_device_enumerator.h"
#include "Utilities/StrUtil.h"
#include "XAudio2Backend.h"
#include <Windows.h>
@@ -14,42 +16,91 @@
LOG_CHANNEL(XAudio);
template <>
void fmt_class_string<ERole>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto value)
{
switch (value)
{
case eConsole: return "eConsole";
case eMultimedia: return "eMultimedia";
case eCommunications: return "eCommunications";
}
return unknown;
});
}
template <>
void fmt_class_string<EDataFlow>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto value)
{
switch (value)
{
case eRender: return "eRender";
case eCapture: return "eCapture";
case eAll: return "eAll";
}
return unknown;
});
}
XAudio2Backend::XAudio2Backend()
: AudioBackend()
{
Microsoft::WRL::ComPtr<IXAudio2> instance;
Microsoft::WRL::ComPtr<IXAudio2> instance{};
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> enumerator{};
// In order to prevent errors on CreateMasteringVoice, apparently we need CoInitializeEx according to:
// https://docs.microsoft.com/en-us/windows/win32/api/xaudio2fx/nf-xaudio2fx-xaudio2createvolumemeter
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
if (HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); SUCCEEDED(hr))
{
m_com_init_success = true;
}
hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_USE_DEFAULT_PROCESSOR);
if (FAILED(hr))
if (HRESULT hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_USE_DEFAULT_PROCESSOR); FAILED(hr))
{
XAudio.error("XAudio2Create() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
return;
}
hr = instance->RegisterForCallbacks(this);
if (FAILED(hr))
if (HRESULT hr = instance->RegisterForCallbacks(this); FAILED(hr))
{
// Some error recovery functionality will be lost, but otherwise backend is operational
XAudio.error("RegisterForCallbacks() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
}
// Try to register a listener for device changes
if (HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(enumerator.GetAddressOf())); FAILED(hr))
{
XAudio.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
return;
}
if (HRESULT hr = enumerator->RegisterEndpointNotificationCallback(this); FAILED(hr))
{
XAudio.error("RegisterEndpointNotificationCallback() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
return;
}
// All succeeded, "commit"
m_xaudio2_instance = std::move(instance);
m_device_enumerator = std::move(enumerator);
}
XAudio2Backend::~XAudio2Backend()
{
Close();
if (m_device_enumerator != nullptr)
{
m_device_enumerator->UnregisterEndpointNotificationCallback(this);
m_device_enumerator = nullptr;
}
if (m_xaudio2_instance != nullptr)
{
m_xaudio2_instance->StopEngine();
@@ -69,14 +120,13 @@ bool XAudio2Backend::Initialized()
bool XAudio2Backend::Operational()
{
std::lock_guard lock(m_error_cb_mutex);
return m_source_voice != nullptr && !m_reset_req.observe();
}
if (m_dev_listener.output_device_changed())
{
m_reset_req = true;
}
return m_source_voice != nullptr && !m_reset_req;
bool XAudio2Backend::DefaultDeviceChanged()
{
std::lock_guard lock{m_dev_sw_mutex};
return !m_reset_req.observe() && m_default_dev_changed;
}
void XAudio2Backend::Play()
@@ -114,11 +164,15 @@ void XAudio2Backend::CloseUnlocked()
m_playing = false;
m_last_sample.fill(0);
m_default_dev_changed = false;
m_current_device.clear();
}
void XAudio2Backend::Close()
{
std::lock_guard lock(m_cb_mutex);
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
CloseUnlocked();
}
@@ -144,7 +198,7 @@ void XAudio2Backend::Pause()
}
}
bool XAudio2Backend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
{
if (!Initialized())
{
@@ -153,11 +207,57 @@ bool XAudio2Backend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChan
}
std::lock_guard lock(m_cb_mutex);
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
CloseUnlocked();
const bool use_default_device = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
std::string selected_dev_id{};
if (use_default_device)
{
Microsoft::WRL::ComPtr<IMMDevice> default_dev{};
if (HRESULT hr = m_device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, default_dev.GetAddressOf()); FAILED(hr))
{
XAudio.error("GetDefaultAudioEndpoint() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
return false;
}
LPWSTR default_id{};
if (HRESULT hr = default_dev->GetId(&default_id); FAILED(hr))
{
XAudio.error("GetId() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
return false;
}
selected_dev_id = wchar_to_utf8(std::wstring_view{default_id});
CoTaskMemFree(default_id);
if (selected_dev_id.empty())
{
XAudio.error("Default device id is empty");
return false;
}
}
if (HRESULT hr = m_xaudio2_instance->CreateMasteringVoice(&m_master_voice, 0, 0, 0, utf8_to_wchar(use_default_device ? selected_dev_id : dev_id).c_str()); FAILED(hr))
{
XAudio.error("CreateMasteringVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
m_master_voice = nullptr;
return false;
}
XAUDIO2_VOICE_DETAILS vd{};
m_master_voice->GetVoiceDetails(&vd);
if (vd.InputChannels == 0)
{
XAudio.error("Channel count 0 is invalid");
CloseUnlocked();
return false;
}
m_sampling_rate = freq;
m_sample_size = sample_size;
m_channels = ch_cnt;
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(vd.InputChannels)), static_cast<u32>(ch_cnt)));
WAVEFORMATEX waveformatex{};
waveformatex.wFormatTag = get_convert_to_s16() ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
@@ -168,65 +268,56 @@ bool XAudio2Backend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChan
waveformatex.wBitsPerSample = get_sample_size() * 8;
waveformatex.cbSize = 0;
if (HRESULT hr = m_xaudio2_instance->CreateMasteringVoice(&m_master_voice); FAILED(hr))
{
XAudio.error("CreateMasteringVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
m_master_voice = nullptr;
}
else if (HRESULT hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO, this); FAILED(hr))
if (HRESULT hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO, this); FAILED(hr))
{
XAudio.error("CreateSourceVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
CloseUnlocked();
return false;
}
else if (HRESULT hr = m_source_voice->Start(); FAILED(hr))
if (HRESULT hr = m_source_voice->Start(); FAILED(hr))
{
XAudio.error("Start() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
CloseUnlocked();
return false;
}
else if (HRESULT hr = m_source_voice->SetVolume(1.0f); FAILED(hr))
if (HRESULT hr = m_source_voice->SetVolume(1.0f); FAILED(hr))
{
XAudio.error("SetVolume() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
}
if (m_source_voice == nullptr)
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS / 1000);
if (use_default_device)
{
XAudio.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
return false;
m_current_device = selected_dev_id;
}
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS * static_cast<u32>(XAUDIO2_DEFAULT_FREQ_RATIO) / 1000);
return true;
}
void XAudio2Backend::SetWriteCallback(std::function<u32(u32, void *)> cb)
{
std::lock_guard lock(m_cb_mutex);
m_write_callback = cb;
}
f64 XAudio2Backend::GetCallbackFrameLen()
{
constexpr f64 _10ms = 0.01;
if (m_source_voice == nullptr)
if (m_xaudio2_instance == nullptr)
{
XAudio.error("GetCallbackFrameLen() called uninitialized");
return _10ms;
}
void *ext;
Microsoft::WRL::ComPtr<IXAudio2Extension> xaudio_ext{};
f64 min_latency{};
const HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, &ext);
if (FAILED(hr))
if (HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, std::bit_cast<void**>(xaudio_ext.GetAddressOf())); FAILED(hr))
{
XAudio.error("QueryInterface() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
}
else
{
u32 samples_per_q = 0, freq = 0;
static_cast<IXAudio2Extension *>(ext)->GetProcessingQuantum(&samples_per_q, &freq);
xaudio_ext->GetProcessingQuantum(&samples_per_q, &freq);
if (freq)
{
@@ -240,7 +331,7 @@ f64 XAudio2Backend::GetCallbackFrameLen()
void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired)
{
std::unique_lock lock(m_cb_mutex, std::defer_lock);
if (BytesRequired && lock.try_lock() && m_write_callback && m_playing)
if (BytesRequired && !m_reset_req.observe() && lock.try_lock() && m_write_callback && m_playing)
{
ensure(BytesRequired <= m_data_buf.size(), "XAudio internal buffer is too small. Report to developers!");
@@ -270,11 +361,53 @@ void XAudio2Backend::OnCriticalError(HRESULT Error)
{
XAudio.error("OnCriticalError() called: %s (0x%08x)", std::system_category().message(Error), static_cast<u32>(Error));
std::lock_guard lock(m_error_cb_mutex);
m_reset_req = true;
std::lock_guard lock(m_state_cb_mutex);
if (m_error_callback)
if (!m_reset_req.test_and_set() && m_state_callback)
{
m_error_callback();
m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
}
}
HRESULT XAudio2Backend::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id)
{
XAudio.notice("OnDefaultDeviceChanged(flow=%s, role=%s, new_default_device_id=0x%x)", flow, role, new_default_device_id);
if (!new_default_device_id)
{
XAudio.notice("OnDefaultDeviceChanged(): new_default_device_id empty");
return S_OK;
}
// Listen only for one device role, otherwise we're going to receive more than one notification for flow type
if (role != eConsole)
{
XAudio.notice("OnDefaultDeviceChanged(): we don't care about this device");
return S_OK;
}
std::lock_guard lock{m_dev_sw_mutex};
// Non default device is used
if (m_current_device.empty())
{
return S_OK;
}
const std::string new_device_id = wchar_to_utf8(std::wstring_view{new_default_device_id});
if (flow == eRender || flow == eAll)
{
if (!m_reset_req.observe() && new_device_id != m_current_device)
{
m_default_dev_changed = true;
if (m_state_callback)
{
m_state_callback(AudioStateEvent::DEFAULT_DEVICE_CHANGED);
}
}
}
return S_OK;
}
+21 -9
View File
@@ -7,12 +7,12 @@
#include <memory>
#include "Utilities/mutex.h"
#include "Emu/Audio/AudioBackend.h"
#include "Emu/Audio/audio_device_listener.h"
#include <xaudio2redist.h>
#include <wrl/client.h>
#include <MMDeviceAPI.h>
class XAudio2Backend final : public AudioBackend, public IXAudio2VoiceCallback, public IXAudio2EngineCallback
class XAudio2Backend final : public AudioBackend, public IXAudio2VoiceCallback, public IXAudio2EngineCallback, public IMMNotificationClient
{
public:
XAudio2Backend();
@@ -25,11 +25,11 @@ public:
bool Initialized() override;
bool Operational() override;
bool DefaultDeviceChanged() override;
bool Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
void Close() override;
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
f64 GetCallbackFrameLen() override;
void Play() override;
@@ -43,14 +43,16 @@ private:
IXAudio2SourceVoice* m_source_voice{};
bool m_com_init_success = false;
shared_mutex m_cb_mutex{};
std::function<u32(u32, void *)> m_write_callback{};
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> m_device_enumerator{};
shared_mutex m_dev_sw_mutex{};
std::string m_current_device{};
bool m_default_dev_changed = false;
std::vector<u8> m_data_buf{};
std::array<u8, sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)> m_last_sample{};
bool m_reset_req = false;
audio_device_listener m_dev_listener{};
atomic_t<bool> m_reset_req = false;
// XAudio voice callbacks
void OnVoiceProcessingPassStart(UINT32 BytesRequired) override;
@@ -66,5 +68,15 @@ private:
void OnProcessingPassEnd() override {};
void OnCriticalError(HRESULT Error) override;
// IMMNotificationClient callbacks
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };
IFACEMETHODIMP_(ULONG) Release() override { return 1; };
IFACEMETHODIMP QueryInterface(REFIID /*iid*/, void** /*object*/) override { return E_NOINTERFACE; };
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR /*device_id*/, const PROPERTYKEY /*key*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceAdded(LPCWSTR /*device_id*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR /*device_id*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR /*device_id*/, DWORD /*new_state*/) override { return S_OK; };
IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override;
void CloseUnlocked();
};
@@ -0,0 +1,153 @@
#ifndef _WIN32
#error "XAudio2 can only be built on Windows."
#endif
#include "Emu/Audio/XAudio2/xaudio2_enumerator.h"
#include "util/logs.hpp"
#include "Utilities/StrUtil.h"
#include <algorithm>
#include <wrl/client.h>
#include <Windows.h>
#include <system_error>
#include <mmdeviceapi.h>
#include <Functiondiscoverykeys_devpkey.h>
LOG_CHANNEL(xaudio_dev_enum);
xaudio2_enumerator::xaudio2_enumerator() : audio_device_enumerator()
{
}
xaudio2_enumerator::~xaudio2_enumerator()
{
}
std::vector<audio_device_enumerator::audio_device> xaudio2_enumerator::get_output_devices()
{
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> devEnum{};
if (HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(devEnum.GetAddressOf())); FAILED(hr))
{
xaudio_dev_enum.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
return {};
}
Microsoft::WRL::ComPtr<IMMDeviceCollection> devices{};
if (HRESULT hr = devEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &devices); FAILED(hr))
{
xaudio_dev_enum.error("EnumAudioEndpoints() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
return {};
}
UINT count = 0;
if (HRESULT hr = devices->GetCount(&count); FAILED(hr))
{
xaudio_dev_enum.error("devices->GetCount() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
return {};
}
if (count == 0)
{
xaudio_dev_enum.warning("No devices available");
return {};
}
std::vector<audio_device> device_list{};
for (UINT dev_idx = 0; dev_idx < count; dev_idx++)
{
Microsoft::WRL::ComPtr<IMMDevice> endpoint{};
if (HRESULT hr = devices->Item(dev_idx, endpoint.GetAddressOf()); FAILED(hr))
{
xaudio_dev_enum.error("devices->Item() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
continue;
}
LPWSTR id = nullptr;
if (HRESULT hr = endpoint->GetId(&id); FAILED(hr))
{
xaudio_dev_enum.error("endpoint->GetId() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
continue;
}
if (std::wstring_view{id}.empty())
{
xaudio_dev_enum.error("Empty device id - skipping");
CoTaskMemFree(id);
continue;
}
audio_device dev{};
dev.id = wchar_to_utf8(id);
CoTaskMemFree(id);
Microsoft::WRL::ComPtr<IPropertyStore> props{};
if (HRESULT hr = endpoint->OpenPropertyStore(STGM_READ, props.GetAddressOf()); FAILED(hr))
{
xaudio_dev_enum.error("endpoint->OpenPropertyStore() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
continue;
}
PROPVARIANT var;
PropVariantInit(&var);
if (HRESULT hr = props->GetValue(PKEY_Device_FriendlyName, &var); FAILED(hr))
{
xaudio_dev_enum.error("props->GetValue() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
PropVariantClear(&var);
continue;
}
if (var.vt != VT_LPWSTR)
{
PropVariantClear(&var);
continue;
}
dev.name = wchar_to_utf8(var.pwszVal);
if (dev.name.empty())
{
dev.name = dev.id;
}
PropVariantClear(&var);
dev.max_ch = 2;
if (HRESULT hr = props->GetValue(PKEY_AudioEngine_DeviceFormat, &var); SUCCEEDED(hr))
{
if (var.vt == VT_BLOB)
{
if (var.blob.cbSize == sizeof(PCMWAVEFORMAT))
{
const PCMWAVEFORMAT* pcm = std::bit_cast<const PCMWAVEFORMAT*>(var.blob.pBlobData);
dev.max_ch = pcm->wf.nChannels;
}
else if (var.blob.cbSize >= sizeof(WAVEFORMATEX))
{
const WAVEFORMATEX* wfx = std::bit_cast<const WAVEFORMATEX*>(var.blob.pBlobData);
if (var.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize || wfx->wFormatTag == WAVE_FORMAT_PCM)
{
dev.max_ch = wfx->nChannels;
}
}
}
}
else
{
xaudio_dev_enum.error("props->GetValue() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
}
PropVariantClear(&var);
xaudio_dev_enum.notice("Found device: id=%s, name=%s, max_ch=%d", dev.id, dev.name, dev.max_ch);
device_list.emplace_back(dev);
}
std::sort(device_list.begin(), device_list.end(), [](audio_device_enumerator::audio_device a, audio_device_enumerator::audio_device b)
{
return a.name < b.name;
});
return device_list;
}
@@ -0,0 +1,17 @@
#pragma once
#ifndef _WIN32
#error "XAudio2 can only be built on Windows."
#endif
#include "Emu/Audio/audio_device_enumerator.h"
class xaudio2_enumerator final : public audio_device_enumerator
{
public:
xaudio2_enumerator();
~xaudio2_enumerator() override;
std::vector<audio_device> get_output_devices() override;
};
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "util/types.hpp"
#include <vector>
#include <string>
class audio_device_enumerator
{
public:
static constexpr std::string_view DEFAULT_DEV_ID = "@@@default@@@";
struct audio_device
{
std::string id{};
std::string name{};
usz max_ch{};
};
audio_device_enumerator() {};
virtual ~audio_device_enumerator() = default;
// Enumerate available output devices.
virtual std::vector<audio_device> get_output_devices() = 0;
};
-147
View File
@@ -1,147 +0,0 @@
#include "stdafx.h"
#include "audio_device_listener.h"
#include "util/logs.hpp"
#include "Utilities/StrUtil.h"
#include "Emu/Cell/Modules/cellAudio.h"
#include "Emu/IdManager.h"
LOG_CHANNEL(IO);
audio_device_listener::audio_device_listener()
{
#ifdef _WIN32
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
{
m_com_init_success = true;
}
// Try to register a listener for device changes
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_device_enumerator));
if (hr != S_OK)
{
IO.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
}
else if (m_device_enumerator)
{
hr = m_device_enumerator->RegisterEndpointNotificationCallback(this);
if (FAILED(hr))
{
IO.error("RegisterEndpointNotificationCallback() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
m_device_enumerator->Release();
}
}
else
{
IO.error("Device enumerator invalid");
}
#endif
}
audio_device_listener::~audio_device_listener()
{
#ifdef _WIN32
if (m_device_enumerator != nullptr)
{
m_device_enumerator->UnregisterEndpointNotificationCallback(this);
m_device_enumerator->Release();
}
if (m_com_init_success)
{
CoUninitialize();
}
#endif
}
bool audio_device_listener::input_device_changed()
{
return m_input_device_changed.test_and_reset();
}
bool audio_device_listener::output_device_changed()
{
return m_output_device_changed.test_and_reset();
}
#ifdef _WIN32
template <>
void fmt_class_string<ERole>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto value)
{
switch (value)
{
case eConsole: return "eConsole";
case eMultimedia: return "eMultimedia";
case eCommunications: return "eCommunications";
}
return unknown;
});
}
template <>
void fmt_class_string<EDataFlow>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto value)
{
switch (value)
{
case eRender: return "eRender";
case eCapture: return "eCapture";
case eAll: return "eAll";
}
return unknown;
});
}
HRESULT audio_device_listener::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id)
{
IO.notice("OnDefaultDeviceChanged(flow=%s, role=%s, new_default_device_id=0x%x)", flow, role, new_default_device_id);
if (!new_default_device_id)
{
IO.notice("OnDefaultDeviceChanged(): new_default_device_id empty");
return S_OK;
}
// Listen only for one device role, otherwise we're going to receive more than one
// notification for flow type
if (role != eConsole)
{
IO.notice("OnDefaultDeviceChanged(): we don't care about this device");
return S_OK;
}
const std::wstring tmp(new_default_device_id);
const std::string new_device_id = wchar_to_utf8(tmp.c_str());
if (flow == eRender || flow == eAll)
{
if (output_device_id != new_device_id)
{
output_device_id = new_device_id;
IO.warning("Default output device changed: new device = '%s'", output_device_id);
m_output_device_changed = true;
}
}
if (flow == eCapture || flow == eAll)
{
if (input_device_id != new_device_id)
{
input_device_id = new_device_id;
IO.warning("Default input device changed: new device = '%s'", input_device_id);
m_input_device_changed = true;
}
}
return S_OK;
}
#endif
-42
View File
@@ -1,42 +0,0 @@
#pragma once
#ifdef _WIN32
#include <MMDeviceAPI.h>
#endif
#include "util/atomic.hpp"
#ifdef _WIN32
class audio_device_listener : public IMMNotificationClient
#else
class audio_device_listener
#endif
{
public:
audio_device_listener();
~audio_device_listener();
bool input_device_changed();
bool output_device_changed();
private:
#ifdef _WIN32
std::string input_device_id;
std::string output_device_id;
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };
IFACEMETHODIMP_(ULONG) Release() override { return 1; };
IFACEMETHODIMP QueryInterface(REFIID /*iid*/, void** /*object*/) override { return S_OK; };
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR /*device_id*/, const PROPERTYKEY /*key*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceAdded(LPCWSTR /*device_id*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR /*device_id*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR /*device_id*/, DWORD /*new_state*/) override { return S_OK; };
IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override;
IMMDeviceEnumerator* m_device_enumerator = nullptr;
bool m_com_init_success = false;
#endif
atomic_t<bool> m_input_device_changed = false;
atomic_t<bool> m_output_device_changed = false;
};
+14 -2
View File
@@ -2,6 +2,7 @@ add_library(rpcs3_emu
cache_utils.cpp
IdManager.cpp
localized_string.cpp
savestate_utils.cpp
System.cpp
system_config.cpp
system_config_types.cpp
@@ -58,6 +59,7 @@ target_sources(rpcs3_emu PRIVATE
../../Utilities/rXml.cpp
../../Utilities/sema.cpp
../../Utilities/simple_ringbuf.cpp
../../Utilities/stack_trace.cpp
../../Utilities/StrFmt.cpp
../../Utilities/Thread.cpp
../../Utilities/version.cpp
@@ -118,17 +120,20 @@ target_sources(rpcs3_emu PRIVATE
# Audio
target_sources(rpcs3_emu PRIVATE
Audio/audio_device_listener.cpp
Audio/audio_resampler.cpp
Audio/AudioDumper.cpp
Audio/AudioBackend.cpp
Audio/Cubeb/CubebBackend.cpp
Audio/Cubeb/cubeb_enumerator.cpp
)
if(USE_FAUDIO)
find_package(SDL2)
if(SDL2_FOUND AND NOT SDL2_VERSION VERSION_LESS 2.0.9)
target_sources(rpcs3_emu PRIVATE Audio/FAudio/FAudioBackend.cpp)
target_sources(rpcs3_emu PRIVATE
Audio/FAudio/FAudioBackend.cpp
Audio/FAudio/faudio_enumerator.cpp
)
target_link_libraries(rpcs3_emu PUBLIC 3rdparty::faudio)
endif()
endif()
@@ -139,6 +144,7 @@ if(WIN32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DELAYLOAD:xaudio2_9redist.dll")
target_sources(rpcs3_emu PRIVATE
Audio/XAudio2/XAudio2Backend.cpp
Audio/XAudio2/xaudio2_enumerator.cpp
)
endif()
@@ -259,6 +265,7 @@ target_sources(rpcs3_emu PRIVATE
Cell/Modules/cellMsgDialog.cpp
Cell/Modules/cellMusic.cpp
Cell/Modules/cellMusicDecode.cpp
Cell/Modules/cellMusicSelectionContext.cpp
Cell/Modules/cellMusicExport.cpp
Cell/Modules/cellNetAoi.cpp
Cell/Modules/cellNetCtl.cpp
@@ -430,6 +437,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/Null/NullGSRender.cpp
RSX/Overlays/overlay_animation.cpp
RSX/Overlays/overlay_controls.cpp
RSX/Overlays/overlay_cursor.cpp
RSX/Overlays/overlay_edit_text.cpp
RSX/Overlays/overlay_fonts.cpp
RSX/Overlays/overlay_list_view.cpp
@@ -457,10 +465,14 @@ target_sources(rpcs3_emu PRIVATE
RSX/Program/VertexProgramDecompiler.cpp
RSX/Capture/rsx_capture.cpp
RSX/Capture/rsx_replay.cpp
RSX/GL/glutils/blitter.cpp
RSX/GL/glutils/buffer_object.cpp
RSX/GL/glutils/common.cpp
RSX/GL/glutils/fbo.cpp
RSX/GL/glutils/image.cpp
RSX/GL/glutils/program.cpp
RSX/GL/glutils/ring_buffer.cpp
RSX/GL/glutils/sampler.cpp
RSX/GL/GLCommonDecompiler.cpp
RSX/GL/GLCompute.cpp
RSX/GL/GLDraw.cpp
+112 -7
View File
@@ -49,6 +49,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
case cpu_flag::pause: return "p";
case cpu_flag::suspend: return "s";
case cpu_flag::ret: return "ret";
case cpu_flag::again: return "a";
case cpu_flag::signal: return "sig";
case cpu_flag::memory: return "mem";
case cpu_flag::pending: return "pend";
@@ -699,6 +700,12 @@ bool cpu_thread::check_state() noexcept
store = true;
}
if (flags & cpu_flag::notify)
{
flags -= cpu_flag::notify;
store = true;
}
// Can't process dbg_step if we only paused temporarily
if (cpu_can_stop && flags & cpu_flag::dbg_step)
{
@@ -720,7 +727,7 @@ bool cpu_thread::check_state() noexcept
}
// Atomically clean wait flag and escape
if (!(flags & (cpu_flag::exit + cpu_flag::ret + cpu_flag::stop)))
if (!is_stopped(flags) && flags.none_of(cpu_flag::ret))
{
// Check pause flags which hold thread inside check_state (ignore suspend/debug flags on cpu_flag::temp)
if (flags & (cpu_flag::pause + cpu_flag::memory) || (cpu_can_stop && flags & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::suspend)))
@@ -768,16 +775,16 @@ bool cpu_thread::check_state() noexcept
cpu_counter::add(this);
}
constexpr auto pending_and_temp = (cpu_flag::pending + cpu_flag::temp);
if ((state0 & pending_and_temp) == cpu_flag::pending)
if (cpu_can_stop && state0 & cpu_flag::pending)
{
// Execute pending work
cpu_work();
if ((state1 ^ state) - pending_and_temp)
if ((state1 ^ state) - cpu_flag::pending)
{
// Work could have changed flags
// Reset internal flags as if check_state() has just been called
cpu_sleep_called = false;
continue;
}
}
@@ -874,8 +881,19 @@ void cpu_thread::notify()
cpu_thread& cpu_thread::operator=(thread_state)
{
state += cpu_flag::exit;
state.notify_one(cpu_flag::exit);
if (state & cpu_flag::exit)
{
// Must be notified elsewhere or self-raised
return *this;
}
const auto old = state.fetch_add(cpu_flag::exit);
if (old & cpu_flag::wait && old.none_of(cpu_flag::again + cpu_flag::exit))
{
state.notify_one(cpu_flag::exit);
}
return *this;
}
@@ -945,6 +963,24 @@ u32* cpu_thread::get_pc2()
return nullptr;
}
cpu_thread* cpu_thread::get_next_cpu()
{
switch (id_type())
{
case 1:
{
return static_cast<ppu_thread*>(this)->next_cpu;
}
case 2:
{
return static_cast<spu_thread*>(this)->next_cpu;
}
default: break;
}
return nullptr;
}
std::shared_ptr<CPUDisAsm> make_disasm(const cpu_thread* cpu);
void cpu_thread::dump_all(std::string& ret) const
@@ -1195,3 +1231,72 @@ u32 CPUDisAsm::DisAsmBranchTarget(s32 /*imm*/)
// Unused
return 0;
}
extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates()
{
const u64 start = get_system_time();
// Attempt to lock for half a second, if somehow takes longer abort it
do
{
if (cpu_thread::suspend_all(nullptr, {}, []()
{
return idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
{
if (!spu.unsavable)
{
if (Emu.IsPaused())
{
// If emulation is paused, we can only hope it's already in a state compatible with savestates
return !!(spu.state & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause));
}
else
{
ensure(!spu.state.test_and_set(cpu_flag::dbg_global_pause));
}
return false;
}
return true;
}).ret;
}))
{
if (Emu.IsPaused())
{
return false;
}
// It's faster to lock once
reader_lock lock(id_manager::g_mutex);
idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
{
spu.state -= cpu_flag::dbg_global_pause;
}, idm::unlocked);
// For faster signalling, first remove state flags then batch notifications
idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
{
if (spu.state & cpu_flag::wait)
{
spu.state.notify_one();
}
}, idm::unlocked);
continue;
}
return true;
} while (std::this_thread::yield(), get_system_time() - start <= 500'000);
idm::select<named_thread<spu_thread>>([&](u32, named_thread<spu_thread>& spu)
{
if (spu.state.test_and_reset(cpu_flag::dbg_global_pause))
{
spu.state.notify_one();
}
});
return false;
}
+5 -2
View File
@@ -19,10 +19,12 @@ enum class cpu_flag : u32
pause, // Thread suspended by suspend_all technique
suspend, // Thread suspended
ret, // Callback return requested
again, // Thread must complete the syscall after deserialization
signal, // Thread received a signal (HLE)
memory, // Thread must unlock memory mutex
pending, // Thread has postponed work
pending_recheck, // Thread needs to recheck if there is pending work before ::pending removal
notify, // Flag meant solely to allow atomic notification on state without changing other flags
dbg_global_pause, // Emulation paused
dbg_pause, // Thread paused
@@ -34,7 +36,7 @@ enum class cpu_flag : u32
// Test stopped state
constexpr bool is_stopped(bs_t<cpu_flag> state)
{
return !!(state & (cpu_flag::stop + cpu_flag::exit));
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::again));
}
// Test paused state
@@ -139,6 +141,7 @@ public:
u32 get_pc() const;
u32* get_pc2(); // Last PC before stepping for the debugger (may be null)
cpu_thread* get_next_cpu(); // Access next_cpu member if the is one
void notify();
cpu_thread& operator=(thread_state);
@@ -172,7 +175,7 @@ public:
virtual void cpu_sleep() {}
// Callback for cpu_flag::pending
virtual void cpu_work() {}
virtual void cpu_work() { state -= cpu_flag::pending + cpu_flag::pending_recheck; }
// Callback for cpu_flag::ret
virtual void cpu_return() {}
+22
View File
@@ -32,7 +32,21 @@ cpu_translator::cpu_translator(llvm::Module* _module, bool is_be)
if (m_use_ssse3)
{
#if defined(ARCH_X64)
return m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_ssse3_pshuf_b_128), {data0, index});
#elif defined(ARCH_ARM64)
// Modified from sse2neon
// movi v2.16b, #143
// and v1.16b, v1.16b, v2.16b
// tbl v0.16b, { v0.16b }, v1.16b
auto mask = llvm::ConstantInt::get(get_type<u8[16]>(), 0x8F);
auto and_mask = llvm::ConstantInt::get(get_type<bool[16]>(), true);
auto vec_len = llvm::ConstantInt::get(get_type<u32>(), 16);
auto index_masked = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::vp_and), {index, mask, and_mask, vec_len});
return m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl1), {data0, index_masked});
#else
#error "Unimplemented"
#endif
}
else
{
@@ -140,6 +154,14 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
m_use_avx512_icl = true;
m_use_vnni = true;
}
// Aarch64 CPUs
if (cpu == "cyclone" || cpu.contains("cortex"))
{
m_use_fma = true;
// AVX does not use intrinsics so far
m_use_avx = true;
}
}
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
+2 -5
View File
@@ -2896,12 +2896,8 @@ protected:
bool m_is_be;
// Allow PSHUFB intrinsic
#ifdef ARCH_X64
bool m_use_ssse3 = true;
#else
// TODO: fix the pshufb arm64 native impl using TBL instruction
bool m_use_ssse3 = false;
#endif
// Allow FMA
bool m_use_fma = false;
@@ -2979,6 +2975,7 @@ public:
m_engine->updateGlobalMapping({lame.data(), lame.size()}, reinterpret_cast<uptr>(_func));
const auto inst = m_ir->CreateCall(func, {args...});
inst->setTailCallKind(llvm::CallInst::TCK_NoTail);
#ifdef _WIN32
inst->setCallingConv(llvm::CallingConv::Win64);
#endif
+2
View File
@@ -39,6 +39,8 @@ public:
{
return value;
}
ENABLE_BITWISE_SERIALIZATION;
};
enum CellNotAnError : s32
+2
View File
@@ -88,6 +88,8 @@ enum : u32
struct alignas(16) spu_mfc_cmd
{
ENABLE_BITWISE_SERIALIZATION;
MFC cmd;
u8 tag;
u16 size;
+119 -9
View File
@@ -66,11 +66,24 @@ void cell_audio_config::reset(bool backend_changed)
const AudioFreq freq = AudioFreq::FREQ_48K;
const AudioSampleSize sample_size = raw.convert_to_s16 ? AudioSampleSize::S16 : AudioSampleSize::FLOAT;
const auto [ch_cnt, downmix] = AudioBackend::get_channel_count_and_downmixer(0); // CELL_AUDIO_OUT_PRIMARY
const f64 cb_frame_len = backend->Open(freq, sample_size, ch_cnt) ? backend->GetCallbackFrameLen() : 0.0;
const auto [req_ch_cnt, downmix] = AudioBackend::get_channel_count_and_downmixer(0); // CELL_AUDIO_OUT_PRIMARY
f64 cb_frame_len = 0.0;
u32 ch_cnt = 2;
if (backend->Open(raw.audio_device, freq, sample_size, req_ch_cnt))
{
cb_frame_len = backend->GetCallbackFrameLen();
ch_cnt = backend->get_channels();
}
else
{
cellAudio.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
}
audio_downmix = downmix;
audio_channels = static_cast<u32>(ch_cnt);
backend_ch_cnt = AudioChannelCnt{ch_cnt};
audio_channels = static_cast<u32>(req_ch_cnt);
audio_sampling_rate = static_cast<u32>(freq);
audio_block_period = AUDIO_BUFFER_SAMPLES * 1'000'000 / audio_sampling_rate;
audio_sample_size = static_cast<u32>(sample_size);
@@ -143,7 +156,7 @@ audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
return cfg.audio_min_buffer_duration;
}();
cb_ringbuf.set_buf_size(static_cast<u32>(cfg.audio_channels * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
cb_ringbuf.set_buf_size(static_cast<u32>(static_cast<u32>(cfg.backend_ch_cnt) * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
backend->SetWriteCallback(std::bind(&audio_ringbuffer::backend_write_callback, this, std::placeholders::_1, std::placeholders::_2));
}
@@ -191,7 +204,7 @@ float* audio_ringbuffer::get_current_buffer() const
u64 audio_ringbuffer::get_enqueued_samples() const
{
AUDIT(cfg.buffering_enabled);
const u64 ringbuf_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * cfg.audio_channels);
const u64 ringbuf_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt));
if (cfg.time_stretching_enabled)
{
@@ -252,7 +265,7 @@ void audio_ringbuffer::process_resampled_data()
{
if (!cfg.time_stretching_enabled) return;
const auto samples = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * cfg.audio_channels)));
const auto samples = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt))));
commit_data(samples.first, samples.second);
}
@@ -263,12 +276,48 @@ void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
// Dump audio if enabled
m_dump.WriteData(buf, sample_cnt * static_cast<u32>(AudioSampleSize::FLOAT));
if (cfg.backend->get_convert_to_s16())
if (cfg.backend_ch_cnt < AudioChannelCnt{cfg.audio_channels})
{
AudioBackend::convert_to_s16(sample_cnt, buf, buf);
if (AudioChannelCnt{cfg.audio_channels} == AudioChannelCnt::SURROUND_7_1)
{
if (cfg.backend_ch_cnt == AudioChannelCnt::SURROUND_5_1)
{
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::SURROUND_5_1>(sample_cnt, buf, buf);
}
else if (cfg.backend_ch_cnt == AudioChannelCnt::STEREO)
{
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::STEREO>(sample_cnt, buf, buf);
}
else
{
fmt::throw_exception("Invalid downmix combination: %u -> %u", cfg.audio_channels, static_cast<u32>(cfg.backend_ch_cnt));
}
}
else if (AudioChannelCnt{cfg.audio_channels} == AudioChannelCnt::SURROUND_5_1)
{
if (cfg.backend_ch_cnt == AudioChannelCnt::STEREO)
{
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, AudioChannelCnt::STEREO>(sample_cnt, buf, buf);
}
else
{
fmt::throw_exception("Invalid downmix combination: %u -> %u", cfg.audio_channels, static_cast<u32>(cfg.backend_ch_cnt));
}
}
else
{
fmt::throw_exception("Invalid downmix combination: %u -> %u", cfg.audio_channels, static_cast<u32>(cfg.backend_ch_cnt));
}
}
cb_ringbuf.push(buf, sample_cnt * cfg.audio_sample_size);
const u32 sample_cnt_out = sample_cnt / cfg.audio_channels * static_cast<u32>(cfg.backend_ch_cnt);
if (cfg.backend->get_convert_to_s16())
{
AudioBackend::convert_to_s16(sample_cnt_out, buf, buf);
}
cb_ringbuf.push(buf, sample_cnt_out * cfg.audio_sample_size);
}
void audio_ringbuffer::play()
@@ -342,6 +391,52 @@ void audio_port::tag(s32 offset)
prev_touched_tag_nr = -1;
}
cell_audio_thread::cell_audio_thread(utils::serial& ar)
: cell_audio_thread()
{
ar(init);
if (!init)
{
return;
}
ar(key_count, event_period);
keys.resize(ar);
for (key_info& k : keys)
{
ar(k.start_period, k.flags, k.source);
k.port = lv2_event_queue::load_ptr(ar, k.port);
}
ar(ports);
}
void cell_audio_thread::save(utils::serial& ar)
{
ar(init);
if (!init)
{
return;
}
USING_SERIALIZATION_VERSION(cellAudio);
ar(key_count, event_period);
ar(keys.size());
for (const key_info& k : keys)
{
ar(k.start_period, k.flags, k.source);
lv2_event_queue::save_ptr(ar, k.port.get());
}
ar(ports);
}
std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
{
AUDIT(cfg.buffering_enabled);
@@ -511,6 +606,7 @@ void cell_audio_thread::advance(u64 timestamp)
for (u32 i = 0; i < queue_count; i++)
{
lv2_obj::notify_all_t notify;
queues[i]->send(event_sources[i], 0, 0, 0);
}
}
@@ -521,6 +617,7 @@ namespace audio
{
return
{
.audio_device = g_cfg.audio.audio_device,
.buffering_enabled = static_cast<bool>(g_cfg.audio.enable_buffering),
.desired_buffer_duration = g_cfg.audio.desired_buffer_duration,
.enable_time_stretching = static_cast<bool>(g_cfg.audio.enable_time_stretching),
@@ -546,6 +643,7 @@ namespace audio
if (const auto raw = g_audio.cfg.raw;
force_reset ||
raw.audio_device != new_raw.audio_device ||
raw.desired_buffer_duration != new_raw.desired_buffer_duration ||
raw.buffering_enabled != new_raw.buffering_enabled ||
raw.time_stretching_threshold != new_raw.time_stretching_threshold ||
@@ -615,6 +713,11 @@ void cell_audio_thread::operator()()
thread_ctrl::scoped_priority high_prio(+1);
while (Emu.IsPaused())
{
thread_ctrl::wait_for(5000);
}
u32 untouched_expected = 0;
// Main cellAudio loop
@@ -648,6 +751,13 @@ void cell_audio_thread::operator()()
continue;
}
if (ringbuffer->device_changed())
{
cellAudio.warning("Default device changed, attempting to switch...");
update_config(false);
continue;
}
if (m_backend_failed)
{
cellAudio.warning("Backend recovered");
+24 -3
View File
@@ -189,12 +189,23 @@ struct audio_port
f32 last_tag_value[PORT_BUFFER_TAG_COUNT] = { 0 };
void tag(s32 offset = 0);
audio_port() = default;
// Handle copy ctor of atomic var
audio_port(const audio_port& r)
{
std::memcpy(this, &r, sizeof(r));
}
ENABLE_BITWISE_SERIALIZATION;
};
struct cell_audio_config
{
struct raw_config
{
std::string audio_device{};
bool buffering_enabled = false;
s64 desired_buffer_duration = 0;
bool enable_time_stretching = false;
@@ -211,6 +222,7 @@ struct cell_audio_config
std::shared_ptr<AudioBackend> backend = nullptr;
AudioChannelCnt audio_downmix = AudioChannelCnt::SURROUND_7_1;
AudioChannelCnt backend_ch_cnt = AudioChannelCnt::SURROUND_7_1;
u32 audio_channels = 0;
u32 audio_sampling_rate = 0;
u32 audio_block_period = 0;
@@ -333,6 +345,11 @@ public:
return backend->Operational();
}
bool device_changed() const
{
return backend->DefaultDeviceChanged();
}
std::string_view get_backend_name() const
{
return backend->GetName();
@@ -366,7 +383,7 @@ public:
atomic_t<audio_backend_update> m_update_configuration = audio_backend_update::NONE;
shared_mutex mutex{};
atomic_t<u32> init = 0;
atomic_t<u8> init = 0;
u32 key_count = 0;
u8 event_period = 0;
@@ -390,10 +407,14 @@ public:
bool m_backend_failed = false;
bool m_audio_should_restart = false;
cell_audio_thread();
void operator()();
SAVESTATE_INIT_POS(9);
cell_audio_thread();
cell_audio_thread(utils::serial& ar);
void save(utils::serial& ar);
audio_port* open_port();
static constexpr auto thread_name = "cellAudio Thread"sv;
+17
View File
@@ -178,6 +178,23 @@ audio_out_configuration::audio_out_configuration()
cellSysutil.notice("cellAudioOut: initial secondary output configuration: channels=%d, encoder=%d, downmixer=%d", secondary_output.channels, secondary_output.encoder, secondary_output.downmixer);
}
audio_out_configuration::audio_out_configuration(utils::serial& ar)
: audio_out_configuration()
{
// Load configuartion (ar is reading)
save(ar);
}
void audio_out_configuration::save(utils::serial& ar)
{
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellAudioOut);
for (auto& state : out)
{
ar(state.state, state.channels, state.encoder, state.downmixer, state.copy_control, state.sound_modes, state.sound_mode);
}
}
std::pair<AudioChannelCnt, AudioChannelCnt> audio_out_configuration::audio_out::get_channel_count_and_downmixer() const
{
std::pair<AudioChannelCnt, AudioChannelCnt> ret;
+5
View File
@@ -135,6 +135,8 @@ struct CellAudioOutSoundMode
u8 fs;
u8 reserved;
be_t<u32> layout;
ENABLE_BITWISE_SERIALIZATION;
};
struct CellAudioOutDeviceInfo
@@ -217,5 +219,8 @@ struct audio_out_configuration
std::array<audio_out, 2> out;
SAVESTATE_INIT_POS(8.9); // Is a dependency of cellAudio
audio_out_configuration();
audio_out_configuration(utils::serial& ar);
void save(utils::serial& ar);
};
+19
View File
@@ -131,6 +131,25 @@ static const char* get_camera_attr_name(s32 value)
return nullptr;
}
camera_context::camera_context(utils::serial& ar)
{
save(ar);
}
void camera_context::save(utils::serial& ar)
{
ar(init);
if (!init)
{
return;
}
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera);
ar(notify_data_map, start_timestamp, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num);
}
static bool check_dev_num(s32 dev_num)
{
return dev_num == 0;
+14 -1
View File
@@ -375,6 +375,8 @@ struct CellCameraInfoEx
be_t<u32> container;
be_t<s32> read_mode;
vm::bptr<u8> pbuf[2];
ENABLE_BITWISE_SERIALIZATION;
};
struct CellCameraReadEx
@@ -392,6 +394,8 @@ class camera_context
{
u64 source;
u64 flag;
ENABLE_BITWISE_SERIALIZATION;
};
public:
@@ -433,14 +437,23 @@ public:
struct attr_t
{
u32 v1, v2;
ENABLE_BITWISE_SERIALIZATION;
};
attr_t attr[500]{};
atomic_t<bool> has_new_frame = false;
atomic_t<u32> frame_num = 0;
atomic_t<u32> frame_timestamp = 0;
atomic_t<u32> bytes_read = 0;
atomic_t<u32> init = 0;
atomic_t<u8> init = 0;
SAVESTATE_INIT_POS(16);
camera_context() = default;
camera_context(utils::serial& ar);
void save(utils::serial& ar);
static constexpr auto thread_name = "Camera Thread"sv;
+117 -12
View File
@@ -1,7 +1,11 @@
#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/IdManager.h"
#include "Emu/localized_string.h"
#include "cellSysutil.h"
#include "cellCrossController.h"
#include "cellMsgDialog.h"
LOG_CHANNEL(cellCrossController);
@@ -33,15 +37,117 @@ void fmt_class_string<CellCrossControllerError>::format(std::string& out, u64 ar
});
}
void finish_callback(ppu_thread& ppu, s32 button_type, vm::ptr<void> userdata); // Forward declaration
struct cross_controller
{
atomic_t<s32> status{0};
std::unique_ptr<named_thread<std::function<void()>>> connection_thread;
vm::ptr<CellCrossControllerCallback> callback = vm::null;
vm::ptr<void> userdata = vm::null;
void on_connection_established(s32 status)
{
ensure(!!callback);
close_msg_dialog();
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, status, vm::null, userdata);
return CELL_OK;
});
}
void run_thread(vm::cptr<CellCrossControllerPackageInfo> pPkgInfo)
{
ensure(!!pPkgInfo);
ensure(!!callback);
const std::string msg = fmt::format("%s\n\n%s",
get_localized_string(localized_string_id::CELL_CROSS_CONTROLLER_MSG, pPkgInfo->pTitle.get_ptr()),
get_localized_string(localized_string_id::CELL_CROSS_CONTROLLER_FW_MSG));
vm::bptr<CellMsgDialogCallback> msg_dialog_callback = vm::null;
msg_dialog_callback.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(finish_callback)));
// TODO: Show icons from comboplay_plugin.rco in dialog. Maybe use a new dialog or add an optional icon to this one.
error_code res = open_msg_dialog(false, CELL_MSGDIALOG_TYPE_DISABLE_CANCEL_OFF, vm::make_str(msg), msg_dialog_callback, userdata);
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_INITIALIZED, res == CELL_OK ? CELL_OK : CELL_CROSS_CONTROLLER_ERROR_INTERNAL, vm::null, userdata);
return CELL_OK;
});
status = CELL_CROSS_CONTROLLER_STATUS_INITIALIZED;
connection_thread = std::make_unique<named_thread<std::function<void()>>>(fmt::format("Cross-Controller Thread"), [this]()
{
while (thread_ctrl::state() != thread_state::aborting)
{
if (Emu.IsPaused())
{
thread_ctrl::wait_for(10'000);
continue;
}
// TODO: establish connection to PS Vita
if (false)
{
on_connection_established(CELL_OK);
}
thread_ctrl::wait_for(1000);
}
status = CELL_CROSS_CONTROLLER_STATUS_FINALIZED;
});
}
void stop_thread()
{
if (connection_thread)
{
auto& thread = *connection_thread;
thread = thread_state::aborting;
thread();
connection_thread.reset();
}
};
};
void finish_callback(ppu_thread& ppu, s32 button_type, vm::ptr<void> userdata)
{
cross_controller& cc = g_fxo->get<cross_controller>();
// This function should only be called when the user canceled the dialog
ensure(cc.callback && button_type == CELL_MSGDIALOG_BUTTON_ESCAPE);
cc.callback(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, CELL_CROSS_CONTROLLER_ERROR_CANCEL, vm::null, userdata);
cc.stop_thread();
}
error_code cellCrossControllerInitialize(vm::cptr<CellCrossControllerParam> pParam, vm::cptr<CellCrossControllerPackageInfo> pPkgInfo, vm::ptr<CellCrossControllerCallback> cb, vm::ptr<void> userdata) // LittleBigPlanet 2 and 3
{
cellCrossController.todo("cellCrossControllerInitialize(pParam=*0x%x, pPkgInfo=*0x%x, cb=*0x%x, userdata=*0x%x)", pParam, pPkgInfo, cb, userdata);
// TODO
//if (something)
//{
// return CELL_CROSS_CONTROLLER_ERROR_INVALID_STATE;
//}
if (pParam)
{
cellCrossController.notice("cellCrossControllerInitialize: pParam: pPackageFileName=%s, pSignatureFileName=%s, pIconFileName=%s", pParam->pPackageFileName, pParam->pSignatureFileName, pParam->pIconFileName);
}
if (pPkgInfo)
{
cellCrossController.notice("cellCrossControllerInitialize: pPkgInfo: pTitle=%s, pTitleId=%s, pAppVer=%s", pPkgInfo->pTitle, pPkgInfo->pTitleId, pPkgInfo->pAppVer);
}
cross_controller& cc = g_fxo->get<cross_controller>();
if (cc.status == CELL_CROSS_CONTROLLER_STATUS_INITIALIZED) // TODO: confirm this logic
{
return CELL_CROSS_CONTROLLER_ERROR_INVALID_STATE;
}
if (!pParam || !pPkgInfo)
{
@@ -73,13 +179,9 @@ error_code cellCrossControllerInitialize(vm::cptr<CellCrossControllerParam> pPar
return CELL_CROSS_CONTROLLER_ERROR_INVALID_VALUE;
}
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
cb(ppu, CELL_CROSS_CONTROLLER_STATUS_INITIALIZED, CELL_OK, vm::null, userdata);
cb(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, CELL_OK, vm::null, userdata);
return CELL_OK;
});
cc.callback = cb;
cc.userdata = userdata;
cc.run_thread(pPkgInfo);
return CELL_OK;
}
@@ -88,4 +190,7 @@ error_code cellCrossControllerInitialize(vm::cptr<CellCrossControllerParam> pPar
DECLARE(ppu_module_manager::cellCrossController)("cellCrossController", []()
{
REG_FUNC(cellCrossController, cellCrossControllerInitialize);
// Helper Function
REG_HIDDEN_FUNC(finish_callback);
});
+1
View File
@@ -165,6 +165,7 @@ public:
static const u32 id_base = 1;
static const u32 id_step = 1;
static const u32 id_count = 1023;
SAVESTATE_INIT_POS(34);
ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec);
+327 -156
View File
@@ -9,6 +9,7 @@
#include "Emu/system_config.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/RSX/Overlays/overlay_cursor.h"
#include "Input/pad_thread.h"
#include <cmath> // for fmod
@@ -94,7 +95,7 @@ public:
static constexpr auto thread_name = "Gem Thread"sv;
atomic_t<u32> state = 0;
atomic_t<u8> state = 0;
struct gem_color
{
@@ -107,6 +108,23 @@ public:
g = std::clamp(g_, 0.0f, 1.0f);
b = std::clamp(b_, 0.0f, 1.0f);
}
static inline const gem_color& get_default_color(u32 gem_num)
{
static const gem_color gold = gem_color(1.0f, 0.85f, 0.0f);
static const gem_color green = gem_color(0.0f, 1.0f, 0.0f);
static const gem_color red = gem_color(1.0f, 0.0f, 0.0f);
static const gem_color pink = gem_color(0.9f, 0.0f, 0.5f);
switch (gem_num)
{
case 0: return green;
case 1: return gold;
case 2: return red;
case 3: return pink;
default: fmt::throw_exception("unexpected gem_num %d", gem_num);
}
}
};
struct gem_controller
@@ -131,6 +149,8 @@ public:
u64 calibration_start_us{0}; // The start timestamp of the calibration in microseconds
static constexpr u64 calibration_time_us = 500000; // The calibration supposedly takes 0.5 seconds (500000 microseconds)
ENABLE_BITWISE_SERIALIZATION;
};
CellGemAttribute attribute = {};
@@ -181,6 +201,11 @@ public:
void reset_controller(u32 gem_num)
{
if (gem_num >= CELL_GEM_MAX_NUM)
{
return;
}
switch (g_cfg.io.move)
{
case move_handler::fake:
@@ -194,14 +219,41 @@ public:
break;
}
gem_controller& controller = controllers.at(gem_num);
controller = {};
controller.sphere_rgb = gem_color::get_default_color(gem_num);
// Assign status and port number
if (gem_num < connected_controllers)
{
controllers[gem_num] = {};
controllers[gem_num].status = CELL_GEM_STATUS_READY;
controllers[gem_num].port = 7u - gem_num;
controller.status = CELL_GEM_STATUS_READY;
controller.port = CELL_PAD_MAX_PORT_NUM - gem_num;
}
}
gem_config_data() = default;
SAVESTATE_INIT_POS(15);
void save(utils::serial& ar)
{
ar(state);
if (!state)
{
return;
}
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellGem);
ar(attribute, vc_attribute, status_flags, enable_pitch_correction, inertial_counter, controllers
, connected_controllers, update_started, camera_frame, memory_ptr, start_timestamp);
}
gem_config_data(utils::serial& ar)
{
save(ar);
}
};
static inline int32_t cellGemGetVideoConvertSize(s32 output_format)
@@ -394,6 +446,102 @@ static bool check_gem_num(const u32 gem_num)
return gem_num < CELL_GEM_MAX_NUM;
}
static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_controller& controller, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
{
const u16 x = static_cast<u16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
const u16 y = static_cast<u16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
// Note: We shouldn't use sphere_rgb here. The game will set it to black in many cases.
const gem_config_data::gem_color& rgb = gem_config_data::gem_color::get_default_color(gem_num);
const color4f color = { rgb.r, rgb.g, rgb.b, 0.85f };
rsx::overlays::set_cursor(rsx::overlays::cursor_offset::cell_gem + gem_num, x, y, color, 2'000'000, false);
}
static inline void pos_to_gem_image_state(u32 gem_num, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
{
const auto& shared_data = g_fxo->get<gem_camera_shared>();
if (x_max <= 0) x_max = shared_data.width;
if (y_max <= 0) y_max = shared_data.height;
const f32 scaling_width = x_max / static_cast<f32>(shared_data.width);
const f32 scaling_height = y_max / static_cast<f32>(shared_data.height);
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
// Image coordinates in pixels
const f32 image_x = static_cast<f32>(x_pos) / scaling_width;
const f32 image_y = static_cast<f32>(y_pos) / scaling_height;
// Centered image coordinates in pixels
const f32 centered_x = image_x - (shared_data.width / 2.f);
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
// Camera coordinates in mm (centered, so it's the same as world coordinates)
const f32 camera_x = centered_x * mmPerPixel;
const f32 camera_y = centered_y * mmPerPixel;
// Image coordinates in pixels
gem_image_state->u = image_x;
gem_image_state->v = image_y;
// Projected camera coordinates in mm
gem_image_state->projectionx = camera_x / controller.distance;
gem_image_state->projectiony = camera_y / controller.distance;
if (g_cfg.io.show_move_cursor)
{
draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max);
}
}
static inline void pos_to_gem_state(u32 gem_num, const gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
{
const auto& shared_data = g_fxo->get<gem_camera_shared>();
if (x_max <= 0) x_max = shared_data.width;
if (y_max <= 0) y_max = shared_data.height;
const f32 scaling_width = x_max / static_cast<f32>(shared_data.width);
const f32 scaling_height = y_max / static_cast<f32>(shared_data.height);
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
// Image coordinates in pixels
const f32 image_x = static_cast<f32>(x_pos) / scaling_width;
const f32 image_y = static_cast<f32>(y_pos) / scaling_height;
// Centered image coordinates in pixels
const f32 centered_x = image_x - (shared_data.width / 2.f);
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
// Camera coordinates in mm (centered, so it's the same as world coordinates)
const f32 camera_x = centered_x * mmPerPixel;
const f32 camera_y = centered_y * mmPerPixel;
// World coordinates in mm
gem_state->pos[0] = camera_x;
gem_state->pos[1] = camera_y;
gem_state->pos[2] = static_cast<f32>(controller.distance);
gem_state->pos[3] = 0.f;
gem_state->quat[0] = 320.f - image_x;
gem_state->quat[1] = (y_pos / scaling_width) - 180.f;
gem_state->quat[2] = 1200.f;
// TODO: calculate handle position based on our world coordinate and the angles
gem_state->handle_pos[0] = camera_x;
gem_state->handle_pos[1] = camera_y;
gem_state->handle_pos[2] = static_cast<f32>(controller.distance + 10);
gem_state->handle_pos[3] = 0.f;
if (g_cfg.io.show_move_cursor)
{
draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max);
}
}
extern bool is_input_allowed();
/**
* \brief Maps Move controller data (digital buttons, and analog Trigger data) to DS3 pad input.
* Unavoidably buttons conflict with DS3 mappings, which is problematic for some games.
@@ -402,80 +550,146 @@ static bool check_gem_num(const u32 gem_num)
* \param analog_t Analog value of Move's Trigger. Currently mapped to R2.
* \return true on success, false if port_no controller is invalid
*/
static bool ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
{
digital_buttons = 0;
analog_t = 0;
if (!is_input_allowed())
{
return;
}
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
auto& pad = handler->GetPads()[port_no];
const auto& pad = handler->GetPads().at(port_no);
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return false;
for (Button& button : pad->m_buttons)
{
// here we check btns, and set pad accordingly
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
{
if (button.m_pressed) pad->m_digital_2 |= button.m_outKeyCode;
else pad->m_digital_2 &= ~button.m_outKeyCode;
return;
}
for (const Button& button : pad->m_buttons)
{
if (!button.m_pressed)
{
continue;
}
// here we check btns, and set pad accordingly
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
{
switch (button.m_outKeyCode)
{
case CELL_PAD_CTRL_START:
digital_buttons |= CELL_GEM_CTRL_START;
break;
case CELL_PAD_CTRL_SELECT:
digital_buttons |= CELL_GEM_CTRL_SELECT;
break;
default:
break;
}
}
else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
{
switch (button.m_outKeyCode)
{
case CELL_PAD_CTRL_SQUARE:
pad->m_press_square = button.m_value;
digital_buttons |= CELL_GEM_CTRL_SQUARE;
break;
case CELL_PAD_CTRL_CROSS:
pad->m_press_cross = button.m_value;
digital_buttons |= CELL_GEM_CTRL_CROSS;
break;
case CELL_PAD_CTRL_CIRCLE:
pad->m_press_circle = button.m_value;
digital_buttons |= CELL_GEM_CTRL_CIRCLE;
break;
case CELL_PAD_CTRL_TRIANGLE:
pad->m_press_triangle = button.m_value;
digital_buttons |= CELL_GEM_CTRL_TRIANGLE;
break;
case CELL_PAD_CTRL_R1:
pad->m_press_R1 = button.m_value;
break;
case CELL_PAD_CTRL_L1:
pad->m_press_L1 = button.m_value;
digital_buttons |= CELL_GEM_CTRL_MOVE;
break;
case CELL_PAD_CTRL_R2:
pad->m_press_R2 = button.m_value;
digital_buttons |= CELL_GEM_CTRL_T;
analog_t = std::max<u16>(analog_t, button.m_value);
break;
case CELL_PAD_CTRL_L2:
pad->m_press_L2 = button.m_value;
default:
break;
default: break;
}
}
}
}
digital_buttons = 0;
constexpr u16 ds3_max_x = 255;
constexpr u16 ds3_max_y = 255;
// map the Move key to R1 and the Trigger to R2
static inline void ds3_get_stick_values(const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
{
x_pos = 0;
y_pos = 0;
if (pad->m_press_R1)
digital_buttons |= CELL_GEM_CTRL_MOVE;
if (pad->m_press_R2)
digital_buttons |= CELL_GEM_CTRL_T;
for (const AnalogStick& stick : pad->m_sticks)
{
switch (stick.m_offset)
{
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X:
x_pos = stick.m_value;
break;
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
y_pos = stick.m_value;
break;
default:
break;
}
}
}
if (pad->m_press_cross)
digital_buttons |= CELL_GEM_CTRL_CROSS;
if (pad->m_press_circle)
digital_buttons |= CELL_GEM_CTRL_CIRCLE;
if (pad->m_press_square)
digital_buttons |= CELL_GEM_CTRL_SQUARE;
if (pad->m_press_triangle)
digital_buttons |= CELL_GEM_CTRL_TRIANGLE;
if (pad->m_digital_1)
digital_buttons |= CELL_GEM_CTRL_SELECT;
if (pad->m_digital_2)
digital_buttons |= CELL_GEM_CTRL_START;
static void ds3_pos_to_gem_image_state(const u32 port_no, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state)
{
if (!is_input_allowed())
{
return;
}
analog_t = pad->m_press_R2;
std::lock_guard lock(pad::g_pad_mutex);
return true;
const auto handler = pad::get_current_handler();
const auto& pad = handler->GetPads().at(port_no);
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
return;
}
s32 ds3_pos_x, ds3_pos_y;
ds3_get_stick_values(pad, ds3_pos_x, ds3_pos_y);
pos_to_gem_image_state(port_no, controller, gem_image_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
}
static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state)
{
if (!is_input_allowed())
{
return;
}
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
const auto& pad = handler->GetPads().at(port_no);
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
return;
}
s32 ds3_pos_x, ds3_pos_y;
ds3_get_stick_values(pad, ds3_pos_x, ds3_pos_y);
pos_to_gem_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
}
/**
@@ -486,15 +700,24 @@ static bool ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
* \param ext External data to modify
* \return true on success, false if port_no controller is invalid
*/
static bool ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
{
ext = {};
if (!is_input_allowed())
{
return;
}
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
auto& pad = handler->GetPads()[port_no];
const auto& pad = handler->GetPads().at(port_no);
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return false;
{
return;
}
ext.status = 0; // CELL_GEM_EXT_CONNECTED | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
ext.analog_left_x = pad->m_analog_left_x; // HACK: these pad members are actually only set in cellPad
@@ -513,8 +736,6 @@ static bool ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller
// xxxxx010: Firing mode selector is in position 2.
// xxxxx100: Firing mode selector is in position 3.
}
return true;
}
/**
@@ -528,16 +749,27 @@ static bool ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller
*/
static bool mouse_input_to_pad(const u32 mouse_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
{
digital_buttons = 0;
analog_t = 0;
if (!is_input_allowed())
{
return false;
}
auto& handler = g_fxo->get<MouseHandlerBase>();
std::scoped_lock lock(handler.mutex);
// Make sure that the mouse handler is initialized
handler.Init(std::min<u32>(g_fxo->get<gem_config>().attribute.max_connect, CELL_GEM_MAX_NUM));
if (mouse_no >= handler.GetMice().size())
{
return false;
}
const auto& mouse_data = handler.GetMice().at(0);
const auto& mouse_data = handler.GetMice().at(mouse_no);
const auto is_pressed = [&mouse_data](MouseButtonCodes button) -> bool { return !!(mouse_data.buttons & button); };
digital_buttons = 0;
@@ -571,102 +803,52 @@ static bool mouse_input_to_pad(const u32 mouse_no, be_t<u16>& digital_buttons, b
return true;
}
static bool mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state)
static void mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state)
{
if (!gem_image_state || !is_input_allowed())
{
return;
}
auto& handler = g_fxo->get<MouseHandlerBase>();
std::scoped_lock lock(handler.mutex);
if (!gem_image_state || mouse_no >= handler.GetMice().size())
// Make sure that the mouse handler is initialized
handler.Init(std::min<u32>(g_fxo->get<gem_config>().attribute.max_connect, CELL_GEM_MAX_NUM));
if (mouse_no >= handler.GetMice().size())
{
return false;
return;
}
const auto& mouse = handler.GetMice().at(0);
const auto& shared_data = g_fxo->get<gem_camera_shared>();
const auto& mouse = handler.GetMice().at(mouse_no);
s32 mouse_width = mouse.x_max;
if (mouse_width <= 0) mouse_width = shared_data.width;
s32 mouse_height = mouse.y_max;
if (mouse_height <= 0) mouse_height = shared_data.height;
const f32 scaling_width = mouse_width / static_cast<f32>(shared_data.width);
const f32 scaling_height = mouse_height / static_cast<f32>(shared_data.height);
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
// Image coordinates in pixels
const f32 image_x = static_cast<f32>(mouse.x_pos) / scaling_width;
const f32 image_y = static_cast<f32>(mouse.y_pos) / scaling_height;
// Centered image coordinates in pixels
const f32 centered_x = image_x - (shared_data.width / 2.f);
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
// Camera coordinates in mm (centered, so it's the same as world coordinates)
const f32 camera_x = centered_x * mmPerPixel;
const f32 camera_y = centered_y * mmPerPixel;
// Image coordinates in pixels
gem_image_state->u = image_x;
gem_image_state->v = image_y;
// Projected camera coordinates in mm
gem_image_state->projectionx = camera_x / controller.distance;
gem_image_state->projectiony = camera_y / controller.distance;
return true;
pos_to_gem_image_state(mouse_no, controller, gem_image_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
}
static bool mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state)
static void mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state)
{
if (!gem_state || !is_input_allowed())
{
return;
}
auto& handler = g_fxo->get<MouseHandlerBase>();
std::scoped_lock lock(handler.mutex);
if (!gem_state || mouse_no >= handler.GetMice().size())
// Make sure that the mouse handler is initialized
handler.Init(std::min<u32>(g_fxo->get<gem_config>().attribute.max_connect, CELL_GEM_MAX_NUM));
if (mouse_no >= handler.GetMice().size())
{
return false;
return;
}
const auto& mouse = handler.GetMice().at(0);
const auto& shared_data = g_fxo->get<gem_camera_shared>();
s32 mouse_width = mouse.x_max;
if (mouse_width <= 0) mouse_width = shared_data.width;
s32 mouse_height = mouse.y_max;
if (mouse_height <= 0) mouse_height = shared_data.height;
const f32 scaling_width = mouse_width / static_cast<f32>(shared_data.width);
const f32 scaling_height = mouse_height / static_cast<f32>(shared_data.height);
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
const auto& mouse = handler.GetMice().at(mouse_no);
// Image coordinates in pixels
const f32 image_x = static_cast<f32>(mouse.x_pos) / scaling_width;
const f32 image_y = static_cast<f32>(mouse.y_pos) / scaling_height;
// Centered image coordinates in pixels
const f32 centered_x = image_x - (shared_data.width / 2.f);
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
// Camera coordinates in mm (centered, so it's the same as world coordinates)
const f32 camera_x = centered_x * mmPerPixel;
const f32 camera_y = centered_y * mmPerPixel;
// World coordinates in mm
gem_state->pos[0] = camera_x;
gem_state->pos[1] = camera_y;
gem_state->pos[2] = static_cast<f32>(controller.distance);
gem_state->pos[3] = 0.f;
gem_state->quat[0] = 320.f - image_x;
gem_state->quat[1] = (mouse.y_pos / scaling_width) - 180.f;
gem_state->quat[2] = 1200.f;
// TODO: calculate handle position based on our world coordinate and the angles
gem_state->handle_pos[0] = camera_x;
gem_state->handle_pos[1] = camera_y;
gem_state->handle_pos[2] = static_cast<f32>(controller.distance + 10);
gem_state->handle_pos[3] = 0.f;
return true;
pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
}
// *********************
@@ -696,11 +878,8 @@ error_code cellGemCalibrate(u32 gem_num)
return CELL_EBUSY;
}
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
{
gem.controllers[gem_num].is_calibrating = true;
gem.controllers[gem_num].calibration_start_us = get_guest_system_time();
}
gem.controllers[gem_num].is_calibrating = true;
gem.controllers[gem_num].calibration_start_us = get_guest_system_time();
return CELL_OK;
}
@@ -854,7 +1033,7 @@ error_code cellGemEnd(ppu_thread& ppu)
if (gem.state.compare_and_swap_test(1, 0))
{
if (u32 addr = gem.memory_ptr)
if (u32 addr = std::exchange(gem.memory_ptr, 0))
{
sys_memory_free(ppu, addr);
}
@@ -1051,6 +1230,8 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_imag
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
*gem_image_state = {};
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
{
auto& shared_data = g_fxo->get<gem_camera_shared>();
@@ -1063,10 +1244,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_imag
if (g_cfg.io.move == move_handler::fake)
{
gem_image_state->u = 0;
gem_image_state->v = 0;
gem_image_state->projectionx = 1;
gem_image_state->projectiony = 1;
ds3_pos_to_gem_image_state(gem_num, gem.controllers[gem_num], gem_image_state);
}
else if (g_cfg.io.move == move_handler::mouse)
{
@@ -1100,6 +1278,8 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
return CELL_GEM_TIME_OUT_OF_RANGE;
}
*inertial_state = {};
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
{
ds3_input_to_ext(gem_num, gem.controllers[gem_num], inertial_state->ext);
@@ -1187,7 +1367,7 @@ error_code cellGemGetRGB(u32 gem_num, vm::ptr<float> r, vm::ptr<float> g, vm::pt
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
auto& sphere_color = gem.controllers[gem_num].sphere_rgb;
const gem_config_data::gem_color& sphere_color = gem.controllers[gem_num].sphere_rgb;
*r = sphere_color.r;
*g = sphere_color.g;
*b = sphere_color.b;
@@ -1260,6 +1440,8 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
return CELL_GEM_TIME_OUT_OF_RANGE;
}
*gem_state = {};
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
{
ds3_input_to_ext(gem_num, gem.controllers[gem_num], gem_state->ext);
@@ -1269,7 +1451,6 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
if (gem.controllers[gem_num].enabled_tracking)
tracking_flags |= CELL_GEM_TRACKING_FLAG_POSITION_TRACKED;
*gem_state = {};
gem_state->tracking_flags = tracking_flags;
gem_state->timestamp = (get_guest_system_time() - gem.start_timestamp);
gem_state->camera_pitch_angle = 0.f;
@@ -1278,6 +1459,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
if (g_cfg.io.move == move_handler::fake)
{
ds3_input_to_pad(gem_num, gem_state->pad.digitalbuttons, gem_state->pad.analog_T);
ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state);
}
else if (g_cfg.io.move == move_handler::mouse)
{
@@ -1457,14 +1639,6 @@ error_code cellGemInit(ppu_thread& ppu, vm::cptr<CellGemAttribute> attribute)
gem.status_flags = 0;
gem.attribute = *attribute;
if (g_cfg.io.move == move_handler::mouse)
{
// init mouse handler
auto& handler = g_fxo->get<MouseHandlerBase>();
handler.Init(std::min<u32>(attribute->max_connect, CELL_GEM_MAX_NUM));
}
for (int gem_num = 0; gem_num < CELL_GEM_MAX_NUM; gem_num++)
{
gem.reset_controller(gem_num);
@@ -1494,16 +1668,13 @@ error_code cellGemInvalidateCalibration(s32 gem_num)
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
{
gem.controllers[gem_num].calibrated_magnetometer = false;
gem.controllers[gem_num].calibrated_magnetometer = false;
// TODO: does this really stop an ongoing calibration ?
gem.controllers[gem_num].is_calibrating = false;
gem.controllers[gem_num].calibration_start_us = 0;
// TODO: does this really stop an ongoing calibration ?
gem.controllers[gem_num].is_calibrating = false;
gem.controllers[gem_num].calibration_start_us = 0;
// TODO: gem.status_flags (probably not changed)
}
// TODO: gem.status_flags (probably not changed)
return CELL_OK;
}
+6
View File
@@ -171,6 +171,8 @@ struct CellGemAttribute
be_t<u32> memory_ptr;
be_t<u32> spurs_addr;
u8 spu_priorities[8];
ENABLE_BITWISE_SERIALIZATION;
};
struct CellGemCameraState
@@ -180,6 +182,8 @@ struct CellGemCameraState
be_t<f32> gain;
be_t<f32> pitch_angle;
be_t<f32> pitch_angle_estimate;
ENABLE_BITWISE_SERIALIZATION;
};
struct CellGemExtPortData
@@ -270,4 +274,6 @@ struct CellGemVideoConvertAttribute
vm::bptr<u8> buffer_memory;
vm::bptr<u8> video_data_out;
u8 alpha;
ENABLE_BITWISE_SERIALIZATION;
};
+383 -45
View File
@@ -39,61 +39,134 @@ void fmt_class_string<CellGifDecError>::format(std::string& out, u64 arg)
});
}
// cellGifDec aliases (only for cellGifDec.cpp)
using PPMainHandle = vm::pptr<GifDecoder>;
using PMainHandle = vm::ptr<GifDecoder>;
using PThreadInParam = vm::cptr<CellGifDecThreadInParam>;
using PThreadOutParam = vm::ptr<CellGifDecThreadOutParam>;
using PExtThreadInParam = vm::cptr<CellGifDecExtThreadInParam>;
using PExtThreadOutParam = vm::ptr<CellGifDecExtThreadOutParam>;
using PPSubHandle = vm::pptr<GifStream>;
using PSubHandle = vm::ptr<GifStream>;
using PSrc = vm::cptr<CellGifDecSrc>;
using POpenInfo = vm::ptr<CellGifDecOpnInfo>;
using PInfo = vm::ptr<CellGifDecInfo>;
using PInParam = vm::cptr<CellGifDecInParam>;
using POutParam = vm::ptr<CellGifDecOutParam>;
using PDataCtrlParam = vm::cptr<CellGifDecDataCtrlParam>;
using PDataOutInfo = vm::ptr<CellGifDecDataOutInfo>;
error_code cellGifDecCreate(PPMainHandle mainHandle, PThreadInParam threadInParam, PThreadOutParam threadOutParam)
error_code cellGifDecCreate(vm::ptr<GifDecoder> mainHandle, vm::cptr<CellGifDecThreadInParam> threadInParam, vm::ptr<CellGifDecThreadOutParam> threadOutParam)
{
UNIMPLEMENTED_FUNC(cellGifDec);
cellGifDec.todo("cellGifDecCreate(mainHandle=*0x%x, threadInParam=*0x%x, threadOutParam=*0x%x)", mainHandle, threadInParam, threadOutParam);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
*mainHandle = {};
if (!threadOutParam || !threadInParam || !threadInParam->cbCtrlMallocFunc || !threadInParam->cbCtrlFreeFunc ||
(threadInParam->spuThreadEnable != CELL_GIFDEC_SPU_THREAD_DISABLE &&
(threadInParam->spuThreadEnable != CELL_GIFDEC_SPU_THREAD_ENABLE ||
threadInParam->ppuThreadPriority > 3071 ||
threadInParam->spuThreadPriority > 255)))
{
return CELL_GIFDEC_ERROR_ARG;
}
threadOutParam->gifCodecVersion = 0x240000;
return CELL_OK;
}
error_code cellGifDecExtCreate(PPMainHandle mainHandle, PThreadInParam threadInParam, PThreadOutParam threadOutParam, PExtThreadInParam extThreadInParam, PExtThreadOutParam extThreadOutParam)
error_code cellGifDecExtCreate(vm::ptr<GifDecoder> mainHandle, vm::cptr<CellGifDecThreadInParam> threadInParam, vm::ptr<CellGifDecThreadOutParam> threadOutParam, vm::cptr<CellGifDecExtThreadInParam> extThreadInParam, vm::ptr<CellGifDecExtThreadOutParam> extThreadOutParam)
{
UNIMPLEMENTED_FUNC(cellGifDec);
cellGifDec.todo("cellGifDecExtCreate(mainHandle=*0x%x, threadInParam=*0x%x, threadOutParam=*0x%x, extThreadInParam=*0x%x, extThreadOutParam=*0x%x)", mainHandle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
*mainHandle = {};
if (!threadOutParam || !extThreadOutParam || !extThreadInParam || !threadInParam || !threadInParam->cbCtrlMallocFunc || !threadInParam->cbCtrlFreeFunc ||
(threadInParam->spuThreadEnable != CELL_GIFDEC_SPU_THREAD_DISABLE && threadInParam->spuThreadEnable != CELL_GIFDEC_SPU_THREAD_ENABLE))
{
return CELL_GIFDEC_ERROR_ARG;
}
if (threadInParam->spuThreadEnable == CELL_GIFDEC_SPU_THREAD_ENABLE && !extThreadInParam->spurs)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (extThreadInParam->maxContention == 0u || extThreadInParam->maxContention >= 8u)
{
return CELL_GIFDEC_ERROR_ARG;
}
for (u32 i = 0; i < 8; i++)
{
if (extThreadInParam->priority[i] > 15)
{
return CELL_GIFDEC_ERROR_ARG;
}
}
threadOutParam->gifCodecVersion = 0x240000;
return CELL_OK;
}
error_code cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc src, POpenInfo openInfo)
error_code cellGifDecOpen(vm::ptr<GifDecoder> mainHandle, vm::pptr<GifStream> subHandle, vm::cptr<CellGifDecSrc> src, vm::ptr<CellGifDecOpnInfo> openInfo)
{
cellGifDec.warning("cellGifDecOpen(mainHandle=*0x%x, subHandle=**0x%x, src=*0x%x, openInfo=*0x%x)", mainHandle, subHandle, src, openInfo);
GifStream current_subHandle;
if (!mainHandle || !subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!openInfo || !src)
{
return CELL_GIFDEC_ERROR_ARG;
}
GifStream current_subHandle{};
current_subHandle.fd = 0;
current_subHandle.src = *src;
switch (src->srcSelect)
{
case CELL_GIFDEC_BUFFER:
{
if (!src->streamPtr || !src->streamSize)
{
return CELL_GIFDEC_ERROR_ARG;
}
current_subHandle.fileSize = src->streamSize;
break;
}
case CELL_GIFDEC_FILE:
{
if (!src->fileName)
{
return CELL_GIFDEC_ERROR_OPEN_FILE;
}
// Get file descriptor and size
const auto real_path = vfs::get(src->fileName.get_ptr());
const std::string real_path = vfs::get(src->fileName.get_ptr());
fs::file file_s(real_path);
if (!file_s) return CELL_GIFDEC_ERROR_OPEN_FILE;
if (!file_s)
{
return CELL_GIFDEC_ERROR_OPEN_FILE;
}
if (src->fileOffset < 0)
{
return CELL_GIFDEC_ERROR_ARG;
}
current_subHandle.fileSize = file_s.size();
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0, real_path);
break;
}
default: break; // TODO
default:
{
return CELL_GIFDEC_ERROR_ARG;
}
}
subHandle->set(vm::alloc(sizeof(GifStream), vm::main));
@@ -103,16 +176,103 @@ error_code cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc sr
return CELL_OK;
}
error_code cellGifDecExtOpen()
error_code cellGifDecExtOpen(vm::ptr<GifDecoder> mainHandle, vm::pptr<GifStream> subHandle, vm::cptr<CellGifDecSrc> src, vm::ptr<CellGifDecOpnInfo> openInfo, vm::cptr<CellGifDecCbCtrlStrm> cbCtrlStrm)
{
cellGifDec.todo("cellGifDecExtOpen()");
cellGifDec.todo("cellGifDecExtOpen(mainHandle=*0x%x, subHandle=*0x%x, src=*0x%x, openInfo=*0x%x, cbCtrlStrm=*0x%x)", mainHandle, subHandle, src, openInfo, cbCtrlStrm);
if (!mainHandle || !subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!openInfo || !src)
{
return CELL_GIFDEC_ERROR_ARG;
}
GifStream current_subHandle{};
current_subHandle.fd = 0;
current_subHandle.src = *src;
switch (src->srcSelect)
{
case CELL_GIFDEC_BUFFER:
{
if (!src->streamPtr || !src->streamSize)
{
return CELL_GIFDEC_ERROR_ARG;
}
current_subHandle.fileSize = src->streamSize;
break;
}
case CELL_GIFDEC_FILE:
{
if (!src->fileName)
{
return CELL_GIFDEC_ERROR_OPEN_FILE;
}
// Get file descriptor and size
const std::string real_path = vfs::get(src->fileName.get_ptr());
fs::file file_s(real_path);
if (!file_s)
{
return CELL_GIFDEC_ERROR_OPEN_FILE;
}
if (src->fileOffset < 0)
{
return CELL_GIFDEC_ERROR_ARG;
}
current_subHandle.fileSize = file_s.size();
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0, real_path);
break;
}
default:
{
return CELL_GIFDEC_ERROR_ARG;
}
}
return CELL_OK;
}
error_code cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PInfo info)
error_code cellGifDecReadHeader(vm::ptr<GifDecoder> mainHandle, vm::ptr<GifStream> subHandle, vm::ptr<CellGifDecInfo> info)
{
cellGifDec.warning("cellGifDecReadHeader(mainHandle=*0x%x, subHandle=*0x%x, info=*0x%x)", mainHandle, subHandle, info);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check main handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check sub handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!info)
{
return CELL_GIFDEC_ERROR_ARG;
}
const u32& fd = subHandle->fd;
CellGifDecInfo& current_info = subHandle->info;
@@ -122,9 +282,10 @@ error_code cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PI
switch (subHandle->src.srcSelect)
{
case CELL_GIFDEC_BUFFER:
{
std::memcpy(buffer, subHandle->src.streamPtr.get_ptr(), sizeof(buffer));
break;
}
case CELL_GIFDEC_FILE:
{
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
@@ -156,20 +317,71 @@ error_code cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PI
return CELL_OK;
}
error_code cellGifDecExtReadHeader()
error_code cellGifDecExtReadHeader(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStream> subHandle, vm::ptr<CellGifDecInfo> info, vm::ptr<CellGifDecExtInfo> extInfo)
{
cellGifDec.todo("cellGifDecExtReadHeader()");
cellGifDec.todo("cellGifDecExtReadHeader(mainHandle=*0x%x, subHandle=*0x%x, info=*0x%x, extInfo=*0x%x)", mainHandle, subHandle, info, extInfo);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check main handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check sub handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!info || !extInfo)
{
return CELL_GIFDEC_ERROR_ARG;
}
return CELL_OK;
}
error_code cellGifDecSetParameter(PMainHandle mainHandle, PSubHandle subHandle, PInParam inParam, POutParam outParam)
error_code cellGifDecSetParameter(vm::ptr<GifDecoder> mainHandle, vm::ptr<GifStream> subHandle, vm::cptr<CellGifDecInParam> inParam, vm::ptr<CellGifDecOutParam> outParam)
{
cellGifDec.warning("cellGifDecSetParameter(mainHandle=*0x%x, subHandle=*0x%x, inParam=*0x%x, outParam=*0x%x)", mainHandle, subHandle, inParam, outParam);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check main handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check sub handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!inParam || !outParam)
{
return CELL_GIFDEC_ERROR_ARG;
}
CellGifDecInfo& current_info = subHandle->info;
CellGifDecOutParam& current_outParam = subHandle->outParam;
current_outParam.outputWidthByte = (current_info.SWidth * current_info.SColorResolution * 3)/8;
current_outParam.outputWidthByte = (current_info.SWidth * current_info.SColorResolution * 3) / 8;
current_outParam.outputWidth = current_info.SWidth;
current_outParam.outputHeight = current_info.SHeight;
current_outParam.outputColorSpace = inParam->colorSpace;
@@ -179,24 +391,98 @@ error_code cellGifDecSetParameter(PMainHandle mainHandle, PSubHandle subHandle,
case CELL_GIFDEC_ARGB: current_outParam.outputComponents = 4; break;
default: return CELL_GIFDEC_ERROR_ARG; // Not supported color space
}
current_outParam.outputBitDepth = 0; // Unimplemented
current_outParam.useMemorySpace = 0; // Unimplemented
current_outParam.outputBitDepth = 0; // Unimplemented
current_outParam.useMemorySpace = 0; // Unimplemented
*outParam = current_outParam;
return CELL_OK;
}
error_code cellGifDecExtSetParameter()
error_code cellGifDecExtSetParameter(vm::ptr<GifDecoder> mainHandle, vm::ptr<GifStream> subHandle, vm::cptr<CellGifDecInParam> inParam, vm::ptr<CellGifDecOutParam> outParam, vm::cptr<CellGifDecExtInParam> extInParam, vm::ptr<CellGifDecExtOutParam> extOutParam)
{
cellGifDec.todo("cellGifDecExtSetParameter()");
cellGifDec.todo("cellGifDecExtSetParameter(mainHandle=*0x%x, subHandle=*0x%x, inParam=*0x%x, outParam=*0x%x, extInParam=*0x%x, extOutParam=*0x%x)", mainHandle, subHandle, inParam, outParam, extInParam, extOutParam);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check main handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check sub handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!inParam || !outParam)
{
return CELL_GIFDEC_ERROR_ARG;
}
CellGifDecInfo& current_info = subHandle->info;
CellGifDecOutParam& current_outParam = subHandle->outParam;
current_outParam.outputWidthByte = (current_info.SWidth * current_info.SColorResolution * 3) / 8;
current_outParam.outputWidth = current_info.SWidth;
current_outParam.outputHeight = current_info.SHeight;
current_outParam.outputColorSpace = inParam->colorSpace;
switch (current_outParam.outputColorSpace)
{
case CELL_GIFDEC_RGBA:
case CELL_GIFDEC_ARGB: current_outParam.outputComponents = 4; break;
default: return CELL_GIFDEC_ERROR_ARG; // Not supported color space
}
current_outParam.outputBitDepth = 0; // Unimplemented
current_outParam.useMemorySpace = 0; // Unimplemented
*outParam = current_outParam;
if (!extInParam || extInParam->bufferMode != CELL_GIFDEC_LINE_MODE || !extOutParam)
{
return CELL_GIFDEC_ERROR_ARG;
}
return CELL_OK;
}
error_code cellGifDecDecodeData(PMainHandle mainHandle, PSubHandle subHandle, vm::ptr<u8> data, PDataCtrlParam dataCtrlParam, PDataOutInfo dataOutInfo)
error_code cellGifDecDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStream> subHandle, vm::ptr<u8> data, vm::cptr<CellGifDecDataCtrlParam> dataCtrlParam, vm::ptr<CellGifDecDataOutInfo> dataOutInfo)
{
cellGifDec.warning("cellGifDecDecodeData(mainHandle=*0x%x, subHandle=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x)", mainHandle, subHandle, data, dataCtrlParam, dataOutInfo);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check main handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check sub handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!dataOutInfo || !dataCtrlParam)
{
return CELL_GIFDEC_ERROR_ARG;
}
dataOutInfo->status = CELL_GIFDEC_DEC_STATUS_STOP;
const u32 fd = subHandle->fd;
@@ -306,16 +592,57 @@ error_code cellGifDecDecodeData(PMainHandle mainHandle, PSubHandle subHandle, vm
return CELL_OK;
}
error_code cellGifDecExtDecodeData()
error_code cellGifDecExtDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStream> subHandle, vm::ptr<u8> data, vm::cptr<CellGifDecDataCtrlParam> dataCtrlParam, vm::ptr<CellGifDecDataOutInfo> dataOutInfo, vm::cptr<CellGifDecCbCtrlDisp> cbCtrlDisp, vm::ptr<CellGifDecDispParam> dispParam)
{
cellGifDec.todo("cellGifDecExtDecodeData()");
cellGifDec.todo("cellGifDecExtDecodeData(mainHandle=*0x%x, subHandle=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x, cbCtrlDisp=*0x%x, dispParam=*0x%x)", mainHandle, subHandle, data, dataCtrlParam, dataOutInfo, cbCtrlDisp, dispParam);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check main handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check sub handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!dataOutInfo || !dataCtrlParam)
{
return CELL_GIFDEC_ERROR_ARG;
}
return CELL_OK;
}
error_code cellGifDecClose(PMainHandle mainHandle, PSubHandle subHandle)
error_code cellGifDecClose(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStream> subHandle)
{
cellGifDec.warning("cellGifDecClose(mainHandle=*0x%x, subHandle=*0x%x)", mainHandle, subHandle);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check main handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
if (!subHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
idm::remove<lv2_fs_object, lv2_file>(subHandle->fd);
vm::dealloc(subHandle.addr());
@@ -323,9 +650,20 @@ error_code cellGifDecClose(PMainHandle mainHandle, PSubHandle subHandle)
return CELL_OK;
}
error_code cellGifDecDestroy(PMainHandle mainHandle)
error_code cellGifDecDestroy(vm::ptr<GifDecoder> mainHandle)
{
UNIMPLEMENTED_FUNC(cellGifDec);
cellGifDec.todo("cellGifDecDestroy(mainHandle=*0x%x)", mainHandle);
if (!mainHandle)
{
return CELL_GIFDEC_ERROR_ARG;
}
if (false) // TODO: check main handle
{
return CELL_GIFDEC_ERROR_SEQ;
}
return CELL_OK;
}
+87 -5
View File
@@ -52,6 +52,23 @@ enum CellGifDecDecodeStatus : s32
CELL_GIFDEC_DEC_STATUS_STOP = 1, // Decoding was stopped
};
enum CellGifDecBufferMode : s32
{
CELL_GIFDEC_LINE_MODE = 1
};
enum CellGifDecSpuMode : s32
{
CELL_GIFDEC_RECEIVE_EVENT = 0,
CELL_GIFDEC_TRYRECEIVE_EVENT = 1
};
enum CellGifDecInterlaceMode : s32
{
CELL_GIFDEC_NO_INTERLACE = 0,
CELL_GIFDEC_INTERLACE = 1
};
// Callbacks for memory management
using CellGifDecCbControlMalloc = vm::ptr<void>(u32 size, vm::ptr<void> cbCtrlMallocArg);
using CellGifDecCbControlFree = s32(vm::ptr<void> ptr, vm::ptr<void> cbCtrlFreeArg);
@@ -151,6 +168,71 @@ struct CellGifDecDataCtrlParam
be_t<u64> outputBytesPerLine;
};
struct CellGifDecExtInfo
{
vm::bptr<u64> reserved;
};
struct CellGifDecStrmInfo
{
be_t<u32> decodedStrmSize;
};
struct CellGifDecStrmParam
{
vm::bptr<void> strmPtr;
be_t<u32> strmSize;
};
using CellGifDecCbControlStream = s32(vm::ptr<CellGifDecStrmInfo> strmInfo, vm::ptr<CellGifDecStrmParam> strmParam, vm::ptr<void> cbCtrlStrmArg);
struct CellGifDecCbCtrlStrm
{
vm::bptr<CellGifDecCbControlStream> cbCtrlStrmFunc;
vm::bptr<void> cbCtrlStrmArg;
};
struct CellGifDecExtInParam
{
be_t<s32> bufferMode; // CellGifDecBufferMode
be_t<u32> outputCounts;
be_t<s32> spuMode; // CellGifDecSpuMode
};
struct CellGifDecExtOutParam
{
be_t<u64> outputWidthByte;
be_t<u32> outputHeight;
};
struct CellGifDecDispParam
{
vm::bptr<void> nextOutputImage;
};
struct CellGifDecDispInfo
{
be_t<u64> outputFrameWidthByte;
be_t<u32> outputFrameHeight;
be_t<u64> outputStartXByte;
be_t<u32> outputStartY;
be_t<u64> outputWidthByte;
be_t<u32> outputHeight;
be_t<u32> outputBitDepth;
be_t<u32> outputComponents;
be_t<u32> scanPassCount;
vm::bptr<void> outputImage;
be_t<s32> interlaceFlag; // CellGifDecInterlaceMode
};
using CellGifDecCbControlDisp = s32(vm::ptr<CellGifDecDispInfo> dispInfo, vm::ptr<CellGifDecDispParam> dispParam, vm::ptr<void> cbCtrlDispArg);
struct CellGifDecCbCtrlDisp
{
vm::bptr<CellGifDecCbControlDisp> cbCtrlDispFunc;
vm::bptr<void> cbCtrlDispArg;
};
// Custom structs
struct GifDecoder
{
@@ -158,9 +240,9 @@ struct GifDecoder
struct GifStream
{
u32 fd;
u64 fileSize;
CellGifDecInfo info;
CellGifDecOutParam outParam;
CellGifDecSrc src;
u32 fd{};
u64 fileSize{};
CellGifDecInfo info{};
CellGifDecOutParam outParam{};
CellGifDecSrc src{};
};
+3 -3
View File
@@ -727,7 +727,7 @@ error_code cellHttpUtilFormUrlDecode(vm::ptr<u8> out, u32 size, vm::cptr<char> i
return CELL_HTTP_UTIL_ERROR_INVALID_URI;
}
const auto FUN_00036710 = [](b8 c)
const auto check_char = [](b8 c)
{
u32 utmp = static_cast<u32>(c);
s32 stmp = utmp - 48;
@@ -746,8 +746,8 @@ error_code cellHttpUtilFormUrlDecode(vm::ptr<u8> out, u32 size, vm::cptr<char> i
return stmp;
};
const s32 tmp1 = FUN_00036710(c2);
const s32 tmp2 = FUN_00036710(c3);
const s32 tmp1 = check_char(c2);
const s32 tmp2 = check_char(c3);
if (tmp1 < 0 || tmp2 < 0)
{
+1
View File
@@ -110,6 +110,7 @@ struct CellJpgDecSubHandle
static const u32 id_base = 1;
static const u32 id_step = 1;
static const u32 id_count = 1023;
SAVESTATE_INIT_POS(35);
u32 fd;
u64 fileSize;
+31 -1
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Emu/IdManager.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Io/KeyboardHandler.h"
@@ -8,6 +9,8 @@
extern void libio_sys_config_init();
extern void libio_sys_config_end();
extern bool is_input_allowed();
LOG_CHANNEL(sys_io);
template<>
@@ -31,6 +34,33 @@ void fmt_class_string<CellKbError>::format(std::string& out, u64 arg)
});
}
KeyboardHandlerBase::KeyboardHandlerBase(utils::serial* ar)
{
if (!ar)
{
return;
}
(*ar)(m_info.max_connect);
if (m_info.max_connect)
{
Emu.DeferDeserialization([this]()
{
Init(m_info.max_connect);
init.init();
});
}
}
void KeyboardHandlerBase::save(utils::serial& ar)
{
const auto inited = init.access();
ar(inited ? m_info.max_connect : 0);
}
error_code cellKbInit(u32 max_connect)
{
sys_io.warning("cellKbInit(max_connect=%d)", max_connect);
@@ -305,7 +335,7 @@ error_code cellKbRead(u32 port_no, vm::ptr<CellKbData> data)
KbData& current_data = handler.GetData(port_no);
if (current_info.is_null_handler || (current_info.info & CELL_KB_INFO_INTERCEPTED))
if (current_info.is_null_handler || (current_info.info & CELL_KB_INFO_INTERCEPTED) || !is_input_allowed())
{
data->led = 0;
data->mkey = 0;
+20 -5
View File
@@ -92,9 +92,15 @@ void mic_context::operator()()
m_counter++;
// Process signals
const auto process_signals = [this]() -> bool
{
std::lock_guard lock(mutex);
if (mic_list.empty())
{
return false;
}
for (auto& mic_entry : mic_list)
{
auto& mic = mic_entry.second;
@@ -103,16 +109,25 @@ void mic_context::operator()()
auto mic_queue = lv2_event_queue::find(event_queue_key);
if (!mic_queue)
continue;
for (auto& mic_entry : mic_list)
{
auto& mic = mic_entry.second;
return true;
}
for (const auto& [dev_num, mic] : mic_list)
{
if (mic.has_data())
{
mic_queue->send(0, CELLMIC_DATA, mic_entry.first, 0);
mic_queue->send(0, CELLMIC_DATA, dev_num, 0);
}
}
return true;
};
// Get mic input and sleep if mics are idle
if (!process_signals())
{
thread_ctrl::wait_for(100000);
}
}

Some files were not shown because too many files have changed in this diff Show More