Compare commits

..

123 Commits

Author SHA1 Message Date
Fernando Sahmkow
f783883bf8 video_core: implement formats for N64 emulation 2022-04-14 11:06:27 -04:00
Liam
d4571b123d buffer_cache: cap vertex buffer sizes 2022-04-14 10:46:48 -04:00
Liam
a24e7ba61b maxwell3d: add small_index_2 register 2022-04-14 09:57:06 -04:00
bunnei
8ae43a1be9 Merge pull request #8190 from Docteh/palswap
ui: Set Link Color when setting theme
2022-04-13 22:15:31 -07:00
bunnei
46da380b57 Merge pull request #8027 from lat9nq/cmd-fullscreen-size
emu_window_sdl2: Set window size to display dimensions for exclusive fullscreen
2022-04-13 18:43:47 -07:00
Fernando S
072b3e6e26 Merge pull request #8202 from merryhime/fix-single-core
dynarmic: Fix single core mode
2022-04-13 23:21:11 +02:00
Fernando S
276764cfe9 Merge pull request #8199 from liamwhite/jit-service
service: jit: Implement the JIT service
2022-04-13 23:20:34 +02:00
merry
ba08f63ec4 dynarmic: Fix single core mode
Regression introduced in a5d040df3d. Closes #8201.
2022-04-13 20:40:12 +01:00
Liam
f17aaeccf9 service: jit: Implement the JIT service 2022-04-13 08:41:27 -04:00
bunnei
ca2accfb25 Merge pull request #8165 from bunnei/ensure-session-port-cleanup
Kernel: Track open references to KServerPort and KServerSession.
2022-04-12 14:01:40 -07:00
Kyle K
f6695814be ui: Touching QPalette::Text broke dark -> light UI. don't do 2022-04-12 12:50:56 -07:00
bunnei
dc2dd5d5a6 Merge pull request #8178 from tech-ticks/skyline-icache-fix
hle: kernel: Invalidate entire icache in UnmapProcessMemory and UnmapCodeMemory (fixes #8174)
2022-04-12 11:23:20 -07:00
bunnei
fd5e1e80da Merge pull request #8157 from lat9nq/kernel-races
kernel: Fix some data races
2022-04-11 21:13:01 -07:00
bunnei
fc258f1d7a Merge pull request #8196 from jbeich/freebsd
service: sfdnsres: unbreak build on FreeBSD
2022-04-11 20:58:35 -07:00
Jan Beich
d13e48e002 service: sfdnsres: add missing includes for some BSDs after 82d46a974a
src/core/hle/service/sockets/sfdnsres.cpp: In function 'Service::Sockets::NetDbError Service::Sockets::AddrInfoErrorToNetDbError(s32)':
src/core/hle/service/sockets/sfdnsres.cpp:66:10: error: 'EAI_NODATA' was not declared in this scope; did you mean 'EAI_NONAME'?
   66 |     case EAI_NODATA:
      |          ^~~~~~~~~~
      |          EAI_NONAME
src/core/hle/service/sockets/sfdnsres.cpp: In function 'std::vector<unsigned char> Service::Sockets::SerializeAddrInfo(const addrinfo*, s32, std::string_view)':
src/core/hle/service/sockets/sfdnsres.cpp:127:53: error: 'sockaddr_in' does not name a type; did you mean 'SockAddrIn'?
  127 |                 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
      |                                                     ^~~~~~~~~~~
      |                                                     SockAddrIn
src/core/hle/service/sockets/sfdnsres.cpp:127:64: error: expected '>' before '*' token
  127 |                 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
      |                                                                ^
src/core/hle/service/sockets/sfdnsres.cpp:127:64: error: expected '(' before '*' token
  127 |                 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
      |                                                                ^
      |                                                                (
src/core/hle/service/sockets/sfdnsres.cpp:127:65: error: expected primary-expression before '>' token
  127 |                 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
      |                                                                 ^
src/core/hle/service/sockets/sfdnsres.cpp:127:84: error: expected ')' before ';' token
  127 |                 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
      |                                                                                    ^
      |                                                                                    )
src/core/hle/service/sockets/sfdnsres.cpp:148:53: error: 'sockaddr_in6' does not name a type; did you mean 'SockAddrIn6'?
  148 |                 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
      |                                                     ^~~~~~~~~~~~
      |                                                     SockAddrIn6
src/core/hle/service/sockets/sfdnsres.cpp:148:65: error: expected '>' before '*' token
  148 |                 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
      |                                                                 ^
src/core/hle/service/sockets/sfdnsres.cpp:148:65: error: expected '(' before '*' token
  148 |                 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
      |                                                                 ^
      |                                                                 (
src/core/hle/service/sockets/sfdnsres.cpp:148:66: error: expected primary-expression before '>' token
  148 |                 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
      |                                                                  ^
src/core/hle/service/sockets/sfdnsres.cpp:148:85: error: expected ')' before ';' token
  148 |                 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
      |                                                                                     ^
      |                                                                                     )
2022-04-11 22:26:37 +00:00
Fernando S
b86cfe159f Merge pull request #8180 from liamwhite/symbols
core: extract symbol reading
2022-04-11 18:40:34 +02:00
Kyle K
38dd6dc190 ui: Set Link Color when setting theme
Long story short, QT doesn't allow the link colors to be set via their stylesheets.

There are two ways to work with this, specify the color manually for every link (See the About dialog) The other way is to change the default palette.

IsDarkTheme is copy/pasted from src/yuzu/debugger/wait_tree.cpp
2022-04-11 00:00:29 -07:00
Fernando S
4ad6bca31c Merge pull request #8171 from tech-ticks/skyline-improvements
Improvements for game modding with Skyline, DNS resolution
2022-04-10 23:40:54 +02:00
tech-ticks
3b91d213b1 hle: kernel: Invalidate entire icache in UnmapProcessMemory and UnmapCodeMemory (fixes #8174) 2022-04-09 13:29:19 +02:00
bunnei
bf3c6f8812 Merge pull request #8149 from liamwhite/front-face
OpenGL: flip front faces if Z scale is inverted
2022-04-09 01:39:39 -07:00
Liam
b29242862b core: extract symbol reading 2022-04-09 02:16:34 -04:00
bunnei
32e2fb5d33 Merge pull request #8138 from german77/data-no-race
core: hid: Reduce the amount of data races
2022-04-08 14:14:53 -07:00
bunnei
a7f73d606f hle: kernel: Unify and integrate reference tracking for KServerPort/KServerSession.
- These are not managed elsewhere, and need to be tracked and closed on emulation shutdown.
2022-04-08 14:13:22 -07:00
bunnei
b44a564792 hle: kernel: k_server_port: Release ref-counted host emulation members on Destroy. 2022-04-08 14:11:40 -07:00
bunnei
788bebb160 hle: kernel: k_auto_object: Move unregister with kernel to after Destroy.
- Destructor is no longer invoked, so our object counting was off.
2022-04-08 14:11:40 -07:00
bunnei
d737652d08 hle: service: sm: Remove manual tracking of KServerPorts. 2022-04-08 14:11:39 -07:00
bunnei
f55fc850a2 hle: kernel: hle_ipc: HasSessionRequestHandler: Check if domain handler is expired rather than locking. 2022-04-08 14:11:39 -07:00
bunnei
04efd729d6 Merge pull request #8169 from merryhime/scoped_lock
Replace lock_guard with scoped_lock
2022-04-08 14:01:42 -07:00
tech-ticks
82d46a974a service: sfdnsres: Implement DNS address resolution 2022-04-08 21:28:03 +02:00
Mai M
21359936b8 Merge pull request #8173 from Morph1984/msvc-warn-unused-fn
CMakeLists: Enforce C4505 and C5245
2022-04-08 00:19:00 -04:00
Morph
be95b5a954 CMakeLists: Enforce C4505 and C5245
These are similar to Wunused-function on gcc/clang
2022-04-07 23:00:04 -04:00
Narr the Reg
bbaa08d7f0 core: hid: Fix double lock on softlock and forced updates 2022-04-07 17:08:01 -05:00
merry
50b10c4bac Merge pull request #8167 from Tachi107/patch-1
fix: remove #pragma once in .cpp file
2022-04-07 22:47:43 +01:00
tech-ticks
1c3983c12e service: bsd: Add keepalive socket option 2022-04-07 23:30:23 +02:00
tech-ticks
f05e87402a patch_manager: Apply layered exefs patches from 'atmosphere' SD directory 2022-04-07 23:02:44 +02:00
Narr the Reg
9c85cb354a core: hid: Replace lock_guard with scoped_lock 2022-04-07 13:52:51 -05:00
Merry
d79274a5d9 core/hle: Standardize scoped_lock initializers 2022-04-07 19:44:07 +01:00
Merry
4778656110 yuzu/util: Replace lock_guard with scoped_lock 2022-04-07 19:44:07 +01:00
Merry
3cf6593342 web_service: Replace lock_guard with scoped_lock 2022-04-07 19:44:07 +01:00
Merry
bbc585881a video_core: Replace lock_guard with scoped_lock 2022-04-07 19:44:07 +01:00
Merry
159ae5e47c input_common: Replace lock_guard with scoped_lock 2022-04-07 19:44:07 +01:00
Merry
6a071c42d2 core: Replace lock_guard with scoped_lock 2022-04-07 19:44:07 +01:00
Merry
1f275eb077 core/hle: Replace lock_guard with scoped_lock 2022-04-07 19:44:07 +01:00
Merry
c589db6add common: Replace lock_guard with scoped_lock 2022-04-07 19:30:55 +01:00
german77
fa5277ecdb core: hid: Reduce the amount of dataraces 2022-04-07 13:18:03 -05:00
Fernando S
4265372099 Merge pull request #8161 from liamwhite/gl-s8d24
OpenGL: fix S8D24 to ABGR8 conversions
2022-04-07 16:59:41 +02:00
Fernando S
03d5794183 Merge pull request #8152 from liamwhite/gl-crop
OpenGL: fix cropping
2022-04-07 16:58:59 +02:00
Fernando S
827a901153 Merge pull request #8150 from liamwhite/vk-crop
Vulkan: crop to screen dimensions if crop not explicitly requested
2022-04-07 16:58:29 +02:00
Fernando S
50192eb4ad Merge pull request #8148 from merryhime/interrupts
dynarmic: Better interrupts
2022-04-07 16:21:41 +02:00
Fernando S
a02fd4cddd Merge pull request #8143 from merryhime/rdtsc
native_clock: Use lfence with rdtsc
2022-04-07 16:17:45 +02:00
Fernando S
4d5900aaa1 Merge pull request #8133 from liamwhite/gl-spv-cbuf
shader_recompiler: support const buffer indirect addressing on OpenGL
2022-04-07 12:40:59 +02:00
Andrea Pappacoda
5ca67332ee fix: remove #pragma once in .cpp file 2022-04-07 12:03:47 +02:00
bunnei
172137f1a0 Merge pull request #8164 from liamwhite/jit-stub
service: jit: stub JIT service
2022-04-06 18:34:45 -07:00
Liam
0cfcee95c7 service: jit: stub JIT service 2022-04-06 20:07:01 -04:00
Liam
52ebdd42c6 OpenGL: fix S8D24 to ABGR8 conversions 2022-04-06 19:44:33 -04:00
bunnei
eb8c8db899 Merge pull request #8122 from bunnei/improve-thread-usage
Improve usage of service host threads
2022-04-06 12:25:25 -07:00
bunnei
37199c5f90 Merge pull request #8162 from german77/bombslinger
service: hid: Partially revert #8123
2022-04-05 21:15:38 -07:00
german77
8c089f4e2a service: hid: Partially revert #8123 2022-04-05 22:35:38 -05:00
bunnei
12dc4d0527 Merge pull request #8137 from bunnei/improve-nvflinger-2
Follow-up fixes for NVFlinger rewrite (Part 2)
2022-04-05 19:11:28 -07:00
bunnei
0c1b954e07 Merge pull request #8100 from Morph1984/registered-crash
registered_cache: Prevent nullptr dereference when accumulating files
2022-04-05 18:18:41 -07:00
lat9nq
b976cac49d k_system_control: Fix data race
`return distribution(gen)` is a data race between a read and a write in
two threads, reported by TSan. Remove static random number generators so
they aren't using the same generator.
2022-04-05 19:55:56 -04:00
Mai M
e4c3565ebe Merge pull request #8159 from merryhime/pst
dynarmic: Print stack trace on unrecognised instruction or other exception
2022-04-05 17:18:31 -04:00
Mai M
9af501b75d Merge pull request #8158 from Tachi107/patch-1
build: remove -fconcepts
2022-04-05 17:17:22 -04:00
merry
7f11710e0a dynarmic: Print stack trace on unrecognised instruction or other exception 2022-04-05 20:40:20 +01:00
Andrea Pappacoda
ce859cf526 build: remove -fconcepts
It was needed on GCC versions not supporting `-std=c++20`, but GCC 10 and newer (required to compile yuzu) don't need it anymore
2022-04-05 20:52:11 +02:00
bunnei
119d1692c9 Merge pull request #8156 from yuzu-emu/revert-8154-unneeded-read-block
Revert "texture_cache/util: Remove unneeded ReadBlockUnsafe"
2022-04-04 16:27:13 -07:00
bunnei
02473ea7d5 Revert "texture_cache/util: Remove unneeded ReadBlockUnsafe" 2022-04-04 16:26:53 -07:00
bunnei
c7f3c2cedf Merge pull request #8154 from ameerj/unneeded-read-block
texture_cache/util: Remove unneeded ReadBlockUnsafe
2022-04-04 13:46:44 -07:00
Liam
1ab771c3ad shader_recompiler: Decrease indirect cbuf limit to match hardware 2022-04-04 16:44:01 -04:00
ameerj
494c41dd5a texture_cache/util: Remove unneeded ReadBlockUnsafe
This call was reading GPU memory into the dst buffer, which is then overwritten by the SwizzleTexture call.
2022-04-04 15:57:54 -04:00
bunnei
f114436120 Merge pull request #8089 from merryhime/paranoia
configuration: Add Paranoid CPU accuracy level
2022-04-04 11:07:38 -07:00
Liam
b7be6a4316 OpenGL: fix cropping 2022-04-04 12:51:09 -04:00
Liam
c59c035d74 Vulkan: crop to screen dimensions if crop not explicitly requested 2022-04-04 11:45:15 -04:00
Liam
a57531854e OpenGL: propagate face flip condition 2022-04-04 10:32:14 -04:00
Liam
cb913e5c02 OpenGL: flip front faces if Z scale is inverted 2022-04-04 10:19:40 -04:00
lat9nq
983916e919 k_auto_object: Fix data race
Change the memory order to acqure-release when we decrement the
reference count. Prevents a race with line 89 reported by TSan.
2022-04-03 21:47:58 -04:00
lat9nq
6bcbbb29e7 k_thread: Fix data race
TSan reports a data race between writing at cpp:1162 and reading at
h:262. Make the thread_state atomic to prevent this.
2022-04-03 21:47:58 -04:00
lat9nq
d6a0666268 k_process: Fix data race
TSan reported a race between thread 36 and thread 34, a read at :225 and
a write at :225 respectively. Make total_proces_running_time_ticks
atomic to avoid this race.
2022-04-03 21:47:57 -04:00
lat9nq
5b5a1b7fa7 kernel: Fix current_process race
TSan reported a race at :258 and :803, so make current_process an atomic
pointer.
2022-04-03 21:47:57 -04:00
lat9nq
83b86d915a k_scheduler_lock: Fix data race
TSan reports a race between the main thread and T37 during
IsLockedByCurrentThread and when it's set at the end of Lock(),
respectively. Set owner_thread to an atomic pointer to fix it.

Co-authored-by: bunnei <bunneidev@gmail.com>
2022-04-03 21:47:57 -04:00
Merry
4052bfb4ad native_clock: Internal linkage for FencedRDTSC
__forceinline required on MSVC for function to be inlined
2022-04-03 22:38:12 +01:00
merry
fdd4d019ef native_clock: Use lfence with rdtsc 2022-04-03 22:38:10 +01:00
bunnei
e9cf2d43f1 Merge pull request #8105 from merryhime/atomicload128
native_clock: Use AtomicLoad128
2022-04-03 14:26:28 -07:00
merry
a5d040df3d arm_dynarmic: Use HaltReason for svc calls and reschedules 2022-04-03 18:20:11 +01:00
merry
f8b8af47ad dynarmic: Better interrupts 2022-04-03 16:39:48 +01:00
bunnei
2d1af6beee Merge pull request #8135 from Morph1984/websession-hack
applets/web: Keep foreground (websession) web applet open
2022-04-02 20:49:51 -07:00
bunnei
b5910ad0ba Merge pull request #8123 from german77/bombslinger
service: hid: Remove inaccurate behavior on initialization
2022-04-02 20:12:45 -07:00
german77
3e5469a974 service: npad: Default initialize shared memory 2022-04-02 19:43:49 -06:00
merry
979e53b87b native_clock: Use writeback from CAS to avoid double-loading 2022-04-02 22:22:48 +01:00
merry
084bd225dc atomic_ops: Implement AtomicCompareAndSwap with writeback 2022-04-02 22:22:48 +01:00
Merry
c562c1d6be native_clock: Use AtomicLoad128 2022-04-02 20:55:36 +01:00
Merry
b4746529e1 atomic_ops: Implement AtomicLoad128 2022-04-02 20:55:36 +01:00
merry
7f1e66e94b Merge pull request #8134 from Tachi107/remove-time-stretcher
audio_core: remove time stretcher
2022-04-02 20:49:21 +01:00
Morph
1e47252214 Merge pull request #8141 from merryhime/configure-hotkeys-columns
configure_hotkeys: Make first column stretch and not last column
2022-04-02 15:35:31 -04:00
Morph
0bcbe3a703 Merge pull request #8140 from merryhime/per-game-addon-columns
configure_per_game_addons: Stretch first column and not last
2022-04-02 15:35:26 -04:00
Morph
1cb83c91a6 Merge pull request #8142 from Tachi107/typos
fix: typos
2022-04-02 15:35:14 -04:00
merry
51a8dd4919 externals: Update dynarmic to 8bcd46b7
* Ensure 128-bit ordered load/stores are atomic
* Always order exclusive load/stores
2022-04-02 19:34:36 +01:00
merry
1f74b25fd1 externals: Update dynarmic to 9cadab8fa91a63564774ae7dbe74e7c18715f586
Enforce memory ordering for acquire/release instructions.
2022-04-02 18:52:57 +01:00
merry
f4004b1271 configure_per_game_addons: Set tree view minimum section size to 150px 2022-04-02 17:19:25 +01:00
merry
8c57de1605 configure_hotkeys: Make first column stretch and not last column
Also configure minimum width of columns to be 150px.
2022-04-02 17:17:13 +01:00
Andrea Pappacoda
0c214cb5b9 fix: typos 2022-04-02 16:34:07 +02:00
merry
8dc1913db8 configure_per_game_addons: Stretch first column and not last
This provides more sensible column widths.
2022-04-02 13:46:16 +01:00
bunnei
fdf4909f97 hle: service: nvflinger: buffer_queue_producer: Cleanup & fixes. 2022-04-01 22:59:35 -07:00
bunnei
4036e37bbe hle: service: nvflinger: consumer_base: Cleanup & fixes. 2022-04-01 22:58:40 -07:00
bunnei
30b07878ba hle: service: nvflinger: buffer_queue_producer: Cleanup & add GetReleasedBuffers. 2022-04-01 22:58:02 -07:00
bunnei
7610554b1e hle: service: nvflinger: buffer_queue_core: Cleanup & fixes. 2022-04-01 22:56:32 -07:00
bunnei
f9371f36a4 hle: service: nvflinger: Use correct logger namespace. 2022-04-01 22:55:44 -07:00
Morph
93f010c988 hle: service: nvdrv: Create a service thread where appropriate. 2022-04-02 01:24:30 -04:00
bunnei
d02bf6dab1 hle: service: vi: Create a service thread where appropriate. 2022-04-02 01:24:30 -04:00
bunnei
99770653bb hle: service: bsd: Create a service thread where appropriate. 2022-04-02 01:24:30 -04:00
bunnei
2afef2b609 hle: service: filesystem: Create a service thread where appropriate. 2022-04-02 01:24:30 -04:00
bunnei
11120b5b1e hle: service: audio: Create a service thread where appropriate. 2022-04-02 01:24:30 -04:00
bunnei
bf1750664c hle: service: Add option for service interfaces to create or use the default thread. 2022-04-02 01:24:30 -04:00
bunnei
864523327f hle: kernel: Create a default thread for services that do not need their own host thread. 2022-04-02 01:24:30 -04:00
Morph
1166c3910d applets/web: Keep foreground (websession) web applet open
This is a hack to keep the foreground (websession) web applet open in games using these such as Super Mario 3D All-Stars.
2022-04-01 22:50:01 -04:00
Liam
7d5a38ea6c shader_compiler: support const buffer indirect addressing in GLSL 2022-04-01 17:08:40 -04:00
Andrea Pappacoda
faf6a9876c audio_core: remove time stretcher
Also drop the SoundTouch dependency
2022-04-01 19:54:58 +02:00
Liam
a45baa0e78 shader_recompiler: support const buffer indirect addressing on OpenGL SPIR-V 2022-04-01 11:17:54 -04:00
german77
9346f0b33d service: hid: Remove inaccurate behavior on initialization 2022-03-31 12:35:57 -06:00
Morph
ea7a0d4652 registered_cache: Prevent nullptr dereference when accumulating files
For whatever reason, nca_file/dir can be nullptr in the list of files/dirs. I have not determined the cause of this yet, so add a nullptr check for these prior to dereferencing them.
2022-03-27 17:06:27 -04:00
merry
94967e0f6d configure_cpu: More descriptive text for Paranoid option 2022-03-26 08:56:23 +00:00
merry
3c8547160d configuration: Add Paranoid CPU accuracy level
Disables most optimizations for the paranoid.
2022-03-26 08:46:25 +00:00
lat9nq
50415f68a5 emu_window_sdl2: Set window size to display dimensions for exclusive fullscreen
Since SDL2 does not automatically resize the canvas when entering
fullscreen mode, resize the window to desktop display dimensions.
2022-03-15 04:33:08 -04:00
147 changed files with 2626 additions and 1029 deletions

3
.gitmodules vendored
View File

@@ -7,9 +7,6 @@
[submodule "dynarmic"]
path = externals/dynarmic
url = https://github.com/MerryMage/dynarmic.git
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git
[submodule "libressl"]
path = externals/libressl
url = https://github.com/citra-emu/ext-libressl-portable.git

View File

@@ -68,9 +68,6 @@ if (YUZU_USE_EXTERNAL_SDL2)
add_library(SDL2 ALIAS SDL2-static)
endif()
# SoundTouch
add_subdirectory(soundtouch)
# Cubeb
if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "")

Submodule externals/soundtouch deleted from 060181eaf2

View File

@@ -65,12 +65,14 @@ if (MSVC)
/we4305 # 'context': truncation from 'type1' to 'type2'
/we4388 # 'expression': signed/unsigned mismatch
/we4389 # 'operator': signed/unsigned mismatch
/we4505 # 'function': unreferenced local function has been removed
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
/we4555 # Expression has no effect; expected expression with side-effect
/we4715 # 'function': not all control paths return a value
/we4834 # Discarding return value of function with 'nodiscard' attribute
/we5038 # data member 'member1' will be initialized after data member 'member2'
/we5245 # 'function': unreferenced function with internal linkage has been removed
)
if (ARCHITECTURE_x86_64)
@@ -103,12 +105,6 @@ else()
-Wno-unused-parameter
)
# TODO: Remove when we update to a GCC compiler that enables this
# by default (i.e. GCC 10 or newer).
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
add_compile_options(-fconcepts)
endif()
if (ARCHITECTURE_x86_64)
add_compile_options("-mcx16")
endif()

View File

@@ -36,8 +36,6 @@ add_library(audio_core STATIC
splitter_context.h
stream.cpp
stream.h
time_stretch.cpp
time_stretch.h
voice_context.cpp
voice_context.h
@@ -63,7 +61,6 @@ if (NOT MSVC)
endif()
target_link_libraries(audio_core PUBLIC common core)
target_link_libraries(audio_core PRIVATE SoundTouch)
if(ENABLE_CUBEB)
target_link_libraries(audio_core PRIVATE cubeb)

View File

@@ -7,7 +7,6 @@
#include <cstring>
#include "audio_core/cubeb_sink.h"
#include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/ring_buffer.h"
@@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream {
public:
CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
num_channels} {
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} {
cubeb_stream_params params{};
params.rate = sample_rate;
@@ -131,7 +129,6 @@ private:
Common::RingBuffer<s16, 0x10000> queue;
std::array<s16, 2> last_frame{};
std::atomic<bool> should_flush{};
TimeStretcher time_stretch;
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames);
@@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void*
const std::size_t num_channels = impl->GetNumChannels();
const std::size_t samples_to_write = num_channels * num_frames;
std::size_t samples_written;
/*
if (Settings::values.enable_audio_stretching.GetValue()) {
const std::vector<s16> in{impl->queue.Pop()};
const std::size_t num_in{in.size() / num_channels};
s16* const out{reinterpret_cast<s16*>(buffer)};
const std::size_t out_frames =
impl->time_stretch.Process(in.data(), num_in, out, num_frames);
samples_written = out_frames * num_channels;
if (impl->should_flush) {
impl->time_stretch.Flush();
impl->should_flush = false;
}
} else {
samples_written = impl->queue.Pop(buffer, samples_to_write);
}*/
samples_written = impl->queue.Pop(buffer, samples_to_write);
const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write);
if (samples_written >= num_channels) {
std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),

View File

@@ -7,7 +7,6 @@
#include <cstring>
#include "audio_core/sdl2_sink.h"
#include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/assert.h"
#include "common/logging/log.h"
//#include "common/settings.h"
@@ -27,7 +26,7 @@ namespace AudioCore {
class SDLSinkStream final : public SinkStream {
public:
SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
: num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} {
: num_channels{std::min(num_channels_, 6u)} {
SDL_AudioSpec spec;
spec.freq = sample_rate;
@@ -116,7 +115,6 @@ private:
SDL_AudioDeviceID dev = 0;
u32 num_channels{};
std::atomic<bool> should_flush{};
TimeStretcher time_stretch;
};
SDLSink::SDLSink(std::string_view target_device_name) {

View File

@@ -1,68 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cmath>
#include <cstddef>
#include "audio_core/time_stretch.h"
#include "common/logging/log.h"
namespace AudioCore {
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
m_sound_touch.setChannels(channel_count);
m_sound_touch.setSampleRate(sample_rate);
m_sound_touch.setPitch(1.0);
m_sound_touch.setTempo(1.0);
}
void TimeStretcher::Clear() {
m_sound_touch.clear();
}
void TimeStretcher::Flush() {
m_sound_touch.flush();
}
std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
std::size_t num_out) {
const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
// We were given actual_samples number of samples, and num_samples were requested from us.
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
const double max_latency = 0.25; // seconds
const double max_backlog = m_sample_rate * max_latency;
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
if (backlog_fullness > 4.0) {
// Too many samples in backlog: Don't push anymore on
num_in = 0;
}
// We ideally want the backlog to be about 50% full.
// This gives some headroom both ways to prevent underflow and overflow.
// We tweak current_ratio to encourage this.
constexpr double tweak_time_scale = 0.05; // seconds
const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
// This low-pass filter smoothes out variance in the calculated stretch ratio.
// The time-scale determines how responsive this filter is.
constexpr double lpf_time_scale = 0.712; // seconds
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
// Place a lower limit of 5% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
backlog_fullness);
m_sound_touch.putSamples(in, static_cast<u32>(num_in));
return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
}
} // namespace AudioCore

View File

@@ -1,34 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <SoundTouch.h>
#include "common/common_types.h"
namespace AudioCore {
class TimeStretcher {
public:
TimeStretcher(u32 sample_rate, u32 channel_count);
/// @param in Input sample buffer
/// @param num_in Number of input frames in `in`
/// @param out Output sample buffer
/// @param num_out Desired number of output frames in `out`
/// @returns Actual number of frames written to `out`
std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
void Clear();
void Flush();
private:
u32 m_sample_rate;
soundtouch::SoundTouch m_sound_touch;
double m_stretch_ratio = 1.0;
};
} // namespace AudioCore

View File

@@ -46,6 +46,50 @@ namespace Common {
reinterpret_cast<__int64*>(expected.data())) != 0;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
u8& actual) {
actual =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
u16& actual) {
actual =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
u32& actual) {
actual =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
u64& actual) {
actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
expected);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
u128& actual) {
const bool result =
_InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
actual = expected;
return result;
}
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
u128 result{};
_InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
result[0], reinterpret_cast<__int64*>(result.data()));
return result;
}
#else
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
@@ -72,6 +116,52 @@ namespace Common {
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
u8& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
u16& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
u32& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
u64& actual) {
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
u128& actual) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
unsigned __int128 actual_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
std::memcpy(actual.data(), &actual_a, sizeof(u128));
return actual_a == expected_a;
}
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
unsigned __int128 zeros_a = 0;
unsigned __int128 result_a =
__sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
u128 result;
std::memcpy(result.data(), &result_a, sizeof(u128));
return result;
}
#endif
} // namespace Common

View File

@@ -149,7 +149,7 @@ public:
}
void Unmap(size_t virtual_offset, size_t length) {
std::lock_guard lock{placeholder_mutex};
std::scoped_lock lock{placeholder_mutex};
// Unmap until there are no more placeholders
while (UnmapOnePlaceholder(virtual_offset, length)) {
@@ -169,7 +169,7 @@ public:
}
const size_t virtual_end = virtual_offset + length;
std::lock_guard lock{placeholder_mutex};
std::scoped_lock lock{placeholder_mutex};
auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
while (it != end) {
const size_t offset = std::max(it->lower(), virtual_offset);

View File

@@ -101,6 +101,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, IRS) \
SUB(Service, JIT) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \

View File

@@ -69,6 +69,7 @@ enum class Class : u8 {
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_IRS, ///< The IRS service
Service_JIT, ///< The JIT service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service

View File

@@ -38,6 +38,7 @@ enum class CPUAccuracy : u32 {
Auto = 0,
Accurate = 1,
Unsafe = 2,
Paranoid = 3,
};
enum class FullscreenMode : u32 {
@@ -470,7 +471,7 @@ struct Values {
// Cpu
RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
CPUAccuracy::Unsafe, "cpu_accuracy"};
CPUAccuracy::Paranoid, "cpu_accuracy"};
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};

View File

@@ -17,7 +17,7 @@ namespace Common {
class Event {
public:
void Set() {
std::lock_guard lk{mutex};
std::scoped_lock lk{mutex};
if (!is_set) {
is_set = true;
condvar.notify_one();

View File

@@ -52,7 +52,7 @@ public:
// line before cv.wait
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
std::lock_guard lock{cv_mutex};
std::scoped_lock lock{cv_mutex};
cv.notify_one();
}
@@ -159,7 +159,7 @@ public:
template <typename Arg>
void Push(Arg&& t) {
std::lock_guard lock{write_lock};
std::scoped_lock lock{write_lock};
spsc_queue.Push(t);
}

View File

@@ -10,25 +10,49 @@
#include "common/uint128.h"
#include "common/x64/native_clock.h"
#ifdef _MSC_VER
#include <intrin.h>
#endif
namespace Common {
#ifdef _MSC_VER
__forceinline static u64 FencedRDTSC() {
_mm_lfence();
_ReadWriteBarrier();
const u64 result = __rdtsc();
_mm_lfence();
_ReadWriteBarrier();
return result;
}
#else
static u64 FencedRDTSC() {
u64 result;
asm volatile("lfence\n\t"
"rdtsc\n\t"
"shl $32, %%rdx\n\t"
"or %%rdx, %0\n\t"
"lfence"
: "=a"(result)
:
: "rdx", "memory", "cc");
return result;
}
#endif
u64 EstimateRDTSCFrequency() {
// Discard the first result measuring the rdtsc.
_mm_mfence();
__rdtsc();
FencedRDTSC();
std::this_thread::sleep_for(std::chrono::milliseconds{1});
_mm_mfence();
__rdtsc();
FencedRDTSC();
// Get the current time.
const auto start_time = std::chrono::steady_clock::now();
_mm_mfence();
const u64 tsc_start = __rdtsc();
const u64 tsc_start = FencedRDTSC();
// Wait for 200 milliseconds.
std::this_thread::sleep_for(std::chrono::milliseconds{200});
const auto end_time = std::chrono::steady_clock::now();
_mm_mfence();
const u64 tsc_end = __rdtsc();
const u64 tsc_end = FencedRDTSC();
// Calculate differences.
const u64 timer_diff = static_cast<u64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
@@ -42,8 +66,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
u64 rtsc_frequency_)
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
rtsc_frequency_} {
_mm_mfence();
time_point.inner.last_measure = __rdtsc();
time_point.inner.last_measure = FencedRDTSC();
time_point.inner.accumulated_ticks = 0U;
ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
@@ -55,10 +78,10 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
u64 NativeClock::GetRTSC() {
TimePoint new_time_point{};
TimePoint current_time_point{};
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
do {
current_time_point.pack = time_point.pack;
_mm_mfence();
const u64 current_measure = __rdtsc();
const u64 current_measure = FencedRDTSC();
u64 diff = current_measure - current_time_point.inner.last_measure;
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
@@ -66,7 +89,7 @@ u64 NativeClock::GetRTSC() {
: current_time_point.inner.last_measure;
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack));
current_time_point.pack, current_time_point.pack));
/// The clock cannot be more precise than the guest timer, remove the lower bits
return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
}
@@ -75,13 +98,13 @@ void NativeClock::Pause(bool is_paused) {
if (!is_paused) {
TimePoint current_time_point{};
TimePoint new_time_point{};
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
do {
current_time_point.pack = time_point.pack;
new_time_point.pack = current_time_point.pack;
_mm_mfence();
new_time_point.inner.last_measure = __rdtsc();
new_time_point.inner.last_measure = FencedRDTSC();
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack));
current_time_point.pack, current_time_point.pack));
}
}

View File

@@ -13,6 +13,8 @@ add_library(core STATIC
arm/dynarmic/arm_exclusive_monitor.h
arm/exclusive_monitor.cpp
arm/exclusive_monitor.h
arm/symbols.cpp
arm/symbols.h
constants.cpp
constants.h
core.cpp
@@ -458,6 +460,10 @@ add_library(core STATIC
hle/service/hid/controllers/touchscreen.h
hle/service/hid/controllers/xpad.cpp
hle/service/hid/controllers/xpad.h
hle/service/jit/jit_context.cpp
hle/service/jit/jit_context.h
hle/service/jit/jit.cpp
hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/errors.h

View File

@@ -8,134 +8,13 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/arm/symbols.h"
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/loader/loader.h"
#include "core/memory.h"
namespace Core {
namespace {
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
enum class ELFSymbolType : u8 {
None = 0,
Object = 1,
Function = 2,
Section = 3,
File = 4,
Common = 5,
TLS = 6,
};
enum class ELFSymbolBinding : u8 {
Local = 0,
Global = 1,
Weak = 2,
};
enum class ELFSymbolVisibility : u8 {
Default = 0,
Internal = 1,
Hidden = 2,
Protected = 3,
};
struct ELFSymbol {
u32 name_index;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
u64 value;
u64 size;
};
static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
return {};
}
const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
VAddr string_table_offset{};
VAddr symbol_table_offset{};
u64 symbol_entry_size{};
VAddr dynamic_index = dynamic_offset;
while (true) {
const u64 tag = memory.Read64(dynamic_index);
const u64 value = memory.Read64(dynamic_index + 0x8);
dynamic_index += 0x10;
if (tag == ELF_DYNAMIC_TAG_NULL) {
break;
}
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
string_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
symbol_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
symbol_entry_size = value;
}
}
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
return {};
}
const auto string_table_address = text_offset + string_table_offset;
const auto symbol_table_address = text_offset + symbol_table_offset;
Symbols out;
VAddr symbol_index = symbol_table_address;
while (symbol_index < string_table_address) {
ELFSymbol symbol{};
memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
VAddr string_offset = string_table_address + symbol.name_index;
std::string name;
for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
name += static_cast<char>(c);
}
symbol_index += symbol_entry_size;
out.push_back({symbol, name});
}
return out;
}
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
const auto iter =
std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
const auto& symbol = pair.first;
const auto end_address = symbol.value + symbol.size;
return func_address >= symbol.value && func_address < end_address;
});
if (iter == symbols.end()) {
return std::nullopt;
}
return iter->second;
}
} // Anonymous namespace
constexpr u64 SEGMENT_BASE = 0x7100000000ull;
@@ -169,9 +48,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
return {};
}
std::map<std::string, Symbols> symbols;
std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) {
symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
symbols.insert_or_assign(module.second,
Symbols::GetSymbols(module.first, system.Memory(),
system.CurrentProcess()->Is64BitProcess()));
}
for (auto& entry : out) {
@@ -193,7 +74,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) {
const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) {
// TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol;
@@ -225,9 +106,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
return {};
}
std::map<std::string, Symbols> symbols;
std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) {
symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
symbols.insert_or_assign(module.second,
Symbols::GetSymbols(module.first, system.Memory(),
system.CurrentProcess()->Is64BitProcess()));
}
for (auto& entry : out) {
@@ -249,7 +132,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) {
const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) {
// TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol;

View File

@@ -171,6 +171,9 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
/// Signal an interrupt and ask the core to halt as soon as possible.
virtual void SignalInterrupt() = 0;
struct BacktraceEntry {
std::string module;
u64 address;

View File

@@ -25,6 +25,9 @@ namespace Core {
using namespace Common::Literals;
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -70,11 +73,13 @@ public:
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
parent.LogBacktrace();
UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
MemoryReadCode(pc));
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
parent.LogBacktrace();
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
@@ -82,15 +87,13 @@ public:
}
void CallSVC(u32 swi) override {
parent.svc_called = true;
parent.svc_swi = swi;
parent.jit->HaltExecution();
parent.jit->HaltExecution(svc_call);
}
void AddTicks(u64 ticks) override {
if (parent.uses_wall_clock) {
return;
}
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
// if not all cores are doing a similar amount of work. Instead of doing this, we should
@@ -106,12 +109,8 @@ public:
}
u64 GetTicksRemaining() override {
if (parent.uses_wall_clock) {
if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
return minimum_run_cycles;
}
return 0U;
}
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -146,6 +145,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
config.enable_cycle_counting = !uses_wall_clock;
// Code cache size
config.code_cache_size = 512_MiB;
@@ -186,35 +186,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_recompile_exclusives) {
config.recompile_on_exclusive_fastmem_failure = false;
}
}
} else {
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
}
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
// Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
}
// Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
// Paranoia mode for debugging optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
config.unsafe_optimizations = false;
config.optimizations = Dynarmic::no_optimizations;
}
}
return std::make_unique<Dynarmic::A32::Jit>(config);
@@ -222,13 +228,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
void ARM_Dynarmic_32::Run() {
while (true) {
jit->Run();
if (!svc_called) {
break;
const auto hr = jit->Run();
if (Has(hr, svc_call)) {
Kernel::Svc::Call(system, svc_swi);
}
svc_called = false;
Kernel::Svc::Call(system, svc_swi);
if (shutdown) {
if (Has(hr, break_loop) || !uses_wall_clock) {
break;
}
}
@@ -314,8 +318,11 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
}
void ARM_Dynarmic_32::PrepareReschedule() {
jit->HaltExecution();
shutdown = true;
jit->HaltExecution(break_loop);
}
void ARM_Dynarmic_32::SignalInterrupt() {
jit->HaltExecution(break_loop);
}
void ARM_Dynarmic_32::ClearInstructionCache() {

View File

@@ -57,6 +57,7 @@ public:
void LoadContext(const ThreadContext64& ctx) override {}
void PrepareReschedule() override;
void SignalInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -83,9 +84,6 @@ private:
// SVC callback
u32 svc_swi{};
bool svc_called{};
bool shutdown{};
};
} // namespace Core

View File

@@ -26,6 +26,9 @@ namespace Core {
using Vector = Dynarmic::A64::Vector;
using namespace Common::Literals;
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -81,6 +84,7 @@ public:
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
parent.LogBacktrace();
LOG_ERROR(Core_ARM,
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
num_instructions, MemoryReadCode(pc));
@@ -105,7 +109,7 @@ public:
break;
}
parent.jit->HaltExecution();
parent.jit->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -118,21 +122,19 @@ public:
return;
case Dynarmic::A64::Exception::Breakpoint:
default:
parent.LogBacktrace();
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
}
}
void CallSVC(u32 swi) override {
parent.svc_called = true;
parent.svc_swi = swi;
parent.jit->HaltExecution();
parent.jit->HaltExecution(svc_call);
}
void AddTicks(u64 ticks) override {
if (parent.uses_wall_clock) {
return;
}
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -147,12 +149,8 @@ public:
}
u64 GetTicksRemaining() override {
if (parent.uses_wall_clock) {
if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
return minimum_run_cycles;
}
return 0U;
}
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -208,6 +206,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
config.enable_cycle_counting = !uses_wall_clock;
// Code cache size
config.code_cache_size = 512_MiB;
@@ -248,35 +247,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_recompile_exclusives) {
config.recompile_on_exclusive_fastmem_failure = false;
}
}
} else {
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_fastmem_check) {
config.fastmem_address_space_bits = 64;
}
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
}
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
config.unsafe_optimizations = true;
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
// Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
}
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
}
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
}
if (Settings::values.cpuopt_unsafe_fastmem_check) {
config.fastmem_address_space_bits = 64;
}
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
}
// Curated optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.fastmem_address_space_bits = 64;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
// Paranoia mode for debugging optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
config.unsafe_optimizations = false;
config.optimizations = Dynarmic::no_optimizations;
}
}
return std::make_shared<Dynarmic::A64::Jit>(config);
@@ -284,13 +289,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
void ARM_Dynarmic_64::Run() {
while (true) {
jit->Run();
if (!svc_called) {
break;
const auto hr = jit->Run();
if (Has(hr, svc_call)) {
Kernel::Svc::Call(system, svc_swi);
}
svc_called = false;
Kernel::Svc::Call(system, svc_swi);
if (shutdown) {
if (Has(hr, break_loop) || !uses_wall_clock) {
break;
}
}
@@ -381,8 +384,11 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
}
void ARM_Dynarmic_64::PrepareReschedule() {
jit->HaltExecution();
shutdown = true;
jit->HaltExecution(break_loop);
}
void ARM_Dynarmic_64::SignalInterrupt() {
jit->HaltExecution(break_loop);
}
void ARM_Dynarmic_64::ClearInstructionCache() {

View File

@@ -51,6 +51,7 @@ public:
void LoadContext(const ThreadContext64& ctx) override;
void PrepareReschedule() override;
void SignalInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -77,9 +78,6 @@ private:
// SVC callback
u32 svc_swi{};
bool svc_called{};
bool shutdown{};
};
} // namespace Core

190
src/core/arm/symbols.cpp Normal file
View File

@@ -0,0 +1,190 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "core/arm/symbols.h"
#include "core/core.h"
#include "core/memory.h"
namespace Core {
namespace {
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
enum class ELFSymbolType : u8 {
None = 0,
Object = 1,
Function = 2,
Section = 3,
File = 4,
Common = 5,
TLS = 6,
};
enum class ELFSymbolBinding : u8 {
Local = 0,
Global = 1,
Weak = 2,
};
enum class ELFSymbolVisibility : u8 {
Default = 0,
Internal = 1,
Hidden = 2,
Protected = 3,
};
struct ELF64Symbol {
u32 name_index;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
u64 value;
u64 size;
};
static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
struct ELF32Symbol {
u32 name_index;
u32 value;
u32 size;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
};
static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
} // Anonymous namespace
namespace Symbols {
template <typename Word, typename ELFSymbol, typename ByteReader>
static Symbols GetSymbols(ByteReader ReadBytes) {
const auto Read8{[&](u64 index) {
u8 ret;
ReadBytes(&ret, index, sizeof(u8));
return ret;
}};
const auto Read32{[&](u64 index) {
u32 ret;
ReadBytes(&ret, index, sizeof(u32));
return ret;
}};
const auto ReadWord{[&](u64 index) {
Word ret;
ReadBytes(&ret, index, sizeof(Word));
return ret;
}};
const u32 mod_offset = Read32(4);
if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
return {};
}
VAddr string_table_offset{};
VAddr symbol_table_offset{};
u64 symbol_entry_size{};
const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
VAddr dynamic_index = dynamic_offset;
while (true) {
const Word tag = ReadWord(dynamic_index);
const Word value = ReadWord(dynamic_index + sizeof(Word));
dynamic_index += 2 * sizeof(Word);
if (tag == ELF_DYNAMIC_TAG_NULL) {
break;
}
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
string_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
symbol_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
symbol_entry_size = value;
}
}
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
return {};
}
Symbols out;
VAddr symbol_index = symbol_table_offset;
while (symbol_index < string_table_offset) {
ELFSymbol symbol{};
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
VAddr string_offset = string_table_offset + symbol.name_index;
std::string name;
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
name += static_cast<char>(c);
}
symbol_index += symbol_entry_size;
out[name] = std::make_pair(symbol.value, symbol.size);
}
return out;
}
Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
const auto ReadBytes{
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
if (is_64) {
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
} else {
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
}
}
Symbols GetSymbols(std::span<const u8> data, bool is_64) {
const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
std::memcpy(ptr, data.data() + offset, size);
}};
if (is_64) {
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
} else {
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
}
}
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
const auto& [name, sym_info] = pair;
const auto& [start_address, size] = sym_info;
const auto end_address = start_address + size;
return addr >= start_address && addr < end_address;
});
if (iter == symbols.cend()) {
return std::nullopt;
}
return iter->first;
}
} // namespace Symbols
} // namespace Core

27
src/core/arm/symbols.h Normal file
View File

@@ -0,0 +1,27 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <optional>
#include <span>
#include <string>
#include <utility>
#include "common/common_types.h"
namespace Core::Memory {
class Memory;
} // namespace Core::Memory
namespace Core::Symbols {
using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
} // namespace Core::Symbols

View File

@@ -148,29 +148,33 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
// LayeredExeFS
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
if (load_dir != nullptr && load_dir->GetSize() > 0) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(
patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
const auto load_patch_dirs = load_dir->GetSubdirectories();
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
}
std::vector<VirtualDir> layers;
layers.reserve(patch_dirs.size() + 1);
for (const auto& subdir : patch_dirs) {
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
continue;
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
if (exefs_dir != nullptr)
layers.push_back(std::move(exefs_dir));
}
layers.push_back(exefs);
std::vector<VirtualDir> layers;
layers.reserve(patch_dirs.size() + 1);
for (const auto& subdir : patch_dirs) {
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
continue;
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
if (layered != nullptr) {
LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
exefs = std::move(layered);
}
auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
if (exefs_dir != nullptr)
layers.push_back(std::move(exefs_dir));
}
layers.push_back(exefs);
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
if (layered != nullptr) {
LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
exefs = std::move(layered);
}
if (Settings::values.dump_exefs) {
@@ -536,11 +540,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
// SDMC mod directory (RomFS LayeredFS)
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 &&
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
const auto mod_disabled =
std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS");
if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) {
std::string types;
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
AppendCommaIfNotEmpty(types, "LayeredExeFS");
}
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
AppendCommaIfNotEmpty(types, "LayeredFS");
}
if (!types.empty()) {
const auto mod_disabled =
std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types);
}
}
// DLC

View File

@@ -387,15 +387,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
continue;
for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
if (!FollowsNcaIdFormat(nca_dir->GetName()))
if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
continue;
}
ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
}
for (const auto& nca_file : d2_dir->GetFiles()) {
if (!FollowsNcaIdFormat(nca_file->GetName()))
if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
continue;
}
ids.push_back(
Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));

View File

@@ -132,7 +132,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
}
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
auto& raw_status = console.motion_values.raw_status;
auto& emulated = console.motion_values.emulated;
@@ -151,6 +151,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
emulated.UpdateOrientation(raw_status.delta_timestamp);
if (is_configuring) {
lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
return;
}
@@ -166,6 +167,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
// Find what is this value
motion.verticalization_error = 0.0f;
lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
}
@@ -173,11 +175,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
if (index >= console.touch_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
console.touch_values[index] = TransformToTouch(callback);
if (is_configuring) {
lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
return;
}
@@ -189,26 +192,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
.pressed = console.touch_values[index].pressed.value,
};
lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
}
ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
std::scoped_lock lock{mutex};
return console.motion_values;
}
TouchValues EmulatedConsole::GetTouchValues() const {
std::scoped_lock lock{mutex};
return console.touch_values;
}
ConsoleMotion EmulatedConsole::GetMotion() const {
std::scoped_lock lock{mutex};
return console.motion_state;
}
TouchFingerState EmulatedConsole::GetTouch() const {
std::scoped_lock lock{mutex};
return console.touch_state;
}
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ConsoleUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
@@ -218,13 +227,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
}
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
std::lock_guard lock{mutex};
std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, update_callback);
return last_callback_key++;
}
void EmulatedConsole::DeleteCallback(int key) {
std::lock_guard lock{mutex};
std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);

View File

@@ -183,6 +183,7 @@ private:
TouchDevices touch_devices;
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
std::unordered_map<int, ConsoleUpdateCallback> callback_list;
int last_callback_key = 0;

View File

@@ -353,14 +353,17 @@ void EmulatedController::DisableConfiguration() {
}
void EmulatedController::EnableSystemButtons() {
std::scoped_lock lock{mutex};
system_buttons_enabled = true;
}
void EmulatedController::DisableSystemButtons() {
std::scoped_lock lock{mutex};
system_buttons_enabled = false;
}
void EmulatedController::ResetSystemButtons() {
std::scoped_lock lock{mutex};
controller.home_button_state.home.Assign(false);
controller.capture_button_state.capture.Assign(false);
}
@@ -494,139 +497,141 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
if (index >= controller.button_values.size()) {
return;
}
{
std::lock_guard lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = controller.button_values[index];
std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = controller.button_values[index];
// Only read button values that have the same uuid or are pressed once
if (current_status.uuid != uuid) {
if (!new_status.value) {
return;
}
}
current_status.toggle = new_status.toggle;
current_status.uuid = uuid;
// Update button status with current
if (!current_status.toggle) {
current_status.locked = false;
if (current_status.value != new_status.value) {
current_status.value = new_status.value;
value_changed = true;
}
} else {
// Toggle button and lock status
if (new_status.value && !current_status.locked) {
current_status.locked = true;
current_status.value = !current_status.value;
value_changed = true;
}
// Unlock button ready for next press
if (!new_status.value && current_status.locked) {
current_status.locked = false;
}
}
if (!value_changed) {
// Only read button values that have the same uuid or are pressed once
if (current_status.uuid != uuid) {
if (!new_status.value) {
return;
}
if (is_configuring) {
controller.npad_button_state.raw = NpadButton::None;
controller.debug_pad_button_state.raw = 0;
TriggerOnChange(ControllerTriggerType::Button, false);
return;
}
switch (index) {
case Settings::NativeButton::A:
controller.npad_button_state.a.Assign(current_status.value);
controller.debug_pad_button_state.a.Assign(current_status.value);
break;
case Settings::NativeButton::B:
controller.npad_button_state.b.Assign(current_status.value);
controller.debug_pad_button_state.b.Assign(current_status.value);
break;
case Settings::NativeButton::X:
controller.npad_button_state.x.Assign(current_status.value);
controller.debug_pad_button_state.x.Assign(current_status.value);
break;
case Settings::NativeButton::Y:
controller.npad_button_state.y.Assign(current_status.value);
controller.debug_pad_button_state.y.Assign(current_status.value);
break;
case Settings::NativeButton::LStick:
controller.npad_button_state.stick_l.Assign(current_status.value);
break;
case Settings::NativeButton::RStick:
controller.npad_button_state.stick_r.Assign(current_status.value);
break;
case Settings::NativeButton::L:
controller.npad_button_state.l.Assign(current_status.value);
controller.debug_pad_button_state.l.Assign(current_status.value);
break;
case Settings::NativeButton::R:
controller.npad_button_state.r.Assign(current_status.value);
controller.debug_pad_button_state.r.Assign(current_status.value);
break;
case Settings::NativeButton::ZL:
controller.npad_button_state.zl.Assign(current_status.value);
controller.debug_pad_button_state.zl.Assign(current_status.value);
break;
case Settings::NativeButton::ZR:
controller.npad_button_state.zr.Assign(current_status.value);
controller.debug_pad_button_state.zr.Assign(current_status.value);
break;
case Settings::NativeButton::Plus:
controller.npad_button_state.plus.Assign(current_status.value);
controller.debug_pad_button_state.plus.Assign(current_status.value);
break;
case Settings::NativeButton::Minus:
controller.npad_button_state.minus.Assign(current_status.value);
controller.debug_pad_button_state.minus.Assign(current_status.value);
break;
case Settings::NativeButton::DLeft:
controller.npad_button_state.left.Assign(current_status.value);
controller.debug_pad_button_state.d_left.Assign(current_status.value);
break;
case Settings::NativeButton::DUp:
controller.npad_button_state.up.Assign(current_status.value);
controller.debug_pad_button_state.d_up.Assign(current_status.value);
break;
case Settings::NativeButton::DRight:
controller.npad_button_state.right.Assign(current_status.value);
controller.debug_pad_button_state.d_right.Assign(current_status.value);
break;
case Settings::NativeButton::DDown:
controller.npad_button_state.down.Assign(current_status.value);
controller.debug_pad_button_state.d_down.Assign(current_status.value);
break;
case Settings::NativeButton::SL:
controller.npad_button_state.left_sl.Assign(current_status.value);
controller.npad_button_state.right_sl.Assign(current_status.value);
break;
case Settings::NativeButton::SR:
controller.npad_button_state.left_sr.Assign(current_status.value);
controller.npad_button_state.right_sr.Assign(current_status.value);
break;
case Settings::NativeButton::Home:
if (!system_buttons_enabled) {
break;
}
controller.home_button_state.home.Assign(current_status.value);
break;
case Settings::NativeButton::Screenshot:
if (!system_buttons_enabled) {
break;
}
controller.capture_button_state.capture.Assign(current_status.value);
break;
}
}
current_status.toggle = new_status.toggle;
current_status.uuid = uuid;
// Update button status with current
if (!current_status.toggle) {
current_status.locked = false;
if (current_status.value != new_status.value) {
current_status.value = new_status.value;
value_changed = true;
}
} else {
// Toggle button and lock status
if (new_status.value && !current_status.locked) {
current_status.locked = true;
current_status.value = !current_status.value;
value_changed = true;
}
// Unlock button ready for next press
if (!new_status.value && current_status.locked) {
current_status.locked = false;
}
}
if (!value_changed) {
return;
}
if (is_configuring) {
controller.npad_button_state.raw = NpadButton::None;
controller.debug_pad_button_state.raw = 0;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Button, false);
return;
}
switch (index) {
case Settings::NativeButton::A:
controller.npad_button_state.a.Assign(current_status.value);
controller.debug_pad_button_state.a.Assign(current_status.value);
break;
case Settings::NativeButton::B:
controller.npad_button_state.b.Assign(current_status.value);
controller.debug_pad_button_state.b.Assign(current_status.value);
break;
case Settings::NativeButton::X:
controller.npad_button_state.x.Assign(current_status.value);
controller.debug_pad_button_state.x.Assign(current_status.value);
break;
case Settings::NativeButton::Y:
controller.npad_button_state.y.Assign(current_status.value);
controller.debug_pad_button_state.y.Assign(current_status.value);
break;
case Settings::NativeButton::LStick:
controller.npad_button_state.stick_l.Assign(current_status.value);
break;
case Settings::NativeButton::RStick:
controller.npad_button_state.stick_r.Assign(current_status.value);
break;
case Settings::NativeButton::L:
controller.npad_button_state.l.Assign(current_status.value);
controller.debug_pad_button_state.l.Assign(current_status.value);
break;
case Settings::NativeButton::R:
controller.npad_button_state.r.Assign(current_status.value);
controller.debug_pad_button_state.r.Assign(current_status.value);
break;
case Settings::NativeButton::ZL:
controller.npad_button_state.zl.Assign(current_status.value);
controller.debug_pad_button_state.zl.Assign(current_status.value);
break;
case Settings::NativeButton::ZR:
controller.npad_button_state.zr.Assign(current_status.value);
controller.debug_pad_button_state.zr.Assign(current_status.value);
break;
case Settings::NativeButton::Plus:
controller.npad_button_state.plus.Assign(current_status.value);
controller.debug_pad_button_state.plus.Assign(current_status.value);
break;
case Settings::NativeButton::Minus:
controller.npad_button_state.minus.Assign(current_status.value);
controller.debug_pad_button_state.minus.Assign(current_status.value);
break;
case Settings::NativeButton::DLeft:
controller.npad_button_state.left.Assign(current_status.value);
controller.debug_pad_button_state.d_left.Assign(current_status.value);
break;
case Settings::NativeButton::DUp:
controller.npad_button_state.up.Assign(current_status.value);
controller.debug_pad_button_state.d_up.Assign(current_status.value);
break;
case Settings::NativeButton::DRight:
controller.npad_button_state.right.Assign(current_status.value);
controller.debug_pad_button_state.d_right.Assign(current_status.value);
break;
case Settings::NativeButton::DDown:
controller.npad_button_state.down.Assign(current_status.value);
controller.debug_pad_button_state.d_down.Assign(current_status.value);
break;
case Settings::NativeButton::SL:
controller.npad_button_state.left_sl.Assign(current_status.value);
controller.npad_button_state.right_sl.Assign(current_status.value);
break;
case Settings::NativeButton::SR:
controller.npad_button_state.left_sr.Assign(current_status.value);
controller.npad_button_state.right_sr.Assign(current_status.value);
break;
case Settings::NativeButton::Home:
if (!system_buttons_enabled) {
break;
}
controller.home_button_state.home.Assign(current_status.value);
break;
case Settings::NativeButton::Screenshot:
if (!system_buttons_enabled) {
break;
}
controller.capture_button_state.capture.Assign(current_status.value);
break;
}
lock.unlock();
if (!is_connected) {
if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
Connect();
@@ -643,7 +648,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (index >= controller.stick_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
const auto stick_value = TransformToStick(callback);
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
@@ -659,6 +664,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (is_configuring) {
controller.analog_stick_state.left = {};
controller.analog_stick_state.right = {};
lock.unlock();
TriggerOnChange(ControllerTriggerType::Stick, false);
return;
}
@@ -685,6 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
break;
}
lock.unlock();
TriggerOnChange(ControllerTriggerType::Stick, true);
}
@@ -693,7 +700,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (index >= controller.trigger_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
const auto trigger_value = TransformToTrigger(callback);
// Only read trigger values that have the same uuid or are pressed once
@@ -709,6 +716,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (is_configuring) {
controller.gc_trigger_state.left = 0;
controller.gc_trigger_state.right = 0;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Trigger, false);
return;
}
@@ -727,6 +735,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
break;
}
lock.unlock();
TriggerOnChange(ControllerTriggerType::Trigger, true);
}
@@ -735,7 +744,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
if (index >= controller.motion_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
auto& raw_status = controller.motion_values[index].raw_status;
auto& emulated = controller.motion_values[index].emulated;
@@ -756,6 +765,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
force_update_motion = raw_status.force_update;
if (is_configuring) {
lock.unlock();
TriggerOnChange(ControllerTriggerType::Motion, false);
return;
}
@@ -767,6 +777,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
lock.unlock();
TriggerOnChange(ControllerTriggerType::Motion, true);
}
@@ -775,10 +786,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
if (index >= controller.battery_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
controller.battery_values[index] = TransformToBattery(callback);
if (is_configuring) {
lock.unlock();
TriggerOnChange(ControllerTriggerType::Battery, false);
return;
}
@@ -835,6 +847,8 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
};
break;
}
lock.unlock();
TriggerOnChange(ControllerTriggerType::Battery, true);
}
@@ -932,6 +946,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
}
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
@@ -947,6 +962,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
}
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
@@ -982,40 +998,44 @@ void EmulatedController::Connect(bool use_temporary_value) {
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
return;
}
{
std::lock_guard lock{mutex};
if (is_configuring) {
tmp_is_connected = true;
TriggerOnChange(ControllerTriggerType::Connected, false);
return;
}
if (is_connected) {
return;
}
is_connected = true;
std::unique_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = true;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Connected, false);
return;
}
if (is_connected) {
return;
}
is_connected = true;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Connected, true);
}
void EmulatedController::Disconnect() {
{
std::lock_guard lock{mutex};
if (is_configuring) {
tmp_is_connected = false;
TriggerOnChange(ControllerTriggerType::Disconnected, false);
return;
}
if (!is_connected) {
return;
}
is_connected = false;
std::unique_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = false;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Disconnected, false);
return;
}
if (!is_connected) {
return;
}
is_connected = false;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Disconnected, true);
}
bool EmulatedController::IsConnected(bool get_temporary_value) const {
std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_is_connected;
}
@@ -1029,10 +1049,12 @@ bool EmulatedController::IsVibrationEnabled() const {
}
NpadIdType EmulatedController::GetNpadIdType() const {
std::scoped_lock lock{mutex};
return npad_id_type;
}
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_npad_type;
}
@@ -1040,27 +1062,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
}
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
{
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
if (is_configuring) {
if (tmp_npad_type == npad_type_) {
return;
}
tmp_npad_type = npad_type_;
TriggerOnChange(ControllerTriggerType::Type, false);
if (is_configuring) {
if (tmp_npad_type == npad_type_) {
return;
}
if (npad_type == npad_type_) {
return;
}
if (is_connected) {
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
NpadIdTypeToIndex(npad_id_type));
}
npad_type = npad_type_;
tmp_npad_type = npad_type_;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Type, false);
return;
}
if (npad_type == npad_type_) {
return;
}
if (is_connected) {
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
NpadIdTypeToIndex(npad_id_type));
}
npad_type = npad_type_;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Type, true);
}
@@ -1088,30 +1111,37 @@ LedPattern EmulatedController::GetLedPattern() const {
}
ButtonValues EmulatedController::GetButtonsValues() const {
std::scoped_lock lock{mutex};
return controller.button_values;
}
SticksValues EmulatedController::GetSticksValues() const {
std::scoped_lock lock{mutex};
return controller.stick_values;
}
TriggerValues EmulatedController::GetTriggersValues() const {
std::scoped_lock lock{mutex};
return controller.trigger_values;
}
ControllerMotionValues EmulatedController::GetMotionValues() const {
std::scoped_lock lock{mutex};
return controller.motion_values;
}
ColorValues EmulatedController::GetColorsValues() const {
std::scoped_lock lock{mutex};
return controller.color_values;
}
BatteryValues EmulatedController::GetBatteryValues() const {
std::scoped_lock lock{mutex};
return controller.battery_values;
}
HomeButtonState EmulatedController::GetHomeButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1119,6 +1149,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
}
CaptureButtonState EmulatedController::GetCaptureButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1126,6 +1157,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
}
NpadButtonState EmulatedController::GetNpadButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1133,6 +1165,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
}
DebugPadButton EmulatedController::GetDebugPadButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1140,20 +1173,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
}
AnalogSticks EmulatedController::GetSticks() const {
std::unique_lock lock{mutex};
if (is_configuring) {
return {};
}
// Some drivers like stick from buttons need constant refreshing
for (auto& device : stick_devices) {
if (!device) {
continue;
}
lock.unlock();
device->SoftUpdate();
lock.lock();
}
return controller.analog_stick_state;
}
NpadGcTriggerState EmulatedController::GetTriggers() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1161,26 +1201,35 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
}
MotionState EmulatedController::GetMotions() const {
std::unique_lock lock{mutex};
// Some drivers like mouse motion need constant refreshing
if (force_update_motion) {
for (auto& device : motion_devices) {
if (!device) {
continue;
}
lock.unlock();
device->ForceUpdate();
lock.lock();
}
}
return controller.motion_state;
}
ControllerColors EmulatedController::GetColors() const {
std::scoped_lock lock{mutex};
return controller.colors_state;
}
BatteryLevelState EmulatedController::GetBattery() const {
std::scoped_lock lock{mutex};
return controller.battery_state;
}
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ControllerUpdateCallback& poller = poller_pair.second;
if (!is_npad_service_update && poller.is_npad_service) {
@@ -1193,13 +1242,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
}
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
std::lock_guard lock{mutex};
std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedController::DeleteCallback(int key) {
std::lock_guard lock{mutex};
std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);

View File

@@ -400,7 +400,7 @@ private:
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
NpadIdType npad_id_type;
const NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
@@ -434,6 +434,7 @@ private:
StickDevices tas_stick_devices;
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0;

View File

@@ -169,7 +169,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
if (index >= device_status.keyboard_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_values[index];
@@ -201,6 +201,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
}
if (is_configuring) {
lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
return;
}
@@ -208,6 +209,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
// Index should be converted from NativeKeyboard to KeyboardKeyIndex
UpdateKey(index, current_status.value);
lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
}
@@ -227,7 +229,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
if (index >= device_status.keyboard_moddifier_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_moddifier_values[index];
@@ -259,6 +261,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
}
if (is_configuring) {
lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
return;
}
@@ -289,6 +292,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
break;
}
lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
}
@@ -297,7 +301,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
if (index >= device_status.mouse_button_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.mouse_button_values[index];
@@ -329,6 +333,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
}
if (is_configuring) {
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -351,6 +356,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
break;
}
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
@@ -359,13 +365,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
if (index >= device_status.mouse_analog_values.size()) {
return;
}
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
const auto analog_value = TransformToAnalog(callback);
device_status.mouse_analog_values[index] = analog_value;
if (is_configuring) {
device_status.mouse_position_state = {};
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -379,17 +386,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
break;
}
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
std::lock_guard lock{mutex};
std::unique_lock lock{mutex};
const auto touch_value = TransformToTouch(callback);
device_status.mouse_stick_value = touch_value;
if (is_configuring) {
device_status.mouse_position_state = {};
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -397,42 +406,52 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
device_status.mouse_position_state.x = touch_value.x.value;
device_status.mouse_position_state.y = touch_value.y.value;
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
KeyboardValues EmulatedDevices::GetKeyboardValues() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_values;
}
KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_values;
}
MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
std::scoped_lock lock{mutex};
return device_status.mouse_button_values;
}
KeyboardKey EmulatedDevices::GetKeyboard() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_state;
}
KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_state;
}
MouseButton EmulatedDevices::GetMouseButtons() const {
std::scoped_lock lock{mutex};
return device_status.mouse_button_state;
}
MousePosition EmulatedDevices::GetMousePosition() const {
std::scoped_lock lock{mutex};
return device_status.mouse_position_state;
}
AnalogStickState EmulatedDevices::GetMouseWheel() const {
std::scoped_lock lock{mutex};
return device_status.mouse_wheel_state;
}
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const InterfaceUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
@@ -442,13 +461,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
}
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
std::lock_guard lock{mutex};
std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedDevices::DeleteCallback(int key) {
std::lock_guard lock{mutex};
std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);

View File

@@ -200,6 +200,7 @@ private:
MouseStickDevice mouse_stick_device;
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
std::unordered_map<int, InterfaceUpdateCallback> callback_list;
int last_callback_key = 0;

View File

@@ -148,9 +148,9 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
} // Anonymous namespace
u64 KSystemControl::GenerateRandomU64() {
static std::random_device device;
static std::mt19937 gen(device());
static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return distribution(gen);
}

View File

@@ -24,8 +24,15 @@
namespace Kernel {
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_)
: kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {}
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
ServiceThreadType thread_type)
: kernel{kernel_} {
if (thread_type == ServiceThreadType::CreateNew) {
service_thread = kernel.CreateServiceThread(service_name_);
} else {
service_thread = kernel.GetDefaultServiceThread();
}
}
SessionRequestHandler::~SessionRequestHandler() {
kernel.ReleaseServiceThread(service_thread);
@@ -44,7 +51,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
return false;
}
return DomainHandler(object_id - 1).lock() != nullptr;
return !DomainHandler(object_id - 1).expired();
} else {
return session_handler != nullptr;
}
@@ -52,6 +59,9 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
void SessionRequestHandler::ClientConnected(KServerSession* session) {
session->ClientConnected(shared_from_this());
// Ensure our server session is tracked globally.
kernel.RegisterServerObject(session);
}
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {

View File

@@ -33,6 +33,11 @@ namespace Service {
class ServiceFrameworkBase;
}
enum class ServiceThreadType {
Default,
CreateNew,
};
namespace Kernel {
class Domain;
@@ -57,7 +62,8 @@ enum class ThreadWakeupReason;
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
SessionRequestHandler(KernelCore& kernel, const char* service_name_);
SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
ServiceThreadType thread_type);
virtual ~SessionRequestHandler();
/**

View File

@@ -89,9 +89,7 @@ public:
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
RegisterWithKernel();
}
virtual ~KAutoObject() {
UnregisterWithKernel();
}
virtual ~KAutoObject() = default;
static KAutoObject* Create(KAutoObject* ptr);
@@ -163,11 +161,12 @@ public:
do {
ASSERT(cur_ref_count > 0);
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
std::memory_order_relaxed));
std::memory_order_acq_rel));
// If ref count hits zero, destroy the object.
if (cur_ref_count - 1 == 0) {
this->Destroy();
this->UnregisterWithKernel();
}
}

View File

@@ -35,9 +35,14 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
R_TRY(page_table.LockForCodeMemory(addr, size))
// Clear the memory.
for (const auto& block : m_page_group.Nodes()) {
std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
}
//
// FIXME: this ends up clobbering address ranges outside the scope of the mapping within
// guest memory, and is not specifically required if the guest program is correctly
// written, so disable until this is further investigated.
//
// for (const auto& block : m_page_group.Nodes()) {
// std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
// }
// Set remaining tracking members.
m_address = addr;

View File

@@ -346,7 +346,8 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::
return ResultSuccess;
}
ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
ICacheInvalidationStrategy icache_invalidation_strategy) {
// Validate the mapping request.
R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
ResultInvalidMemoryRegion);
@@ -396,7 +397,11 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std
bool reprotected_pages = false;
SCOPE_EXIT({
if (reprotected_pages && any_code_pages) {
system.InvalidateCpuInstructionCacheRange(dst_address, size);
if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
system.InvalidateCpuInstructionCacheRange(dst_address, size);
} else {
system.InvalidateCpuInstructionCaches();
}
}
});
@@ -563,6 +568,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None);
system.InvalidateCpuInstructionCaches();
return ResultSuccess;
}

View File

@@ -26,6 +26,8 @@ class KMemoryBlockManager;
class KPageTable final {
public:
enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
@@ -38,7 +40,8 @@ public:
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
KMemoryPermission perm);
ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
ICacheInvalidationStrategy icache_invalidation_strategy);
ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
VAddr src_addr);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);

View File

@@ -422,7 +422,7 @@ private:
bool is_64bit_process = true;
/// Total running time for the process in ticks.
u64 total_process_running_time_ticks = 0;
std::atomic<u64> total_process_running_time_ticks = 0;
/// Per-process handle table for storing created object handles in.
KHandleTable handle_table;

View File

@@ -4,6 +4,7 @@
#pragma once
#include <atomic>
#include "common/assert.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
@@ -75,7 +76,7 @@ private:
KernelCore& kernel;
KAlignedSpinLock spin_lock{};
s32 lock_count{};
KThread* owner_thread{};
std::atomic<KThread*> owner_thread{};
};
} // namespace Kernel

View File

@@ -62,6 +62,12 @@ void KServerPort::Destroy() {
// Close our reference to our parent.
parent->Close();
// Release host emulation members.
session_handler.reset();
// Ensure that the global list tracking server objects does not hold on to a reference.
kernel.UnregisterServerObject(this);
}
bool KServerPort::IsSignaled() const {

View File

@@ -49,6 +49,9 @@ void KServerSession::Destroy() {
// Release host emulation members.
manager.reset();
// Ensure that the global list tracking server objects does not hold on to a reference.
kernel.UnregisterServerObject(this);
}
void KServerSession::OnClientClosed() {

View File

@@ -723,7 +723,7 @@ void KThread::UpdateState() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Set our suspend flags in state.
const auto old_state = thread_state;
const ThreadState old_state = thread_state;
const auto new_state =
static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
thread_state = new_state;
@@ -738,7 +738,7 @@ void KThread::Continue() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Clear our suspend flags in state.
const auto old_state = thread_state;
const ThreadState old_state = thread_state;
thread_state = old_state & ThreadState::Mask;
// Note the state change in scheduler.

View File

@@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <atomic>
#include <span>
#include <string>
#include <utility>
@@ -751,7 +752,7 @@ private:
KAffinityMask original_physical_affinity_mask{};
s32 original_physical_ideal_core_id{};
s32 num_core_migration_disables{};
ThreadState thread_state{};
std::atomic<ThreadState> thread_state{};
std::atomic<bool> termination_requested{};
bool wait_cancelled{};
bool cancellable{};

View File

@@ -61,6 +61,7 @@ struct KernelCore::Impl {
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
global_handle_table->Initialize(KHandleTable::MaxTableSize);
default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
is_phantom_mode_for_singlecore = false;
@@ -84,7 +85,7 @@ struct KernelCore::Impl {
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
cores[core_id].Initialize(current_process->Is64BitProcess());
cores[core_id].Initialize((*current_process).Is64BitProcess());
system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
@@ -95,15 +96,15 @@ struct KernelCore::Impl {
process_list.clear();
// Close all open server ports.
std::unordered_set<KServerPort*> server_ports_;
// Close all open server sessions and ports.
std::unordered_set<KAutoObject*> server_objects_;
{
std::lock_guard lk(server_ports_lock);
server_ports_ = server_ports;
server_ports.clear();
std::scoped_lock lk(server_objects_lock);
server_objects_ = server_objects;
server_objects.clear();
}
for (auto* server_port : server_ports_) {
server_port->Close();
for (auto* server_object : server_objects_) {
server_object->Close();
}
// Ensures all service threads gracefully shutdown.
@@ -156,7 +157,7 @@ struct KernelCore::Impl {
// Close kernel objects that were not freed on shutdown
{
std::lock_guard lk(registered_in_use_objects_lock);
std::scoped_lock lk{registered_in_use_objects_lock};
if (registered_in_use_objects.size()) {
for (auto& object : registered_in_use_objects) {
object->Close();
@@ -167,17 +168,17 @@ struct KernelCore::Impl {
// Shutdown all processes.
if (current_process) {
current_process->Finalize();
(*current_process).Finalize();
// current_process->Close();
// TODO: The current process should be destroyed based on accurate ref counting after
// calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
current_process->Destroy();
(*current_process).Destroy();
current_process = nullptr;
}
// Track kernel objects that were not freed on shutdown
{
std::lock_guard lk(registered_objects_lock);
std::scoped_lock lk{registered_objects_lock};
if (registered_objects.size()) {
LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
registered_objects.size());
@@ -658,13 +659,20 @@ struct KernelCore::Impl {
}
KClientPort* port = &search->second(system.ServiceManager(), system);
{
std::lock_guard lk(server_ports_lock);
server_ports.insert(&port->GetParent()->GetServerPort());
}
RegisterServerObject(&port->GetParent()->GetServerPort());
return port;
}
void RegisterServerObject(KAutoObject* server_object) {
std::scoped_lock lk(server_objects_lock);
server_objects.insert(server_object);
}
void UnregisterServerObject(KAutoObject* server_object) {
std::scoped_lock lk(server_objects_lock);
server_objects.erase(server_object);
}
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
const std::string& name) {
auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
@@ -677,6 +685,12 @@ struct KernelCore::Impl {
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
if (auto strong_ptr = service_thread.lock()) {
if (strong_ptr == default_service_thread.lock()) {
// Nothing to do here, the service is using default_service_thread, which will be
// released on shutdown.
return;
}
service_threads_manager.QueueWork(
[this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
}
@@ -686,7 +700,7 @@ struct KernelCore::Impl {
service_threads_manager.QueueWork([this]() { service_threads.clear(); });
}
std::mutex server_ports_lock;
std::mutex server_objects_lock;
std::mutex registered_objects_lock;
std::mutex registered_in_use_objects_lock;
@@ -697,7 +711,7 @@ struct KernelCore::Impl {
// Lists all processes that exist in the current session.
std::vector<KProcess*> process_list;
KProcess* current_process{};
std::atomic<KProcess*> current_process{};
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
Kernel::TimeManager time_manager;
@@ -716,7 +730,7 @@ struct KernelCore::Impl {
/// the ConnectToPort SVC.
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
NamedPortTable named_ports;
std::unordered_set<KServerPort*> server_ports;
std::unordered_set<KAutoObject*> server_objects;
std::unordered_set<KAutoObject*> registered_objects;
std::unordered_set<KAutoObject*> registered_in_use_objects;
@@ -739,7 +753,8 @@ struct KernelCore::Impl {
std::unique_ptr<KMemoryLayout> memory_layout;
// Threads used for services
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
std::weak_ptr<ServiceThread> default_service_thread;
Common::ThreadWorker service_threads_manager;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
@@ -920,23 +935,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
return impl->CreateNamedServicePort(std::move(name));
}
void KernelCore::RegisterServerObject(KAutoObject* server_object) {
impl->RegisterServerObject(server_object);
}
void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
impl->UnregisterServerObject(server_object);
}
void KernelCore::RegisterKernelObject(KAutoObject* object) {
std::lock_guard lk(impl->registered_objects_lock);
std::scoped_lock lk{impl->registered_objects_lock};
impl->registered_objects.insert(object);
}
void KernelCore::UnregisterKernelObject(KAutoObject* object) {
std::lock_guard lk(impl->registered_objects_lock);
std::scoped_lock lk{impl->registered_objects_lock};
impl->registered_objects.erase(object);
}
void KernelCore::RegisterInUseObject(KAutoObject* object) {
std::lock_guard lk(impl->registered_in_use_objects_lock);
std::scoped_lock lk{impl->registered_in_use_objects_lock};
impl->registered_in_use_objects.insert(object);
}
void KernelCore::UnregisterInUseObject(KAutoObject* object) {
std::lock_guard lk(impl->registered_in_use_objects_lock);
std::scoped_lock lk{impl->registered_in_use_objects_lock};
impl->registered_in_use_objects.erase(object);
}
@@ -1065,6 +1088,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
return impl->CreateServiceThread(*this, name);
}
std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
return impl->default_service_thread;
}
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
impl->ReleaseServiceThread(service_thread);
}

View File

@@ -195,6 +195,14 @@ public:
/// Opens a port to a service previously registered with RegisterNamedService.
KClientPort* CreateNamedServicePort(std::string name);
/// Registers a server session or port with the gobal emulation state, to be freed on shutdown.
/// This is necessary because we do not emulate processes for HLE sessions and ports.
void RegisterServerObject(KAutoObject* server_object);
/// Unregisters a server session or port previously registered with RegisterServerSession when
/// it was destroyed during the current emulation session.
void UnregisterServerObject(KAutoObject* server_object);
/// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown.
void RegisterKernelObject(KAutoObject* object);
@@ -271,15 +279,25 @@ public:
void ExitSVCProfile();
/**
* Creates an HLE service thread, which are used to execute service routines asynchronously.
* While these are allocated per ServerSession, these need to be owned and managed outside
* of ServerSession to avoid a circular dependency.
* Creates a host thread to execute HLE service requests, which are used to execute service
* routines asynchronously. While these are allocated per ServerSession, these need to be owned
* and managed outside of ServerSession to avoid a circular dependency. In general, most
* services can just use the default service thread, and not need their own host service thread.
* See GetDefaultServiceThread.
* @param name String name for the ServerSession creating this thread, used for debug
* purposes.
* @returns The a weak pointer newly created service thread.
*/
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
/**
* Gets the default host service thread, which executes HLE service requests. Unless service
* requests need to block on the host, the default service thread should be used in favor of
* creating a new service thread.
* @returns The a weak pointer for the default service thread.
*/
std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
/**
* Releases a HLE service thread, instructing KernelCore to free it. This should be called when
* the ServerSession associated with the thread is destroyed.

View File

@@ -58,6 +58,7 @@ bool PhysicalCore::IsInterrupted() const {
void PhysicalCore::Interrupt() {
guard->lock();
interrupts[core_index].SetInterrupt(true);
arm_interface->SignalInterrupt();
guard->unlock();
}

View File

@@ -1713,7 +1713,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ResultInvalidMemoryRegion;
}
return page_table.UnmapCodeMemory(dst_address, src_address, size);
return page_table.UnmapCodeMemory(dst_address, src_address, size,
KPageTable::ICacheInvalidationStrategy::InvalidateAll);
}
/// Exits the current process

View File

@@ -24,7 +24,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
}
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
if (nanoseconds > 0) {
ASSERT(thread);
ASSERT(thread->GetState() != ThreadState::Runnable);
@@ -35,7 +35,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
}
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
reinterpret_cast<uintptr_t>(thread));
}

View File

@@ -1337,7 +1337,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{200, nullptr, "GetLastApplicationExitReason"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
{1001, nullptr, "PrepareForJit"},
{1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
};
// clang-format on
@@ -1787,6 +1787,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
}
void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
Core::System& system) {
auto message_queue = std::make_shared<AppletMessageQueue>(system);

View File

@@ -336,6 +336,7 @@ private:
void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
void PrepareForJit(Kernel::HLERequestContext& ctx);
KernelHelpers::ServiceContext service_context;

View File

@@ -446,6 +446,14 @@ void WebBrowser::ExecuteLogin() {
}
void WebBrowser::ExecuteOffline() {
// TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
// Super Mario 3D All-Stars.
// TODO (Morph): Implement WebSession.
if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
LOG_WARNING(Service_AM, "WebSession is not implemented");
return;
}
const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
if (!Common::FS::Exists(main_url)) {

View File

@@ -41,9 +41,10 @@ public:
explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
AudioCore::AudioOut& audio_core_, std::string&& device_name_,
std::string&& unique_name)
: ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_},
device_name{std::move(device_name_)}, audio_params{audio_params_},
main_memory{system.Memory()}, service_context{system_, "IAudioOut"} {
: ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
audio_core{audio_core_}, device_name{std::move(device_name_)},
audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
"IAudioOut"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},

View File

@@ -24,7 +24,8 @@ public:
explicit IAudioRenderer(Core::System& system_,
const AudioCommon::AudioRendererParameter& audren_params,
const std::size_t instance_number)
: ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} {
: ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
service_context{system_, "IAudioRenderer"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},

View File

@@ -174,7 +174,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
"Could not write all of the bytes but everything else has succeded.");
"Could not write all of the bytes but everything else has succeeded.");
if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
// TODO(DarkLordZach): Find a better error code for this

View File

@@ -58,7 +58,8 @@ enum class FileSystemType : u8 {
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
: ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
: ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew},
backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Read, "Read"},
{1, nullptr, "Write"},
@@ -116,7 +117,8 @@ private:
class IFile final : public ServiceFramework<IFile> {
public:
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
: ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
: ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew},
backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"},
{1, &IFile::Write, "Write"},
@@ -252,7 +254,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
class IDirectory final : public ServiceFramework<IDirectory> {
public:
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
: ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
: ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew},
backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
@@ -308,8 +311,8 @@ private:
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
: ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
size_)} {
: ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew},
backend{std::move(backend_)}, size{std::move(size_)} {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
{1, &IFileSystem::DeleteFile, "DeleteFile"},

View File

@@ -262,11 +262,6 @@ void Controller_NPad::OnInit() {
service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
}
if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
// We want to support all controllers
hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
}
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(Core::HID::NpadIdType));
@@ -288,14 +283,6 @@ void Controller_NPad::OnInit() {
WriteEmptyEntry(npad);
}
}
// Connect controllers
for (auto& controller : controller_data) {
const auto& device = controller.device;
if (device->IsConnected()) {
AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
}
}
}
void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
@@ -320,6 +307,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
}
void Controller_NPad::OnRelease() {
is_controller_initialized = false;
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
service_context.CloseEvent(controller.styleset_changed_event);
@@ -330,7 +318,7 @@ void Controller_NPad::OnRelease() {
}
void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
auto& controller = GetControllerFromNpadIdType(npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
if (!controller.device->IsConnected()) {
@@ -651,9 +639,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
hid_core.SetSupportedStyleTag(style_set);
if (is_controller_initialized) {
return;
}
// Once SetSupportedStyleSet is called controllers are fully initialized
is_controller_initialized = true;
// Connect all active controllers
for (auto& controller : controller_data) {
const auto& device = controller.device;
if (device->IsConnected()) {
AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
}
}
}
Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
if (!is_controller_initialized) {
return {Core::HID::NpadStyleSet::None};
}
return hid_core.GetSupportedStyleTag();
}

View File

@@ -191,16 +191,16 @@ private:
// This is nn::hid::detail::NpadFullKeyColorState
struct NpadFullKeyColorState {
ColorAttribute attribute;
Core::HID::NpadControllerColor fullkey;
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor fullkey{};
};
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
// This is nn::hid::detail::NpadJoyColorState
struct NpadJoyColorState {
ColorAttribute attribute;
Core::HID::NpadControllerColor left;
Core::HID::NpadControllerColor right;
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor left{};
Core::HID::NpadControllerColor right{};
};
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
@@ -226,11 +226,11 @@ private:
// This is nn::hid::NpadPalmaState
// This is nn::hid::NpadSystemExtState
struct NPadGenericState {
s64_le sampling_number;
Core::HID::NpadButtonState npad_buttons;
Core::HID::AnalogStickState l_stick;
Core::HID::AnalogStickState r_stick;
NpadAttribute connection_status;
s64_le sampling_number{};
Core::HID::NpadButtonState npad_buttons{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
NpadAttribute connection_status{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -253,7 +253,7 @@ private:
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
SixAxisSensorAttribute attribute;
SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -325,11 +325,11 @@ private:
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
struct NfcXcdDeviceHandleStateImpl {
u64 handle;
bool is_available;
bool is_activated;
u64 handle{};
bool is_available{};
bool is_activated{};
INSERT_PADDING_BYTES(0x6); // Reserved
u64 sampling_number;
u64 sampling_number{};
};
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -366,8 +366,8 @@ private:
};
struct AppletFooterUi {
AppletFooterUiAttributes attributes;
AppletFooterUiType type;
AppletFooterUiAttributes attributes{};
AppletFooterUiType type{AppletFooterUiType::None};
INSERT_PADDING_BYTES(0x5B); // Reserved
};
static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -404,41 +404,41 @@ private:
// This is nn::hid::detail::NpadInternalState
struct NpadInternalState {
Core::HID::NpadStyleTag style_tag;
NpadJoyAssignmentMode assignment_mode;
NpadFullKeyColorState fullkey_color;
NpadJoyColorState joycon_color;
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
Lifo<NPadGenericState, hid_entry_count> palma_lifo;
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
DeviceType device_type;
Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
NpadFullKeyColorState fullkey_color{};
NpadJoyColorState joycon_color{};
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
DeviceType device_type{};
INSERT_PADDING_BYTES(0x4); // Reserved
NPadSystemProperties system_properties;
NpadSystemButtonProperties button_properties;
Core::HID::NpadBatteryLevel battery_level_dual;
Core::HID::NpadBatteryLevel battery_level_left;
Core::HID::NpadBatteryLevel battery_level_right;
NPadSystemProperties system_properties{};
NpadSystemButtonProperties button_properties{};
Core::HID::NpadBatteryLevel battery_level_dual{};
Core::HID::NpadBatteryLevel battery_level_left{};
Core::HID::NpadBatteryLevel battery_level_right{};
union {
Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
AppletFooterUi applet_footer;
AppletFooterUi applet_footer{};
Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
};
INSERT_PADDING_BYTES(0x20); // Unknown
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
NpadLarkType lark_type_l_and_main;
NpadLarkType lark_type_r;
NpadLuciaType lucia_type;
NpadLagonType lagon_type;
NpadLagerType lager_type;
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
NpadLarkType lark_type_l_and_main{};
NpadLarkType lark_type_r{};
NpadLuciaType lucia_type{};
NpadLagonType lagon_type{};
NpadLagerType lager_type{};
// FW 13.x Investigate there is some sort of bitflag related to joycons
INSERT_PADDING_BYTES(0x4);
INSERT_PADDING_BYTES(0xc08); // Unknown
@@ -511,7 +511,8 @@ private:
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
bool permit_vibration_session_enabled{false};
bool analog_stick_use_center_clamp{};
bool analog_stick_use_center_clamp{false};
bool is_in_lr_assignment_mode{false};
bool is_controller_initialized{false};
};
} // namespace Service::HID

View File

@@ -0,0 +1,332 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/symbols.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/jit/jit.h"
#include "core/hle/service/jit/jit_context.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
namespace Service::JIT {
struct CodeRange {
u64 offset;
u64 size;
};
class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
public:
explicit IJitEnvironment(Core::System& system_, CodeRange user_rx, CodeRange user_ro)
: ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew},
context{system_.Memory()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IJitEnvironment::GenerateCode, "GenerateCode"},
{1, &IJitEnvironment::Control, "Control"},
{1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
{1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
};
// clang-format on
RegisterHandlers(functions);
// Identity map user code range into sysmodule context
configuration.user_ro_memory = user_ro;
configuration.user_rx_memory = user_rx;
configuration.sys_ro_memory = user_ro;
configuration.sys_rx_memory = user_rx;
}
void GenerateCode(Kernel::HLERequestContext& ctx) {
struct Parameters {
u32 data_size;
u64 command;
CodeRange cr1;
CodeRange cr2;
Struct32 data;
};
IPC::RequestParser rp{ctx};
const auto parameters{rp.PopRaw<Parameters>()};
std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
const VAddr return_ptr{context.AddHeap(0u)};
const VAddr cr1_in_ptr{context.AddHeap(parameters.cr1)};
const VAddr cr2_in_ptr{context.AddHeap(parameters.cr2)};
const VAddr cr1_out_ptr{
context.AddHeap(CodeRange{.offset = parameters.cr1.offset, .size = 0})};
const VAddr cr2_out_ptr{
context.AddHeap(CodeRange{.offset = parameters.cr2.offset, .size = 0})};
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
const VAddr data_ptr{context.AddHeap(parameters.data)};
const VAddr configuration_ptr{context.AddHeap(configuration)};
context.CallFunction(callbacks.GenerateCode, return_ptr, cr1_out_ptr, cr2_out_ptr,
configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
cr1_in_ptr, cr2_in_ptr, data_ptr, parameters.data_size, output_ptr,
output_buffer.size());
const s32 return_value{context.GetHeap<s32>(return_ptr)};
if (return_value == 0) {
system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset,
configuration.user_rx_memory.size);
if (ctx.CanWriteBuffer()) {
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
}
const auto cr1_out{context.GetHeap<CodeRange>(cr1_out_ptr)};
const auto cr2_out{context.GetHeap<CodeRange>(cr2_out_ptr)};
IPC::ResponseBuilder rb{ctx, 8};
rb.Push(ResultSuccess);
rb.Push<u64>(return_value);
rb.PushRaw(cr1_out);
rb.PushRaw(cr2_out);
} else {
LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
}
};
void Control(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto command{rp.PopRaw<u64>()};
const auto input_buffer{ctx.ReadBuffer()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
const VAddr return_ptr{context.AddHeap(0u)};
const VAddr configuration_ptr{context.AddHeap(configuration)};
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
const u64 wrapper_value{
context.CallFunction(callbacks.Control, return_ptr, configuration_ptr, command,
input_ptr, input_buffer.size(), output_ptr, output_buffer.size())};
const s32 return_value{context.GetHeap<s32>(return_ptr)};
if (wrapper_value == 0 && return_value == 0) {
if (ctx.CanWriteBuffer()) {
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(return_value);
} else {
LOG_WARNING(Service_JIT, "plugin Control callback failed");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
}
}
void LoadPlugin(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto tmem_size{rp.PopRaw<u64>()};
if (tmem_size == 0) {
LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
const auto tmem_handle{ctx.GetCopyHandle(0)};
auto tmem{system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
tmem_handle)};
if (tmem.IsNull()) {
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
configuration.work_memory.offset = tmem->GetSourceAddress();
configuration.work_memory.size = tmem_size;
const auto nro_plugin{ctx.ReadBuffer(1)};
auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
const auto GetSymbol{[&](std::string name) { return symbols[name].first; }};
callbacks =
GuestCallbacks{.rtld_fini = GetSymbol("_fini"),
.rtld_init = GetSymbol("_init"),
.Control = GetSymbol("nnjitpluginControl"),
.ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"),
.SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"),
.Configure = GetSymbol("nnjitpluginConfigure"),
.GenerateCode = GetSymbol("nnjitpluginGenerateCode"),
.GetVersion = GetSymbol("nnjitpluginGetVersion"),
.Keeper = GetSymbol("nnjitpluginKeeper"),
.OnPrepared = GetSymbol("nnjitpluginOnPrepared")};
if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
callbacks.OnPrepared == 0) {
LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
if (!context.LoadNRO(nro_plugin)) {
LOG_ERROR(Service_JIT, "failed to load plugin");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
context.MapProcessMemory(configuration.sys_ro_memory.offset,
configuration.sys_ro_memory.size);
context.MapProcessMemory(configuration.sys_rx_memory.offset,
configuration.sys_rx_memory.size);
context.MapProcessMemory(configuration.work_memory.offset, configuration.work_memory.size);
if (callbacks.rtld_init != 0) {
context.CallFunction(callbacks.rtld_init);
}
const auto version{context.CallFunction(callbacks.GetVersion)};
if (version != 1) {
LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
const auto resolve{context.GetHelper("_resolve")};
if (callbacks.ResolveBasicSymbols != 0) {
context.CallFunction(callbacks.ResolveBasicSymbols, resolve);
}
const auto resolve_ptr{context.AddHeap(resolve)};
if (callbacks.SetupDiagnostics != 0) {
context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr);
}
context.CallFunction(callbacks.Configure, 0u);
const auto configuration_ptr{context.AddHeap(configuration)};
context.CallFunction(callbacks.OnPrepared, configuration_ptr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetCodeAddress(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.Push(configuration.user_rx_memory.offset);
rb.Push(configuration.user_ro_memory.offset);
}
private:
using Struct32 = std::array<u8, 32>;
struct GuestCallbacks {
VAddr rtld_fini;
VAddr rtld_init;
VAddr Control;
VAddr ResolveBasicSymbols;
VAddr SetupDiagnostics;
VAddr Configure;
VAddr GenerateCode;
VAddr GetVersion;
VAddr Keeper;
VAddr OnPrepared;
};
struct JITConfiguration {
CodeRange user_rx_memory;
CodeRange user_ro_memory;
CodeRange work_memory;
CodeRange sys_rx_memory;
CodeRange sys_ro_memory;
};
GuestCallbacks callbacks;
JITConfiguration configuration;
JITContext context;
};
class JITU final : public ServiceFramework<JITU> {
public:
explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
};
// clang-format on
RegisterHandlers(functions);
}
void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
struct Parameters {
u64 rx_size;
u64 ro_size;
};
IPC::RequestParser rp{ctx};
const auto parameters{rp.PopRaw<Parameters>()};
const auto executable_mem_handle{ctx.GetCopyHandle(1)};
const auto readable_mem_handle{ctx.GetCopyHandle(2)};
if (parameters.rx_size == 0 || parameters.ro_size == 0) {
LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
// The copy handle at index 0 is the process handle, but handle tables are
// per-process, so there is no point reading it here until we are multiprocess
const auto& process{*system.CurrentProcess()};
auto executable_mem{
process.GetHandleTable().GetObject<Kernel::KCodeMemory>(executable_mem_handle)};
if (executable_mem.IsNull()) {
LOG_ERROR(Service_JIT, "executable_mem is null for handle=0x{:08X}",
executable_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto readable_mem{
process.GetHandleTable().GetObject<Kernel::KCodeMemory>(readable_mem_handle)};
if (readable_mem.IsNull()) {
LOG_ERROR(Service_JIT, "readable_mem is null for handle=0x{:08X}", readable_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
const CodeRange user_rx{
.offset = executable_mem->GetSourceAddress(),
.size = parameters.rx_size,
};
const CodeRange user_ro{
.offset = readable_mem->GetSourceAddress(),
.size = parameters.ro_size,
};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IJitEnvironment>(system, user_rx, user_ro);
}
};
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<JITU>(system)->InstallAsService(sm);
}
} // namespace Service::JIT

View File

@@ -0,0 +1,20 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Core {
class System;
}
namespace Service::SM {
class ServiceManager;
}
namespace Service::JIT {
/// Registers all JIT services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
} // namespace Service::JIT

View File

@@ -0,0 +1,424 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <map>
#include <span>
#include <boost/icl/interval_set.hpp>
#include <dynarmic/interface/A64/a64.h>
#include <dynarmic/interface/A64/config.h>
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/div_ceil.h"
#include "common/logging/log.h"
#include "core/hle/service/jit/jit_context.h"
#include "core/memory.h"
namespace Service::JIT {
constexpr std::array<u8, 4> STOP_ARM64 = {
0x01, 0x00, 0x00, 0xd4, // svc #0
};
constexpr std::array<u8, 8> RESOLVE_ARM64 = {
0x21, 0x00, 0x00, 0xd4, // svc #1
0xc0, 0x03, 0x5f, 0xd6, // ret
};
constexpr std::array<u8, 4> PANIC_ARM64 = {
0x41, 0x00, 0x00, 0xd4, // svc #2
};
constexpr std::array<u8, 60> MEMMOVE_ARM64 = {
0x1f, 0x00, 0x01, 0xeb, // cmp x0, x1
0x83, 0x01, 0x00, 0x54, // b.lo #+34
0x42, 0x04, 0x00, 0xd1, // sub x2, x2, 1
0x22, 0x01, 0xf8, 0xb7, // tbnz x2, #63, #+36
0x23, 0x68, 0x62, 0x38, // ldrb w3, [x1, x2]
0x03, 0x68, 0x22, 0x38, // strb w3, [x0, x2]
0xfc, 0xff, 0xff, 0x17, // b #-16
0x24, 0x68, 0x63, 0x38, // ldrb w4, [x1, x3]
0x04, 0x68, 0x23, 0x38, // strb w4, [x0, x3]
0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
0x8b, 0xff, 0xff, 0x54, // b.lt #-16
0xc0, 0x03, 0x5f, 0xd6, // ret
0x03, 0x00, 0x80, 0xd2, // mov x3, 0
0xfc, 0xff, 0xff, 0x17, // b #-16
};
constexpr std::array<u8, 28> MEMSET_ARM64 = {
0x03, 0x00, 0x80, 0xd2, // mov x3, 0
0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
0x4b, 0x00, 0x00, 0x54, // b.lt #+8
0xc0, 0x03, 0x5f, 0xd6, // ret
0x01, 0x68, 0x23, 0x38, // strb w1, [x0, x3]
0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
0xfb, 0xff, 0xff, 0x17, // b #-20
};
struct HelperFunction {
const char* name;
const std::span<const u8> data;
};
constexpr std::array<HelperFunction, 6> HELPER_FUNCTIONS{{
{"_stop", STOP_ARM64},
{"_resolve", RESOLVE_ARM64},
{"_panic", PANIC_ARM64},
{"memcpy", MEMMOVE_ARM64},
{"memmove", MEMMOVE_ARM64},
{"memset", MEMSET_ARM64},
}};
struct Elf64_Dyn {
u64 d_tag;
u64 d_un;
};
struct Elf64_Rela {
u64 r_offset;
u64 r_info;
s64 r_addend;
};
static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
return static_cast<u32>(rela->r_info);
}
constexpr int DT_RELA = 7; /* Address of Rela relocs */
constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
constexpr size_t STACK_ALIGN = 16;
class JITContextImpl;
using IntervalSet = boost::icl::interval_set<VAddr>::type;
using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_,
IntervalSet& mapped_ranges_, JITContextImpl& parent_)
: memory{memory_}, local_memory{local_memory_},
mapped_ranges{mapped_ranges_}, parent{parent_} {}
u8 MemoryRead8(u64 vaddr) override {
return ReadMemory<u8>(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
return ReadMemory<u16>(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
return ReadMemory<u32>(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
return ReadMemory<u64>(vaddr);
}
u128 MemoryRead128(u64 vaddr) override {
return ReadMemory<u128>(vaddr);
}
std::string MemoryReadCString(u64 vaddr) {
std::string result;
u8 next;
while ((next = MemoryRead8(vaddr++)) != 0) {
result += next;
}
return result;
}
void MemoryWrite8(u64 vaddr, u8 value) override {
WriteMemory<u8>(vaddr, value);
}
void MemoryWrite16(u64 vaddr, u16 value) override {
WriteMemory<u16>(vaddr, value);
}
void MemoryWrite32(u64 vaddr, u32 value) override {
WriteMemory<u32>(vaddr, value);
}
void MemoryWrite64(u64 vaddr, u64 value) override {
WriteMemory<u64>(vaddr, value);
}
void MemoryWrite128(u64 vaddr, u128 value) override {
WriteMemory<u128>(vaddr, value);
}
bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override {
return WriteMemory<u8>(vaddr, value);
}
bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override {
return WriteMemory<u16>(vaddr, value);
}
bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override {
return WriteMemory<u32>(vaddr, value);
}
bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override {
return WriteMemory<u64>(vaddr, value);
}
bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override {
return WriteMemory<u128>(vaddr, value);
}
void CallSVC(u32 swi) override;
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override;
void InterpreterFallback(u64 pc, size_t num_instructions) override;
void AddTicks(u64 ticks) override {}
u64 GetTicksRemaining() override {
return std::numeric_limits<u32>::max();
}
u64 GetCNTPCT() override {
return 0;
}
template <class T>
T ReadMemory(u64 vaddr) {
T ret{};
if (boost::icl::contains(mapped_ranges, vaddr)) {
memory.ReadBlock(vaddr, &ret, sizeof(T));
} else if (vaddr + sizeof(T) > local_memory.size()) {
LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr);
} else {
std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T));
}
return ret;
}
template <class T>
bool WriteMemory(u64 vaddr, const T value) {
if (boost::icl::contains(mapped_ranges, vaddr)) {
memory.WriteBlock(vaddr, &value, sizeof(T));
} else if (vaddr + sizeof(T) > local_memory.size()) {
LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr);
} else {
std::memcpy(local_memory.data() + vaddr, &value, sizeof(T));
}
return true;
}
private:
Core::Memory::Memory& memory;
std::vector<u8>& local_memory;
IntervalSet& mapped_ranges;
JITContextImpl& parent;
};
class JITContextImpl {
public:
explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
callbacks =
std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this);
user_config.callbacks = callbacks.get();
jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
}
bool LoadNRO(std::span<const u8> data) {
local_memory.clear();
local_memory.insert(local_memory.end(), data.begin(), data.end());
if (FixupRelocations()) {
InsertHelperFunctions();
InsertStack();
return true;
} else {
return false;
}
}
bool FixupRelocations() {
const VAddr mod_offset{callbacks->MemoryRead32(4)};
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
return false;
}
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
VAddr rela_dyn = 0;
size_t num_rela = 0;
while (true) {
const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
dynamic_offset += sizeof(Elf64_Dyn);
if (!dyn.d_tag) {
break;
}
if (dyn.d_tag == DT_RELA) {
rela_dyn = dyn.d_un;
}
if (dyn.d_tag == DT_RELASZ) {
num_rela = dyn.d_un / sizeof(Elf64_Rela);
}
}
for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) {
continue;
}
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
}
return true;
}
void InsertHelperFunctions() {
for (const auto& [name, contents] : HELPER_FUNCTIONS) {
helpers[name] = local_memory.size();
local_memory.insert(local_memory.end(), contents.begin(), contents.end());
}
}
void InsertStack() {
const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -
local_memory.size()};
local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
top_of_stack = local_memory.size();
heap_pointer = top_of_stack;
}
void MapProcessMemory(VAddr dest_address, std::size_t size) {
mapped_ranges.add(IntervalType{dest_address, dest_address + size});
}
void PushArgument(const void* data, size_t size) {
const size_t num_words = Common::DivCeil(size, sizeof(u64));
const size_t current_pos = argument_stack.size();
argument_stack.insert(argument_stack.end(), num_words, 0);
std::memcpy(argument_stack.data() + current_pos, data, size);
}
void SetupArguments() {
for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
jit->SetRegister(i, argument_stack[i]);
}
if (argument_stack.size() > 8) {
const VAddr new_sp = Common::AlignDown(
top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
for (size_t i = 8; i < argument_stack.size(); i++) {
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
}
jit->SetSP(new_sp);
}
argument_stack.clear();
heap_pointer = top_of_stack;
}
u64 CallFunction(VAddr func) {
jit->SetRegister(30, helpers["_stop"]);
jit->SetSP(top_of_stack);
SetupArguments();
jit->SetPC(func);
jit->Run();
return jit->GetRegister(0);
}
VAddr GetHelper(const std::string& name) {
return helpers[name];
}
VAddr AddHeap(const void* data, size_t size) {
const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)};
if (heap_pointer + num_bytes > local_memory.size()) {
local_memory.insert(local_memory.end(),
(heap_pointer + num_bytes) - local_memory.size(), 0);
}
const VAddr location{heap_pointer};
std::memcpy(local_memory.data() + location, data, size);
heap_pointer += num_bytes;
return location;
}
void GetHeap(VAddr location, void* data, size_t size) {
std::memcpy(data, local_memory.data() + location, size);
}
std::unique_ptr<DynarmicCallbacks64> callbacks;
std::vector<u8> local_memory;
std::vector<u64> argument_stack;
IntervalSet mapped_ranges;
Dynarmic::A64::UserConfig user_config;
std::unique_ptr<Dynarmic::A64::Jit> jit;
std::map<std::string, VAddr, std::less<>> helpers;
Core::Memory::Memory& memory;
VAddr top_of_stack;
VAddr heap_pointer;
};
void DynarmicCallbacks64::CallSVC(u32 swi) {
switch (swi) {
case 0:
parent.jit->HaltExecution();
break;
case 1: {
// X0 contains a char* for a symbol to resolve
std::string name{MemoryReadCString(parent.jit->GetRegister(0))};
const auto helper{parent.helpers[name]};
if (helper != 0) {
parent.jit->SetRegister(0, helper);
} else {
LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
parent.jit->SetRegister(0, parent.helpers["_panic"]);
}
break;
}
case 2:
default:
LOG_CRITICAL(Service_JIT, "plugin panicked!");
parent.jit->HaltExecution();
break;
}
}
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
parent.jit->HaltExecution();
}
void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) {
LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc);
parent.jit->HaltExecution();
}
JITContext::JITContext(Core::Memory::Memory& memory)
: impl{std::make_unique<JITContextImpl>(memory)} {}
JITContext::~JITContext() {}
bool JITContext::LoadNRO(std::span<const u8> data) {
return impl->LoadNRO(data);
}
void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) {
impl->MapProcessMemory(dest_address, size);
}
u64 JITContext::CallFunction(VAddr func) {
return impl->CallFunction(func);
}
void JITContext::PushArgument(const void* data, size_t size) {
impl->PushArgument(data, size);
}
VAddr JITContext::GetHelper(const std::string& name) {
return impl->GetHelper(name);
}
VAddr JITContext::AddHeap(const void* data, size_t size) {
return impl->AddHeap(data, size);
}
void JITContext::GetHeap(VAddr location, void* data, size_t size) {
impl->GetHeap(location, data, size);
}
} // namespace Service::JIT

View File

@@ -0,0 +1,65 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <span>
#include <string>
#include "common/common_types.h"
namespace Core::Memory {
class Memory;
}
namespace Service::JIT {
class JITContextImpl;
class JITContext {
public:
explicit JITContext(Core::Memory::Memory& memory);
~JITContext();
[[nodiscard]] bool LoadNRO(std::span<const u8> data);
void MapProcessMemory(VAddr dest_address, std::size_t size);
template <typename T, typename... Ts>
u64 CallFunction(VAddr func, T argument, Ts... rest) {
static_assert(std::is_trivially_copyable_v<T>);
PushArgument(&argument, sizeof(argument));
if constexpr (sizeof...(rest) > 0) {
return CallFunction(func, rest...);
} else {
return CallFunction(func);
}
}
u64 CallFunction(VAddr func);
VAddr GetHelper(const std::string& name);
template <typename T>
VAddr AddHeap(T argument) {
return AddHeap(&argument, sizeof(argument));
}
VAddr AddHeap(const void* data, size_t size);
template <typename T>
T GetHeap(VAddr location) {
static_assert(std::is_trivially_copyable_v<T>);
T result;
GetHeap(location, &result, sizeof(result));
return result;
}
void GetHeap(VAddr location, void* data, size_t size);
private:
std::unique_ptr<JITContextImpl> impl;
void PushArgument(const void* data, size_t size);
};
} // namespace Service::JIT

View File

@@ -389,8 +389,12 @@ public:
if (bss_size) {
auto block_guard = detail::ScopeExit([&] {
page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
page_table.UnmapCodeMemory(
addr + nro_size, bss_addr, bss_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
page_table.UnmapCodeMemory(
addr, nro_addr, nro_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
});
const ResultCode result{
@@ -570,17 +574,21 @@ public:
auto& page_table{system.CurrentProcess()->PageTable()};
if (info.bss_size != 0) {
CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size +
info.ro_size + info.data_size,
info.bss_address, info.bss_size));
CASCADE_CODE(page_table.UnmapCodeMemory(
info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
}
CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
info.src_addr + info.text_size + info.ro_size,
info.data_size));
CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
info.src_addr + info.text_size, info.ro_size));
CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
CASCADE_CODE(page_table.UnmapCodeMemory(
info.nro_address + info.text_size + info.ro_size,
info.src_addr + info.text_size + info.ro_size, info.data_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
CASCADE_CODE(page_table.UnmapCodeMemory(
info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
CASCADE_CODE(page_table.UnmapCodeMemory(
info.nro_address, info.src_addr, info.text_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
return ResultSuccess;
}

View File

@@ -26,7 +26,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
rb.Push<DeviceFD>(0);
rb.PushEnum(NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -230,7 +230,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
}
NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name)
: ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} {
: ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} {
static const FunctionInfo functions[] = {
{0, &NVDRV::Open, "Open"},
{1, &NVDRV::Ioctl1, "Ioctl"},

View File

@@ -21,7 +21,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
return Status::BadValue;
}
std::scoped_lock lock(mutex);
std::scoped_lock lock{mutex};
if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
if (status != Status::NoBufferAvailable) {
@@ -40,7 +40,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
}
Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
std::scoped_lock lock(mutex);
std::scoped_lock lock{mutex};
if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
status != Status::NoError) {

View File

@@ -18,9 +18,8 @@ BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
BufferQueueConsumer::~BufferQueueConsumer() = default;
Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
std::chrono::nanoseconds expected_present,
u64 max_frame_number) {
std::scoped_lock lock(core->mutex);
std::chrono::nanoseconds expected_present) {
std::scoped_lock lock{core->mutex};
// Check that the consumer doesn't currently have the maximum number of buffers acquired.
const s32 num_acquired_buffers{
@@ -50,12 +49,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
const auto& buffer_item{core->queue[1]};
// If dropping entry[0] would leave us with a buffer that the consumer is not yet ready
// for, don't drop it.
if (max_frame_number && buffer_item.frame_number > max_frame_number) {
break;
}
// If entry[1] is timely, drop entry[0] (and repeat).
const auto desired_present = buffer_item.timestamp;
if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
@@ -127,7 +120,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
std::shared_ptr<IProducerListener> listener;
{
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
// If the frame number has changed because the buffer has been reallocated, we can ignore
// this ReleaseBuffer for the old buffer.
@@ -187,7 +180,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -200,4 +193,39 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
return Status::NoError;
}
Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
if (out_slot_mask == nullptr) {
LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
return Status::BadValue;
}
std::scoped_lock lock{core->mutex};
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
}
u64 mask = 0;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
if (!slots[s].acquire_called) {
mask |= (1ULL << s);
}
}
// Remove from the mask queued buffers for which acquire has been called, since the consumer
// will not receive their buffer addresses and so must retain their cached information
auto current(core->queue.begin());
while (current != core->queue.end()) {
if (current->acquire_called) {
mask &= ~(1ULL << current->slot);
}
++current;
}
LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
*out_slot_mask = mask;
return Status::NoError;
}
} // namespace Service::android

View File

@@ -24,10 +24,10 @@ public:
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
~BufferQueueConsumer();
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present,
u64 max_frame_number = 0);
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
Status GetReleasedBuffers(u64* out_slot_mask);
private:
std::shared_ptr<BufferQueueCore> core;

View File

@@ -15,7 +15,7 @@ BufferQueueCore::BufferQueueCore() = default;
BufferQueueCore::~BufferQueueCore() = default;
void BufferQueueCore::NotifyShutdown() {
std::scoped_lock lock(mutex);
std::scoped_lock lock{mutex};
is_shutting_down = true;
@@ -95,7 +95,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
}
void BufferQueueCore::FreeAllBuffersLocked() {
queue.clear();
buffer_has_been_queued = false;
for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {

View File

@@ -73,8 +73,6 @@ private:
u32 transform_hint{};
bool is_allocating{};
mutable std::condition_variable_any is_allocating_condition;
bool allow_allocation{true};
u64 buffer_age{};
bool is_shutting_down{};
};

View File

@@ -38,7 +38,7 @@ BufferQueueProducer::~BufferQueueProducer() {
Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -62,11 +62,12 @@ Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffe
Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
std::shared_ptr<IConsumerListener> listener;
std::shared_ptr<IConsumerListener> listener;
{
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
core->WaitWhileAllocatingLocked();
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
@@ -120,7 +121,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
}
Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
Status* returnFlags) const {
Status* return_flags) const {
bool try_again = true;
while (try_again) {
@@ -142,10 +143,12 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
ASSERT(slots[s].buffer_state == BufferState::Free);
if (slots[s].graphic_buffer != nullptr) {
core->FreeBufferLocked(s);
*returnFlags |= Status::ReleaseAllBuffers;
*return_flags |= Status::ReleaseAllBuffers;
}
}
// Look for a free buffer to give to the client
*found = BufferQueueCore::INVALID_BUFFER_SLOT;
s32 dequeued_count{};
s32 acquired_count{};
for (s32 s{}; s < max_buffer_count; ++s) {
@@ -233,70 +236,52 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
Status return_flags = Status::NoError;
bool attached_by_consumer = false;
{
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
core->WaitWhileAllocatingLocked();
if (format == PixelFormat::NoFormat) {
format = core->default_buffer_format;
}
// Enable the usage bits the consumer requested
usage |= core->consumer_usage_bit;
s32 found{};
Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
if (status != Status::NoError) {
return status;
}
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
LOG_ERROR(Service_NVFlinger, "no available buffer slots");
return Status::Busy;
}
*out_slot = found;
attached_by_consumer = slots[found].attached_by_consumer;
const bool use_default_size = !width && !height;
if (use_default_size) {
width = core->default_width;
height = core->default_height;
}
s32 found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
if (status != Status::NoError) {
return status;
}
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
LOG_DEBUG(Service_NVFlinger, "no available buffer slots");
return Status::Busy;
}
const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
// If we are not allowed to allocate new buffers, WaitForFreeSlotThenRelock must have
// returned a slot containing a buffer. If this buffer would require reallocation to
// meet the requested attributes, we free it and attempt to get another one.
if (!core->allow_allocation) {
if (buffer->NeedsReallocation(width, height, format, usage)) {
core->FreeBufferLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT;
continue;
}
}
}
*out_slot = found;
attached_by_consumer = slots[found].attached_by_consumer;
slots[found].buffer_state = BufferState::Dequeued;
const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
if ((buffer == nullptr) || buffer->NeedsReallocation(width, height, format, usage)) {
if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) ||
(buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) {
slots[found].acquire_called = false;
slots[found].graphic_buffer = nullptr;
slots[found].request_buffer_called = false;
slots[found].fence = Fence::NoFence();
core->buffer_age = 0;
return_flags |= Status::BufferNeedsReallocation;
} else {
// We add 1 because that will be the frame number when this buffer
// is queued
core->buffer_age = core->frame_counter + 1 - slots[found].frame_number;
}
LOG_DEBUG(Service_NVFlinger, "setting buffer age to {}", core->buffer_age);
*out_fence = slots[found].fence;
slots[found].fence = Fence::NoFence();
}
@@ -310,7 +295,8 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
}
{
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
@@ -327,13 +313,15 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
slots[*out_slot].frame_number, return_flags);
return return_flags;
}
Status BufferQueueProducer::DetachBuffer(s32 slot) {
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
@@ -368,8 +356,7 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out
return Status::BadValue;
}
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
core->WaitWhileAllocatingLocked();
if (core->is_abandoned) {
@@ -412,7 +399,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
return Status::BadValue;
}
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
core->WaitWhileAllocatingLocked();
Status return_flags = Status::NoError;
@@ -423,6 +410,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
return status;
}
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
LOG_ERROR(Service_NVFlinger, "No available buffer slots");
return Status::Busy;
@@ -466,13 +454,13 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
return Status::BadValue;
}
std::shared_ptr<IConsumerListener> frameAvailableListener;
std::shared_ptr<IConsumerListener> frameReplacedListener;
std::shared_ptr<IConsumerListener> frame_available_listener;
std::shared_ptr<IConsumerListener> frame_replaced_listener;
s32 callback_ticket{};
BufferItem item;
{
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -541,12 +529,13 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
item.fence = fence;
item.is_droppable = core->dequeue_buffer_cannot_block || async;
item.swap_interval = swap_interval;
sticky_transform = sticky_transform_;
if (core->queue.empty()) {
// When the queue is empty, we can simply queue this buffer
core->queue.push_back(item);
frameAvailableListener = core->consumer_listener;
frame_available_listener = core->consumer_listener;
} else {
// When the queue is not empty, we need to look at the front buffer
// state to see if we need to replace it
@@ -563,10 +552,10 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
}
// Overwrite the droppable buffer with the incoming one
*front = item;
frameReplacedListener = core->consumer_listener;
frame_replaced_listener = core->consumer_listener;
} else {
core->queue.push_back(item);
frameAvailableListener = core->consumer_listener;
frame_available_listener = core->consumer_listener;
}
}
@@ -587,15 +576,15 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
// Call back without the main BufferQueue lock held, but with the callback lock held so we can
// ensure that callbacks occur in order
{
std::scoped_lock lock(callback_mutex);
std::scoped_lock lock{callback_mutex};
while (callback_ticket != current_callback_ticket) {
callback_condition.wait(callback_mutex);
}
if (frameAvailableListener != nullptr) {
frameAvailableListener->OnFrameAvailable(item);
} else if (frameReplacedListener != nullptr) {
frameReplacedListener->OnFrameReplaced(item);
if (frame_available_listener != nullptr) {
frame_available_listener->OnFrameAvailable(item);
} else if (frame_replaced_listener != nullptr) {
frame_replaced_listener->OnFrameReplaced(item);
}
++current_callback_ticket;
@@ -608,7 +597,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -634,7 +623,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
}
Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
if (out_value == nullptr) {
LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
@@ -669,13 +658,6 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
case NativeWindow::ConsumerUsageBits:
value = core->consumer_usage_bit;
break;
case NativeWindow::BufferAge:
if (core->buffer_age > INT32_MAX) {
value = 0;
} else {
value = static_cast<u32>(core->buffer_age);
}
break;
default:
UNREACHABLE();
return Status::BadValue;
@@ -691,7 +673,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
NativeWindowApi api, bool producer_controlled_by_app,
QueueBufferOutput* output) {
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
producer_controlled_by_app);
@@ -737,7 +719,6 @@ Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& li
core->buffer_has_been_queued = false;
core->dequeue_buffer_cannot_block =
core->consumer_controlled_by_app && producer_controlled_by_app;
core->allow_allocation = true;
return status;
}
@@ -749,7 +730,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
std::shared_ptr<IConsumerListener> listener;
{
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
core->WaitWhileAllocatingLocked();
@@ -770,7 +751,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
core->SignalDequeueCondition();
buffer_wait_event->GetWritableEvent().Signal();
listener = core->consumer_listener;
} else if (core->connected_api != NativeWindowApi::NoConnectedApi) {
} else {
LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
core->connected_api, api);
status = Status::BadValue;
@@ -799,7 +780,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
return Status::BadValue;
}
std::scoped_lock lock(core->mutex);
std::scoped_lock lock{core->mutex};
slots[slot] = {};
slots[slot].graphic_buffer = buffer;

View File

@@ -66,7 +66,7 @@ public:
private:
BufferQueueProducer(const BufferQueueProducer&) = delete;
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* returnFlags) const;
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
Kernel::KEvent* buffer_wait_event{};
Service::KernelHelpers::ServiceContext& service_context;

View File

@@ -18,7 +18,7 @@ ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
: consumer{std::move(consumer_)} {}
ConsumerBase::~ConsumerBase() {
std::scoped_lock lock(mutex);
std::scoped_lock lock{mutex};
ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
}
@@ -36,38 +36,41 @@ void ConsumerBase::FreeBufferLocked(s32 slot_index) {
}
void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
std::scoped_lock lock(mutex);
LOG_DEBUG(Service_NVFlinger, "called");
}
void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
std::scoped_lock lock(mutex);
LOG_DEBUG(Service_NVFlinger, "called");
}
void ConsumerBase::OnBuffersReleased() {
std::scoped_lock lock(mutex);
std::scoped_lock lock{mutex};
LOG_DEBUG(Service_NVFlinger, "called");
if (is_abandoned) {
// Nothing to do if we're already abandoned.
return;
}
u64 mask = 0;
consumer->GetReleasedBuffers(&mask);
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
if (mask & (1ULL << i)) {
FreeBufferLocked(i);
}
}
}
void ConsumerBase::OnSidebandStreamChanged() {}
Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when,
u64 max_frame_number) {
if (is_abandoned) {
LOG_ERROR(Service_NVFlinger, "consumer is abandoned!");
return Status::NoInit;
}
Status err = consumer->AcquireBuffer(item, present_when, max_frame_number);
Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
Status err = consumer->AcquireBuffer(item, present_when);
if (err != Status::NoError) {
return err;
}
if (item->graphic_buffer != nullptr) {
if (slots[item->slot].graphic_buffer != nullptr) {
FreeBufferLocked(item->slot);
}
slots[item->slot].graphic_buffer = item->graphic_buffer;
}

View File

@@ -35,8 +35,7 @@ protected:
virtual void OnSidebandStreamChanged() override;
void FreeBufferLocked(s32 slot_index);
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when,
u64 max_frame_number = 0);
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,

View File

@@ -4,8 +4,6 @@
// Parts of this implementation were base on:
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
#pragma once
#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
#include "core/hle/service/nvflinger/parcel.h"

View File

@@ -14,7 +14,7 @@ HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
HosBinderDriverServer::~HosBinderDriverServer() {}
u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
std::lock_guard lk{lock};
std::scoped_lock lk{lock};
last_id++;
@@ -24,7 +24,7 @@ u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&&
}
android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
std::lock_guard lk{lock};
std::scoped_lock lk{lock};
if (auto search = producers.find(id); search != producers.end()) {
return search->second.get();

View File

@@ -104,7 +104,7 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
const auto lock_guard = Lock();
LOG_DEBUG(Service, "Opening \"{}\" display", name);
LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
const auto itr =
std::find_if(displays.begin(), displays.end(),
@@ -219,7 +219,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
auto* layer = display->FindLayer(layer_id);
if (layer == nullptr) {
LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id);
LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
CreateLayerAtId(*display, layer_id);
return display->FindLayer(layer_id);
}

View File

@@ -32,6 +32,7 @@
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/grc/grc.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/jit/jit.h"
#include "core/hle/service/lbl/lbl.h"
#include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/ldr/ldr.h"
@@ -91,8 +92,9 @@ namespace Service {
}
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
u32 max_sessions_, InvokerFn* handler_invoker_)
: SessionRequestHandler(system_.Kernel(), service_name_), system{system_},
ServiceThreadType thread_type, u32 max_sessions_,
InvokerFn* handler_invoker_)
: SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_},
service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
ServiceFrameworkBase::~ServiceFrameworkBase() {
@@ -261,6 +263,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
Glue::InstallInterfaces(system);
GRC::InstallInterfaces(*sm, system);
HID::InstallInterfaces(*sm, system);
JIT::InstallInterfaces(*sm, system);
LBL::InstallInterfaces(*sm, system);
LDN::InstallInterfaces(*sm, system);
LDR::InstallInterfaces(*sm, system);

View File

@@ -114,7 +114,8 @@ private:
Kernel::HLERequestContext& ctx);
explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
u32 max_sessions_, InvokerFn* handler_invoker_);
ServiceThreadType thread_type, u32 max_sessions_,
InvokerFn* handler_invoker_);
~ServiceFrameworkBase() override;
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
@@ -176,14 +177,17 @@ protected:
/**
* Initializes the handler with no functions installed.
*
* @param system_ The system context to construct this service under.
* @param system_ The system context to construct this service under.
* @param service_name_ Name of the service.
* @param max_sessions_ Maximum number of sessions that can be
* connected to this service at the same time.
* @param thread_type Specifies the thread type for this service. If this is set to CreateNew,
* it creates a new thread for it, otherwise this uses the default thread.
* @param max_sessions_ Maximum number of sessions that can be connected to this service at the
* same time.
*/
explicit ServiceFramework(Core::System& system_, const char* service_name_,
ServiceThreadType thread_type = ServiceThreadType::Default,
u32 max_sessions_ = ServerSessionCountMax)
: ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
: ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {}
/// Registers handlers in the service.
template <std::size_t N>

View File

@@ -153,7 +153,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
auto& port = port_result.Unwrap();
SCOPE_EXIT({ port->GetClientPort().Close(); });
server_ports.emplace_back(&port->GetServerPort());
kernel.RegisterServerObject(&port->GetServerPort());
// Create a new session.
Kernel::KClientSession* session{};
@@ -206,7 +206,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
}
SM::SM(ServiceManager& service_manager_, Core::System& system_)
: ServiceFramework{system_, "sm:", 4},
: ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4},
service_manager{service_manager_}, kernel{system_.Kernel()} {
RegisterHandlers({
{0, &SM::Initialize, "Initialize"},
@@ -224,10 +224,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
});
}
SM::~SM() {
for (auto& server_port : server_ports) {
server_port->Close();
}
}
SM::~SM() = default;
} // namespace Service::SM

View File

@@ -22,7 +22,6 @@ class KClientPort;
class KClientSession;
class KernelCore;
class KPort;
class KServerPort;
class SessionRequestHandler;
} // namespace Kernel
@@ -48,7 +47,6 @@ private:
ServiceManager& service_manager;
bool is_initialized{};
Kernel::KernelCore& kernel;
std::vector<Kernel::KServerPort*> server_ports;
};
class ServiceManager {

View File

@@ -689,6 +689,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
case OptName::REUSEADDR:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetReuseAddr(value != 0));
case OptName::KEEPALIVE:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetKeepAlive(value != 0));
case OptName::BROADCAST:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetBroadcast(value != 0));
@@ -837,7 +840,8 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
rb.PushEnum(bsd_errno);
}
BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
BSD::BSD(Core::System& system_, const char* name)
: ServiceFramework{system_, name, ServiceThreadType::CreateNew} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},

View File

@@ -2,8 +2,28 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string_view>
#include <utility>
#include <vector>
#include "common/string_util.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/sockets/sfdnsres.h"
#include "core/memory.h"
#ifdef _WIN32
#include <ws2tcpip.h>
#elif YUZU_UNIX
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#ifndef EAI_NODATA
#define EAI_NODATA EAI_NONAME
#endif
#endif
namespace Service::Sockets {
@@ -21,7 +41,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
{9, nullptr, "CancelRequest"},
{10, nullptr, "GetHostByNameRequestWithOptions"},
{11, nullptr, "GetHostByAddrRequestWithOptions"},
{12, nullptr, "GetAddrInfoRequestWithOptions"},
{12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
{13, nullptr, "GetNameInfoRequestWithOptions"},
{14, nullptr, "ResolverSetOptionRequest"},
{15, nullptr, "ResolverGetOptionRequest"},
@@ -31,7 +51,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
SFDNSRES::~SFDNSRES() = default;
void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
enum class NetDbError : s32 {
Internal = -1,
Success = 0,
HostNotFound = 1,
TryAgain = 2,
NoRecovery = 3,
NoData = 4,
};
static NetDbError AddrInfoErrorToNetDbError(s32 result) {
// Best effort guess to map errors
switch (result) {
case 0:
return NetDbError::Success;
case EAI_AGAIN:
return NetDbError::TryAgain;
case EAI_NODATA:
return NetDbError::NoData;
default:
return NetDbError::HostNotFound;
}
}
static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code,
std::string_view host) {
// Adapted from
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
std::vector<u8> data;
auto* current = addrinfo;
while (current != nullptr) {
struct SerializedResponseHeader {
u32 magic;
s32 flags;
s32 family;
s32 socket_type;
s32 protocol;
u32 address_length;
};
static_assert(sizeof(SerializedResponseHeader) == 0x18,
"Response header size must be 0x18 bytes");
constexpr auto header_size = sizeof(SerializedResponseHeader);
const auto addr_size =
current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4;
const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1;
const auto last_size = data.size();
data.resize(last_size + header_size + addr_size + canonname_size);
// Header in network byte order
SerializedResponseHeader header{};
constexpr auto HEADER_MAGIC = 0xBEEFCAFE;
header.magic = htonl(HEADER_MAGIC);
header.family = htonl(current->ai_family);
header.flags = htonl(current->ai_flags);
header.socket_type = htonl(current->ai_socktype);
header.protocol = htonl(current->ai_protocol);
header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
auto* header_ptr = data.data() + last_size;
std::memcpy(header_ptr, &header, header_size);
if (header.address_length == 0) {
std::memset(header_ptr + header_size, 0, 4);
} else {
switch (current->ai_family) {
case AF_INET: {
struct SockAddrIn {
s16 sin_family;
u16 sin_port;
u32 sin_addr;
u8 sin_zero[8];
};
SockAddrIn serialized_addr{};
const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
serialized_addr.sin_port = htons(addr.sin_port);
serialized_addr.sin_family = htons(addr.sin_family);
serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr);
std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn));
char addr_string_buf[64]{};
inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf));
LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf);
break;
}
case AF_INET6: {
struct SockAddrIn6 {
s16 sin6_family;
u16 sin6_port;
u32 sin6_flowinfo;
u8 sin6_addr[16];
u32 sin6_scope_id;
};
SockAddrIn6 serialized_addr{};
const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
serialized_addr.sin6_family = htons(addr.sin6_family);
serialized_addr.sin6_port = htons(addr.sin6_port);
serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id);
std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr,
sizeof(SockAddrIn6::sin6_addr));
std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6));
char addr_string_buf[64]{};
inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf));
LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf);
break;
}
default:
std::memcpy(header_ptr + header_size, current->ai_addr, addr_size);
break;
}
}
if (current->ai_canonname) {
std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size);
} else {
*(header_ptr + header_size + addr_size) = 0;
}
current = current->ai_next;
}
// 4-byte sentinel value
data.push_back(0);
data.push_back(0);
data.push_back(0);
data.push_back(0);
return data;
}
static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) {
struct Parameters {
u8 use_nsd_resolve;
u32 unknown;
@@ -42,11 +197,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
const auto parameters = rp.PopRaw<Parameters>();
LOG_WARNING(Service,
"(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}",
"called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}",
parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
const auto host_buffer = ctx.ReadBuffer(0);
const std::string host = Common::StringFromBuffer(host_buffer);
const auto service_buffer = ctx.ReadBuffer(1);
const std::string service = Common::StringFromBuffer(service_buffer);
addrinfo* addrinfo;
// Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now
s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo);
u32 data_size = 0;
if (result_code == 0 && addrinfo != nullptr) {
const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host);
data_size = static_cast<u32>(data.size());
freeaddrinfo(addrinfo);
ctx.WriteBuffer(data, 0);
}
return std::make_pair(data_size, result_code);
}
} // namespace Service::Sockets
void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
rb.Push(result_code); // errno
rb.Push(data_size); // serialized size
}
void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) {
// Additional options are ignored
auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push(data_size); // serialized size
rb.Push(result_code); // errno
rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
rb.Push(0);
}
} // namespace Service::Sockets

View File

@@ -19,6 +19,7 @@ public:
private:
void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Sockets

View File

@@ -46,6 +46,7 @@ enum class Protocol : u32 {
enum class OptName : u32 {
REUSEADDR = 0x4,
KEEPALIVE = 0x8,
BROADCAST = 0x20,
LINGER = 0x80,
SNDBUF = 0x1001,

View File

@@ -77,7 +77,8 @@ static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
public:
explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_)
: ServiceFramework{system_, "IHOSBinderDriver"}, server(server_) {
: ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew},
server(server_) {
static const FunctionInfo functions[] = {
{0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
{1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},

View File

@@ -600,6 +600,10 @@ Errno Socket::SetReuseAddr(bool enable) {
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
}
Errno Socket::SetKeepAlive(bool enable) {
return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0);
}
Errno Socket::SetBroadcast(bool enable) {
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
}

View File

@@ -67,6 +67,8 @@ public:
Errno SetReuseAddr(bool enable);
Errno SetKeepAlive(bool enable);
Errno SetBroadcast(bool enable);
Errno SetSndBuf(u32 value);

View File

@@ -53,13 +53,13 @@ PerfStats::~PerfStats() {
}
void PerfStats::BeginSystemFrame() {
std::lock_guard lock{object_mutex};
std::scoped_lock lock{object_mutex};
frame_begin = Clock::now();
}
void PerfStats::EndSystemFrame() {
std::lock_guard lock{object_mutex};
std::scoped_lock lock{object_mutex};
auto frame_end = Clock::now();
const auto frame_time = frame_end - frame_begin;
@@ -79,7 +79,7 @@ void PerfStats::EndGameFrame() {
}
double PerfStats::GetMeanFrametime() const {
std::lock_guard lock{object_mutex};
std::scoped_lock lock{object_mutex};
if (current_index <= IgnoreFrames) {
return 0;
@@ -91,7 +91,7 @@ double PerfStats::GetMeanFrametime() const {
}
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
std::lock_guard lock{object_mutex};
std::scoped_lock lock{object_mutex};
const auto now = Clock::now();
// Walltime elapsed since stats were reset
@@ -120,7 +120,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
}
double PerfStats::GetLastFrameTimeScale() const {
std::lock_guard lock{object_mutex};
std::scoped_lock lock{object_mutex};
constexpr double FRAME_LENGTH = 1.0 / 60;
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;

View File

@@ -80,7 +80,7 @@ bool Freezer::IsActive() const {
}
void Freezer::Clear() {
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
@@ -88,7 +88,7 @@ void Freezer::Clear() {
}
u64 Freezer::Freeze(VAddr address, u32 width) {
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
const auto current_value = MemoryReadWidth(memory, width, address);
entries.push_back({address, width, current_value});
@@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) {
}
void Freezer::Unfreeze(VAddr address) {
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
@@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) {
}
bool Freezer::IsFrozen(VAddr address) const {
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
return FindEntry(address) != entries.cend();
}
void Freezer::SetFrozenValue(VAddr address, u64 value) {
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
const auto iter = FindEntry(address);
@@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
}
std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
const auto iter = FindEntry(address);
@@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
}
std::vector<Freezer::Entry> Freezer::GetEntries() const {
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
return entries;
}
@@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
return;
}
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
for (const auto& entry : entries) {
LOG_DEBUG(Common_Memory,
@@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
}
void Freezer::FillEntryReads() {
std::lock_guard lock{entries_mutex};
std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");

View File

@@ -62,7 +62,7 @@ public:
bool UpdateMotion(SDL_ControllerSensorEvent event) {
constexpr float gravity_constant = 9.80665f;
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
const u64 time_difference = event.timestamp - last_motion_update;
last_motion_update = event.timestamp;
switch (event.sensor) {
@@ -241,7 +241,7 @@ private:
};
std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
std::lock_guard lock{joystick_map_mutex};
std::scoped_lock lock{joystick_map_mutex};
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
@@ -263,7 +263,7 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
std::scoped_lock lock{joystick_map_mutex};
const auto map_it = joystick_map.find(guid);
if (map_it == joystick_map.end()) {
@@ -297,7 +297,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
std::scoped_lock lock{joystick_map_mutex};
if (joystick_map.find(guid) == joystick_map.end()) {
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
PreSetController(joystick->GetPadIdentifier());
@@ -326,7 +326,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
std::scoped_lock lock{joystick_map_mutex};
// This call to guid is safe since the joystick is guaranteed to be in the map
const auto& joystick_guid_list = joystick_map[guid];
const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
@@ -392,7 +392,7 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
}
void SDLDriver::CloseJoysticks() {
std::lock_guard lock{joystick_map_mutex};
std::scoped_lock lock{joystick_map_mutex};
joystick_map.clear();
}

View File

@@ -8,37 +8,37 @@
namespace InputCommon {
void InputEngine::PreSetController(const PadIdentifier& identifier) {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
controller_list.try_emplace(identifier);
}
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
controller.buttons.try_emplace(button, false);
}
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
controller.hat_buttons.try_emplace(button, u8{0});
}
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
controller.axes.try_emplace(axis, 0.0f);
}
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
controller.motions.try_emplace(motion);
}
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
{
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.buttons.insert_or_assign(button, value);
@@ -49,7 +49,7 @@ void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool va
void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
{
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.hat_buttons.insert_or_assign(button, value);
@@ -60,7 +60,7 @@ void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 v
void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
{
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.axes.insert_or_assign(axis, value);
@@ -71,7 +71,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
{
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.battery = value;
@@ -82,7 +82,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
{
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
ControllerData& controller = controller_list.at(identifier);
if (!configuring) {
controller.motions.insert_or_assign(motion, value);
@@ -92,7 +92,7 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
}
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -109,7 +109,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
}
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -126,7 +126,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d
}
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -143,7 +143,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
}
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -155,7 +155,7 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
}
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
std::lock_guard lock{mutex};
std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
if (controller_iter == controller_list.cend()) {
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -186,7 +186,7 @@ void InputEngine::ResetAnalogState() {
}
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
std::lock_guard lock{mutex_callback};
std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
@@ -214,7 +214,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but
}
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
std::lock_guard lock{mutex_callback};
std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
@@ -243,7 +243,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int
}
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
std::lock_guard lock{mutex_callback};
std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
@@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
[[maybe_unused]] Common::Input::BatteryLevel value) {
std::lock_guard lock{mutex_callback};
std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
@@ -284,7 +284,7 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
const BasicMotion& value) {
std::lock_guard lock{mutex_callback};
std::scoped_lock lock{mutex_callback};
for (const auto& poller_pair : callback_list) {
const InputIdentifier& poller = poller_pair.second;
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
@@ -346,18 +346,18 @@ const std::string& InputEngine::GetEngineName() const {
}
int InputEngine::SetCallback(InputIdentifier input_identifier) {
std::lock_guard lock{mutex_callback};
std::scoped_lock lock{mutex_callback};
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
return last_callback_key++;
}
void InputEngine::SetMappingCallback(MappingCallback callback) {
std::lock_guard lock{mutex_callback};
std::scoped_lock lock{mutex_callback};
mapping_callback = std::move(callback);
}
void InputEngine::DeleteCallback(int key) {
std::lock_guard lock{mutex_callback};
std::scoped_lock lock{mutex_callback};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);

View File

@@ -35,6 +35,15 @@ std::string_view OutputVertexIndex(EmitContext& ctx) {
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
}
std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
if (binding.IsImmediate()) {
return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
} else {
const auto binding_var{ctx.var_alloc.Consume(binding)};
return fmt::format("GetCbufIndirect({},{})", binding_var, index);
}
}
void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
const IR::Value& offset, u32 num_bits, std::string_view cast = {},
std::string_view bit_offset = {}) {
@@ -55,8 +64,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
: fmt::format("[({}>>2)%4]", offset_var)};
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)};
const auto cbuf{ChooseCbuf(ctx, binding, index)};
const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
const auto extraction{num_bits == 32 ? cbuf_cast
: fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
bit_offset, num_bits)};
@@ -140,9 +149,9 @@ void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
if (offset.IsImmediate()) {
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
static constexpr u32 cbuf_size{0x10000};
const u32 u32_offset{offset.U32()};
const s32 signed_offset{static_cast<s32>(offset.U32())};
@@ -162,17 +171,17 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
return;
}
const auto offset_var{ctx.var_alloc.Consume(offset)};
const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
if (!ctx.profile.has_gl_component_indexing_bug) {
ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst,
cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var);
ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
offset_var, cast, cbuf, offset_var);
return;
}
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset,
swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var,
"xyzw"[(swizzle + 1) % 4]);
ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
}
}

View File

@@ -359,6 +359,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
header += "layout(location=0) uniform vec4 scaling;";
}
DefineConstantBuffers(bindings);
DefineConstantBufferIndirect();
DefineStorageBuffers(bindings);
SetupImages(bindings);
SetupTextures(bindings);
@@ -436,6 +437,24 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
}
}
void EmitContext::DefineConstantBufferIndirect() {
if (!info.uses_cbuf_indirect) {
return;
}
header += profile.has_gl_cbuf_ftou_bug ? "uvec4 " : "vec4 ";
header += "GetCbufIndirect(uint binding, uint offset){"
"switch(binding){"
"default:";
for (const auto& desc : info.constant_buffer_descriptors) {
header +=
fmt::format("case {}:return {}_cbuf{}[offset];", desc.index, stage_name, desc.index);
}
header += "}}";
}
void EmitContext::DefineStorageBuffers(Bindings& bindings) {
if (info.storage_buffers_descriptors.empty()) {
return;

View File

@@ -162,6 +162,7 @@ public:
private:
void SetupExtensions();
void DefineConstantBuffers(Bindings& bindings);
void DefineConstantBufferIndirect();
void DefineStorageBuffers(Bindings& bindings);
void DefineGenericOutput(size_t index, u32 invocations);
void DefineHelperFunctions();

View File

@@ -1043,15 +1043,15 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
const Id merge_label{OpLabel()};
const Id uniform_type{uniform_types.*member_ptr};
std::array<Id, Info::MAX_CBUFS> buf_labels;
std::array<Sirit::Literal, Info::MAX_CBUFS> buf_literals;
for (u32 i = 0; i < Info::MAX_CBUFS; i++) {
std::array<Id, Info::MAX_INDIRECT_CBUFS> buf_labels;
std::array<Sirit::Literal, Info::MAX_INDIRECT_CBUFS> buf_literals;
for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
buf_labels[i] = OpLabel();
buf_literals[i] = Sirit::Literal{i};
}
OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
OpSwitch(binding, buf_labels[0], buf_literals, buf_labels);
for (u32 i = 0; i < Info::MAX_CBUFS; i++) {
for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
AddLabel(buf_labels[i]);
const Id cbuf{cbufs[i].*member_ptr};
const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)};
@@ -1064,22 +1064,23 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
return func;
}};
IR::Type types{info.used_indirect_cbuf_types};
if (True(types & IR::Type::U8)) {
bool supports_aliasing = profile.support_descriptor_aliasing;
if (supports_aliasing && True(types & IR::Type::U8)) {
load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
}
if (True(types & IR::Type::U16)) {
if (supports_aliasing && True(types & IR::Type::U16)) {
load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
}
if (True(types & IR::Type::F32)) {
if (supports_aliasing && True(types & IR::Type::F32)) {
load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
}
if (True(types & IR::Type::U32)) {
if (supports_aliasing && True(types & IR::Type::U32)) {
load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32);
}
if (True(types & IR::Type::U32x2)) {
if (supports_aliasing && True(types & IR::Type::U32x2)) {
load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2);
}
if (True(types & IR::Type::U32x4)) {
if (!supports_aliasing || True(types & IR::Type::U32x4)) {
load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4);
}
}

View File

@@ -132,7 +132,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
multisample = v.X(meta_reg++);
}
if (tld.clamp != 0) {
throw NotImplementedException("TLD.CL - CLAMP is not implmented");
throw NotImplementedException("TLD.CL - CLAMP is not implemented");
}
IR::TextureInstInfo info{};
info.type.Assign(GetType(tld.type));

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