Compare commits

..

26 Commits

Author SHA1 Message Date
lizzie e7e8bd7b1a 2026-09-25 09:06:19
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 09:06:19 +00:00
lizzie 07c6eeaabd 2026-09-07 10:21:23
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 11:03:53 +02:00
lizzie 29293295d4 2026-09-06 23:31:33
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 11:03:53 +02:00
lizzie 2a37ed8e15 2026-09-06 22:10:34
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 11:03:53 +02:00
lizzie 9ba0d4a541 Trigger build 2026-09-25 11:03:53 +02:00
lizzie 150c2a53a8 2026-09-05 17:42:18
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 11:03:53 +02:00
lizzie 85eb750d2e 2026-09-05 17:17:08
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 11:03:53 +02:00
lizzie bebc19da32 [common] Use std::make_unique_for_overwrite<T> in ScratchBuffer, remove polyfill (#4432)
Original `make_unique_for_overwrite.h` is well defined acc. to standard https://en.cppreference.com/cpp/memory/unique_ptr/make_unique, but by now most libc++ supports the function, so no need for polyfill.

Test that this didn't break anything (for example, Megaman game that has video at the start), or anything using VIC/IPC.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4432
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-25 07:54:49 +02:00
PavelBARABANOV 99bf8cf51a [am, renderer_vulkan] Fix overlay darkening and SGSR black screen on applets (#4475)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
- Overlay applet: use IsOverlayOpenLocked to pick the Z-index, so the darkening background is only layered above the game while the overlay is open.
- Vulkan: skip the SGSR pass for applet layers to avoid presenting a black frame.
- Partial revert fix crashes in games on UE with the overlay applet enabled.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4475
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 05:29:08 +02:00
xbzk dbeb73ee01 [video_core] cpu buffer fix + kepler uploads / maxwell macro dirty tracking fixes (#4473)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Aimed to fix two known UE5 crashes: Kepler uploads and Maxwell macros, both caused by CPU/GPU races due dirty tracking issues.

Kepler ComputeInline: preserved dirty tracking across dma continuations and async readback.
Maxwell macros: preserved gpu owned subranges during page granular cpu uploads.
DiscardWrite: stopped clearing neighboring macro arguments by rounding up ranges.
DMA Step: improved continuation aware dirty sampling.

To the Ender Magnolia crew (maybe 1 or 2 persons): This will fix the dash crash, and the random / shackled beast vaper crashes.

There are some more UE5 issues to go next.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4473
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-24 18:44:14 +02:00
PavelBARABANOV cb73a4dcc7 [renderer_vulkan] Skip post-processing on applet layers (#4474)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Detect applet layers via the absence of the Recording bit in layer_stack_mask (only AppletId::Application sets it) and skip PostProcessChain creation and application for them.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4474
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-24 02:48:46 +02:00
lizzie 278f411dad [frontend] Unify cmdline parsing (#4424)
Addresses #4383.

Makes it so both the Qt and SDL frontends now share the same options (bar `-hlaunch` and `-qlaunch`).

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4424
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-23 14:51:51 +02:00
lizzie 3d37a816c6 [glsl] fix clang 23 build error for unused function SetDefinition (#4389)
Reported by @gixel

> unused function template SetDefinition  in: src/shader_recompiler/backend/glsl/emit_glsl.cpp:32:6: error: unused function template 'SetDefinition' [-Werror,-Wunused-template]

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4389
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-23 14:51:18 +02:00
lizzie a25e32676f [hle] Use correct audren:d service framework (#4472)
Trivial change.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4472
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-23 14:39:12 +02:00
Exverge 38df54edfe [common/sparse_large_vector] decommit unused pages + fix first page in zeroed region (#4471)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Fixes a bug where the first page of a zeroed out region would not be properly zeroed, and now frees unused memory.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4471
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-23 13:52:29 +02:00
PavelBARABANOV 3266c20e3f [ips_layer] read magic from IPS file and fix EOF check (#4469)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
PatchIPS was reading the magic bytes from the target file instead
of the IPS patch, and IsEOF used a strict > comparison that never
matched for 3-byte IPS EOF markers. This caused IPS patches to be
silently skipped, breaking the Russian localization mod for
Persona 5.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4469
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-23 00:36:47 +02:00
Eden CI 9cd24e85c2 [dist, android] Update translations from Transifex for Sep 21 (#4466)
Automatic translation update for Sep 21

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4466
2026-09-21 19:25:54 +02:00
xbzk 74b5e10dc5 [native] doesUpdateMatchProgram update mask fix (#4465)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Old silly bug i've been neglecting:
On Android, installing content to NAND, under some content id conditions (ie MK8D DLC), base/update IDs mismatch, and UI prompts "The content that you selected does not match this game. Install anyway?
Useless, yet fixed.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4465
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 22:56:06 +02:00
xbzk a277b62fe4 [fs] revert #4319 savedata_factory changes (#4462)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This one reverts part of the PR 4319 which was aimed to allow child titles to inherit bundle settings.
The fix seems good so far, but by applying it to save folders caused a trap:
Now child's save folders move to bundle folder, hence saves will vanish, and the android/qt save tools are still pointing to old folders.
Since the save part was only a compliance fix, instead of fiddle in master i'd rather revert this part now, then only touch this again if any bug show up.
No worries. I'll be right here waiting.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4462
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 04:40:14 +02:00
lizzie 908b1e9a37 [dynarmic] fix bad arm codegen on relocations (#4458)
In an earlier version I had these as constants which were referred to via `db`, I haphazardly left the LDR and didn't notice.
Even still relocations apply to only 1 ins at a time.

Fixes regressions on #4158

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4458
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 00:29:28 +02:00
xbzk 847e91c3a8 [applet] add post exit cleanups to frontend applets to avoid accumulation (#4457)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Maran reported MK8D if one press Plus button 38 times.
This could be impacting other games around, but 4442 already quenched the spontaneous accumulation cases, by avoiding multiple event signals. MK8D is an atypical induced example.
The reason was a controller applet accumulation, as we had no proper way to keep track and erase child applets on exit.
Now we have. Enjoy your Plus button rushing fetish!

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4457
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-19 17:44:12 +02:00
lizzie 2d1eb0dab9 [common/error] remove preprocessor macros for strerror_r gating (#4450)
We use C++, we can just use SFINAE for this.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4450
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
2026-09-19 08:21:39 +02:00
lizzie 9a30172e45 [dynarmic] Coalesce non-exclusive Write/Read fallback functions (#4158)
the general idea is to have a common procedure form whom to call
this way theres less "jumping around" for values of different sizes
additionally this **should** allow for better codegen since
most of the u8,u16,u32,u64 can be held within a u64
theoretically this means that you could deifne callbacks in suck
a way that it's essentially as costly as a `mov r64, m64`
but that's not doable due to the fact we have to do translations...

Is this a good change?
Primarily aimed for x86 and ARM to benefit, but I suppose LooooooongArch64 can benefit too.

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4158
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-19 01:39:46 +02:00
xbzk 07f40d5cac [npad,hid,am] npad WriteEmptyEntry fix, hid function 551 stub, and applet PopOutData rework (#4442)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
This was intended to fix Dave the Diver issue, but we found a real silly hid bug, then a maybe broader applet issue:

By testing with John it was found out only the last controller connected worked, the others caused the game to freeze peacefully, no crashes, suggesting some loop or deadlock.

After seeking that lead, it was found that the freeze was caused by a malformed HID history entry when the controller applet removed P2.
Pressing P1 opens the controller applet, then Qt applet disconnects P2, which is expected.

Upon seeking callstack and hid sync path, it was found out that NPad::WriteEmptyEntry:
was using: ReadCurrentEntry().sampling_number + 1
instead of: ReadCurrentEntry().state.sampling_number + 1 (checked AtomicStorage in src\hid_core\resources\ring_lifo.h)
Fixing that fixed the freeze, but killed the sound!

Later handheld/docked was accidentally clicked and sound returned. Good lead.
Then i just had to corner which part of the toggle was causing the refresh and do something similar at the controller applet return routine (PopOutData in library_applet_accessor.cpp). This part seems harmless, but i'm not clearly sure about how proper it is, since it was like a transplant and test operation.

UPDATE:
To try Diablo 3 on my machine it was needed to stub HID function 551.

And to fix all abnormalies like multiple applet requests and crash the function PopOutData was reworked to ensurey idempotency among different applet focus conditions:

focus state changed: updates state and signals if needed
no state change, HLE application frontend: requests focus state changed notification
already pending change message: flag remains true and no duplicate pushed to queue entry

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4442
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-18 23:49:17 +02:00
xbzk 3c38afad74 [fs] share parent resources with bundled programs (#4319)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Normal games are Applications;
Bundled games are Application + Programs;

We've been treating both as the same thing, calling both programs, and making programs unable to see settings and saves from application.

References:
https://switchbrew.org/wiki/Filesystem_services
https://switchbrew.org/wiki/NCM_services#ApplicationId
https://switchbrew.org/wiki/Super_Mario_3D_All-Stars
Ryujinx.HLE/HOS/ApplicationLoader.cs:GetMultiProgramInfo

UPDATE:

The first version was a big rework to make AppId/ProgId concepts proper, and it was successful in most tests, but it caused a regression with applets, which are programs that breed straight from no application.
In order to simplify things i've reverted all the rework, added a helper to identify whether a program has a parent Application and bounded it to it's proper stores.

Although this is no big deal coz it doesn't have impact on emulation, it permeates to several routines, so it needs to be tested broadly. Applets, Normal games, Bundled games, Blobbed NSPs/XCIs, Mods, Updates, Saves, DLCs,.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4319
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-18 23:48:35 +02:00
Feng Chen 0ce29be608 [common] Fix RDTSC nanosecond conversion (#4422)
Split the RDTSC ticks-per-nanosecond ratio into integer and Q0.64 fractional components. This avoids overflowing GetFixedPoint64Factor for TSC frequencies above 1 GHz while preserving the multiply-only conversion path.

Use the invariant clock only above 1 GHz because the reverse nanoseconds-per-tick factor cannot represent an exact ratio of one.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4422
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-17 19:50:19 +02:00
105 changed files with 16173 additions and 14758 deletions
+628 -583
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+548 -503
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+550 -508
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+573 -527
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+33 -21
View File
@@ -1,32 +1,44 @@
# User Handbook - Command Line # User Handbook - Command Line
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments. There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same command line arguments.
## eden
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application. - `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument. - `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
- `-f`: Use fullscreen. - `--config/-c`: Specify alternate configuration file.
- `-u <number>`: Select the index of the user to load as. - `--fullscreen/-f`: Use fullscreen.
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only). - `--help/-h`: Display help.
- `--game/-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
- `--multiplayer/-m`: Specify multiplayer options.
- `--program/-p`: Specify the program arguments to pass (optional).
- `--user/-u <number>`: Specify the user index.
- `--version/-v`: Display version and quit.
- `--input-profile/-i <name>`: Specifies input profile name to use (for player #0 only).
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
- `--filter/-x`: Sets the debug log filter irrespective of settings.
- `--singlecore/-s`: Forces single-core regardless of settings.
Only the Qt frontend supports the following arguments:
- `-qlaunch`: Launch QLaunch. - `-qlaunch`: Launch QLaunch.
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`. - `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
- Requires a copy of Atmosphere to be extracted onto `sdmc`. - Requires a copy of Atmosphere to be extracted onto `sdmc`.
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`. - This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
- `-setup`: Launch setup applet. - `-setup`: Launch setup applet.
## eden-cli `eden` (provided with `--room`), and `eden-room` supports the following options as well:
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234` - `-n/--room-name`: The name of the room.
- `--config/-c`: Specify alternate configuration file. - `-d/--room-description`: The room description.
- `--fullscreen/-f`: Set fullscreen. - `-s/--bind-address`: The bind address for the room.
- `--help/-h`: Display help. - `-p/--port`: The port used for the room.
- `--game/-g`: Specify the game to run. - `-m/--max-members`: The maximum number of players for this room.
- `--multiplayer/-m`: Specify multiplayer options. - `-w/--password`: The password for the room.
- `--program/-p`: Specify the program arguments to pass (optional). - `-g/--preferred-game`: The preferred game for this room.
- `--user/-u`: Specify the user index. - `-i/--preferred-game-id`: The preferred game-id for this room.
- `--version/-v`: Display version and quit. - `-u/--username`: The username used for announce.
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only). - `-t/--token`: The token used for announce.
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings. - `-a/--web-api-url`: yuzu Web API url.
- `--filter/-x`: Sets the debug log filter irrespective of settings. - `-b/--ban-list-file`: The file for storing the room ban list.
- `--singlecore/-s`: Forces single-core regardless of settings. - `-l/--log-file`: The file for storing the room log.
- `-h/--help`: Display this help and exit.
- `-v/--version`: Output version information and exit.
+2 -2
View File
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId, jstring jprogramId,
jstring jupdatePath) { jstring jupdatePath) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId); const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
std::string updatePath = Common::Android::GetJString(env, jupdatePath); std::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
for (const auto& item : nsp->GetNCAs()) { for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) { for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) { if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL; const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
if (update_id == program_id) { if (update_id == program_id) {
return true; return true;
} }
@@ -289,6 +289,7 @@
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string> <string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string> <string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string> <string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
<string name="post_processing_add">Añadir efecto</string>
<string name="post_processing_open_list">Abrir lista</string> <string name="post_processing_open_list">Abrir lista</string>
<string name="post_processing_close_list">Cerrar lista</string> <string name="post_processing_close_list">Cerrar lista</string>
<string name="post_processing_reset">Restablecer a predeterminado</string> <string name="post_processing_reset">Restablecer a predeterminado</string>
@@ -318,6 +318,13 @@
<string name="frame_gen_target_rate">Целевая частота кадров</string> <string name="frame_gen_target_rate">Целевая частота кадров</string>
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string> <string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string> <string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
<string name="frame_gen_on">Вкл.</string>
<string name="frame_gen_off">Выкл.</string>
<string name="frame_gen_fixed">Фикс.</string>
<string name="frame_gen_flow_auto">Авто</string>
<string name="frame_gen_quick_description">Включите или выключите генерацию кадров и выберите множитель кадров.</string>
<string name="frame_gen_target_rate_quick_description">Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически.</string>
<string name="frame_gen_flow_scale_quick_description">Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ.</string>
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string> <string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string> <string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string> <string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
@@ -328,12 +335,14 @@
<string name="frame_gen_flow_scale">Разрешение оценки движения</string> <string name="frame_gen_flow_scale">Разрешение оценки движения</string>
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string> <string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
<string name="frame_gen_unsupported">Генерация кадров недоступна</string> <string name="frame_gen_unsupported">Генерация кадров недоступна</string>
<string name="frame_gen_unsupported_description">В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling.</string>
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string> <string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
<string name="lossless_scaling_install">Установить Lossless.dll</string> <string name="lossless_scaling_install">Установить Lossless.dll</string>
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string> <string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string> <string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
<string name="frame_generation_support">Генерация кадров</string> <string name="frame_generation_support">Генерация кадров</string>
<string name="frame_generation_supported">Поддерживается</string> <string name="frame_generation_supported">Поддерживается</string>
<string name="frame_generation_unsupported">Не поддерживается (нет модели памяти Vulkan или float16)</string>
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string> <string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
<string name="lossless_scaling_installed">Установлена</string> <string name="lossless_scaling_installed">Установлена</string>
<string name="lossless_scaling_not_installed">Не установлена</string> <string name="lossless_scaling_not_installed">Не установлена</string>
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。<br /><br />在您开始之前, 请定位您设备存储上的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">了解更多</a>]]></string>
<string name="notice_notification_channel_name">错误与注意事项</string> <string name="notice_notification_channel_name">错误与注意事项</string>
<string name="notice_notification_channel_description">当发生错误时显示通知。</string> <string name="notice_notification_channel_description">当发生错误时显示通知。</string>
<string name="notification_permission_not_granted">未授予通知权限!</string> <string name="notification_permission_not_granted">未授予通知权限!</string>
@@ -290,6 +291,25 @@
<string name="gpu_driver_fetcher">GPU驱动获取器</string> <string name="gpu_driver_fetcher">GPU驱动获取器</string>
<string name="gpu_driver_manager">GPU 驱动管理器</string> <string name="gpu_driver_manager">GPU 驱动管理器</string>
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string> <string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
<string name="post_processing">后处理效果</string>
<string name="post_processing_description">在渲染完成后应用 ReShade FX 效果</string>
<string name="post_processing_per_game_description">配置此游戏的效果链</string>
<string name="post_processing_add">添加效果</string>
<string name="post_processing_remove">移除</string>
<string name="post_processing_open_list">打开列表</string>
<string name="post_processing_close_list">关闭列表</string>
<string name="post_processing_remove_all">移除效果</string>
<string name="post_processing_preset_locked">预设已激活。在开始游戏前可以在后处理效果中编辑它。</string>
<string name="post_processing_preset_modified">数值已从原始预设被更改。</string>
<string name="post_processing_presets">预设</string>
<string name="post_processing_preset_new">新建预设</string>
<string name="post_processing_preset_new_description">把您加载的效果及当前数值保存为一个预设,以便今后选用。</string>
<string name="post_processing_preset_name_invalid">请给预设起一个不含等号的名称。</string>
<string name="post_processing_preset_none">无预设</string>
<string name="post_processing_preset_delete">删除预设</string>
<string name="post_processing_preset_reset">重置预设值</string>
<string name="post_processing_reset">重置为默认值</string>
<string name="post_processing_empty">找不到效果。请在此文件中放置 .fx 文件:</string>
<string name="frame_gen">帧生成</string> <string name="frame_gen">帧生成</string>
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string> <string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string> <string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
@@ -304,6 +324,13 @@
<string name="frame_gen_target_rate_60">60 FPS</string> <string name="frame_gen_target_rate_60">60 FPS</string>
<string name="frame_gen_target_rate_90">90 FPS</string> <string name="frame_gen_target_rate_90">90 FPS</string>
<string name="frame_gen_target_rate_120">120 FPS</string> <string name="frame_gen_target_rate_120">120 FPS</string>
<string name="frame_gen_on">开</string>
<string name="frame_gen_off">关</string>
<string name="frame_gen_fixed">固定</string>
<string name="frame_gen_flow_auto">自动</string>
<string name="frame_gen_quick_description">打开或关闭帧生成并选择帧生成倍数。</string>
<string name="frame_gen_target_rate_quick_description">生成帧到目标帧率。倍率会自动调整。</string>
<string name="frame_gen_flow_scale_quick_description">用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。</string>
<string name="frame_gen_queue_target">帧队列目标</string> <string name="frame_gen_queue_target">帧队列目标</string>
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string> <string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string> <string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
@@ -314,12 +341,14 @@
<string name="frame_gen_flow_scale">运动预估分辨率</string> <string name="frame_gen_flow_scale">运动预估分辨率</string>
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string> <string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
<string name="frame_gen_unsupported">帧生成不可用</string> <string name="frame_gen_unsupported">帧生成不可用</string>
<string name="frame_gen_unsupported_description">这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。</string>
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string> <string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
<string name="lossless_scaling_install">安装 Lossless.dll</string> <string name="lossless_scaling_install">安装 Lossless.dll</string>
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string> <string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string> <string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
<string name="frame_generation_support">帧生成</string> <string name="frame_generation_support">帧生成</string>
<string name="frame_generation_supported">支持</string> <string name="frame_generation_supported">支持</string>
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
<string name="lossless_scaling">无损缩放</string> <string name="lossless_scaling">无损缩放</string>
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string> <string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
<string name="lossless_scaling_installed">已安装</string> <string name="lossless_scaling_installed">已安装</string>
@@ -611,6 +640,11 @@
<string name="log">日志记录</string> <string name="log">日志记录</string>
<string name="flush_by_line">按行刷新调试日志</string> <string name="flush_by_line">按行刷新调试日志</string>
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string> <string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
<string name="extended_logging">开启扩展日志</string>
<string name="extended_logging_description">将最大日志文件大小从 100 MiB 提升至 1GiB。</string>
<string name="log_filter">日志筛选器</string>
<string name="log_filter_description">控制 Eden 的日志类别。例如: *:Info Service.LM:Debug</string>
<!-- GPU Logging strings --> <!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU 日志</string> <string name="gpu_logging_header">GPU 日志</string>
<string name="gpu_log_level">日志等级</string> <string name="gpu_log_level">日志等级</string>
@@ -894,6 +928,8 @@
<string name="loader_error_file_not_found">ROM 文件不存在</string> <string name="loader_error_file_not_found">ROM 文件不存在</string>
<string name="loader_requires_firmware">游戏需要固件</string> <string name="loader_requires_firmware">游戏需要固件</string>
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
<!-- Intent Launch strings --> <!-- Intent Launch strings -->
<string name="searching_for_game">正在搜索游戏...</string> <string name="searching_for_game">正在搜索游戏...</string>
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string> <string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
@@ -229,7 +229,7 @@
<string name="add_directory_success">遊戲目錄新增成功</string> <string name="add_directory_success">遊戲目錄新增成功</string>
<string name="enable_update_checks">檢查更新</string> <string name="enable_update_checks">檢查更新</string>
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string> <string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
<string name="update_available">偵測到新版本</string> <string name="update_available">發現新版本</string>
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string> <string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
<string name="downloading_update">下載更新中</string> <string name="downloading_update">下載更新中</string>
<string name="update_download_failed">更新下載失敗</string> <string name="update_download_failed">更新下載失敗</string>
@@ -1020,9 +1020,40 @@
<string name="memory_6gb">6GB (不穩定)</string> <string name="memory_6gb">6GB (不穩定)</string>
<string name="memory_8gb">8GB (不穩定)</string> <string name="memory_8gb">8GB (不穩定)</string>
<!-- CPU clock levels -->
<string name="clock_normal">正常</string>
<string name="clock_boost">加速</string>
<string name="clock_fast">超頻</string>
<!-- GPU clock levels -->
<string name="fast_gpu_normal">正常</string>
<string name="fast_gpu_medium">加速</string>
<string name="fast_gpu_high">超頻</string>
<!-- GPU swizzle texture size -->
<string name="gpu_texturesizeswizzle_verysmall">極小 (16MB)</string>
<string name="gpu_texturesizeswizzle_small">小(32MB)</string>
<string name="gpu_texturesizeswizzle_normal">正常(128MB)</string>
<string name="gpu_texturesizeswizzle_large">大(256MB)</string>
<string name="gpu_texturesizeswizzle_verylarge">極大(512MB)</string>
<!-- GPU swizzle streams -->
<string name="gpu_swizzle_verylow">極小(4MB)</string>
<string name="gpu_swizzle_low">小(8MB)</string>
<string name="gpu_swizzle_normal">正常(16MB)</string>
<string name="gpu_swizzle_medium">中(32MB)</string>
<string name="gpu_swizzle_high">大(64MB)</string>
<!-- GPU swizzle chunks -->
<string name="gpu_swizzlechunk_verylow">極小(32)</string>
<string name="gpu_swizzlechunk_low">小(64)</string>
<string name="gpu_swizzlechunk_normal">正常(128)</string>
<string name="gpu_swizzlechunk_medium">中(256)</string>
<string name="gpu_swizzlechunk_high">大(512)</string>
<!-- Temperature Units --> <!-- Temperature Units -->
<string name="temperature_celsius">攝氏度</string> <string name="temperature_celsius">攝氏</string>
<string name="temperature_fahrenheit">華氏度</string> <string name="temperature_fahrenheit">華氏</string>
<!-- Memory Sizes --> <!-- Memory Sizes -->
<string name="memory_byte_shorthand">B</string> <string name="memory_byte_shorthand">B</string>
@@ -1035,6 +1066,8 @@
<string name="renderer_none">無</string> <string name="renderer_none">無</string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">效能</string>
<string name="renderer_accuracy_high">準確</string> <string name="renderer_accuracy_high">準確</string>
<!-- DMA Accuracy --> <!-- DMA Accuracy -->
-1
View File
@@ -76,7 +76,6 @@ add_library(
logging.h logging.h
lz4_compression.cpp lz4_compression.cpp
lz4_compression.h lz4_compression.h
make_unique_for_overwrite.h
math_util.h math_util.h
memory_detect.cpp memory_detect.cpp
memory_detect.h memory_detect.h
+4 -3
View File
@@ -244,7 +244,8 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0} , ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0} , us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0} , ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1} , rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0} , cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0} , gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
, invariant{invariant_} , invariant{invariant_}
@@ -291,7 +292,7 @@ bool WallClock::IsNative() const {
} }
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const { u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count(); return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor);
} }
#elif defined(HAS_NCE) #elif defined(HAS_NCE)
namespace { namespace {
@@ -416,7 +417,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
const WallClock g_wall_clock = [] { const WallClock g_wall_clock = [] {
#if defined(ARCHITECTURE_x86_64) #if defined(ARCHITECTURE_x86_64)
auto const& caps = Common::g_cpu_caps; auto const& caps = Common::g_cpu_caps;
return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency); return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency);
#elif defined(HAS_NCE) #elif defined(HAS_NCE)
return WallClock(false, 1); return WallClock(false, 1);
#else #else
+1
View File
@@ -96,6 +96,7 @@ public:
u64 ns_rdtsc_factor; u64 ns_rdtsc_factor;
u64 us_rdtsc_factor; u64 us_rdtsc_factor;
u64 ms_rdtsc_factor; u64 ms_rdtsc_factor;
u64 rdtsc_ns_integer;
u64 rdtsc_ns_factor; u64 rdtsc_ns_factor;
u64 cntpct_rdtsc_factor; u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor; u64 gputick_rdtsc_factor;
+16 -18
View File
@@ -17,35 +17,33 @@
namespace Common { namespace Common {
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
// We don't need to use the preprocessor, we can just select depending on return type
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(int r, char *err_str) {
return std::string{r != 0
? "(strerror_r failed to format error)"
: err_str};
}
std::string NativeErrorToString(int e) { std::string NativeErrorToString(int e) {
#ifdef _WIN32 #ifdef _WIN32
LPSTR err_str; LPSTR err_str;
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&err_str), 1, nullptr); LPSTR(&err_str), 1, nullptr);
if (!res) { if (res) {
return "(FormatMessageA failed to format error)";
}
std::string ret(err_str); std::string ret(err_str);
LocalFree(err_str); LocalFree(err_str);
return ret; return ret;
}
return "(FormatMessageA failed to format error)";
#else #else
char err_str[255]; char err_str[255];
#if defined(__ANDROID__) || \ return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
// Thread safe (GNU-specific)
const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str);
#else
// Thread safe (XSI-compliant)
int second_err = strerror_r(e, err_str, sizeof(err_str));
if (second_err != 0) {
return "(strerror_r failed to format error)";
}
return std::string(err_str);
#endif // GLIBC etc.
#endif // _WIN32 #endif // _WIN32
} }
-27
View File
@@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <type_traits>
namespace Common {
template <class T>
requires(!std::is_array_v<T>)
std::unique_ptr<T> make_unique_for_overwrite() {
return std::unique_ptr<T>(new T);
}
template <class T>
requires std::is_unbounded_array_v<T>
std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
}
template <class T, class... Args>
requires std::is_bounded_array_v<T>
void make_unique_for_overwrite(Args&&...) = delete;
} // namespace Common
+6 -6
View File
@@ -8,8 +8,7 @@
#include <iterator> #include <iterator>
#include <cstring> #include <cstring>
#include <memory>
#include "common/make_unique_for_overwrite.h"
namespace Common { namespace Common {
@@ -38,8 +37,9 @@ public:
ScratchBuffer() = default; ScratchBuffer() = default;
explicit ScratchBuffer(size_type initial_capacity) explicit ScratchBuffer(size_type initial_capacity)
: last_requested_size{initial_capacity}, buffer_capacity{initial_capacity}, : last_requested_size{initial_capacity}
buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {} , buffer_capacity{initial_capacity}
, buffer{std::make_unique_for_overwrite<T[]>(initial_capacity)} {}
~ScratchBuffer() = default; ~ScratchBuffer() = default;
ScratchBuffer(const ScratchBuffer&) = delete; ScratchBuffer(const ScratchBuffer&) = delete;
@@ -64,7 +64,7 @@ public:
/// The previously held data will remain intact. /// The previously held data will remain intact.
void resize(size_type size) { void resize(size_type size) {
if (size > buffer_capacity) { if (size > buffer_capacity) {
auto new_buffer = Common::make_unique_for_overwrite<T[]>(size); auto new_buffer = std::make_unique_for_overwrite<T[]>(size);
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T)); std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
buffer = std::move(new_buffer); buffer = std::move(new_buffer);
buffer_capacity = size; buffer_capacity = size;
@@ -77,7 +77,7 @@ public:
void resize_destructive(size_type size) { void resize_destructive(size_type size) {
if (size > buffer_capacity) { if (size > buffer_capacity) {
buffer_capacity = size; buffer_capacity = size;
buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity); buffer = std::make_unique_for_overwrite<T[]>(buffer_capacity);
} }
last_requested_size = size; last_requested_size = size;
} }
+2 -2
View File
@@ -649,10 +649,10 @@ struct Values {
SwitchableSetting<bool> fix_bloom_effects{linkage, false, "fix_bloom_effects", SwitchableSetting<bool> fix_bloom_effects{linkage, false, "fix_bloom_effects",
Category::RendererHacks}; Category::RendererHacks};
#ifdef __ANDROID__
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565", SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
Category::RendererHacks}; Category::RendererHacks};
#endif
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack", SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
Category::RendererHacks}; Category::RendererHacks};
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage, SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
+15
View File
@@ -96,6 +96,21 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
#ifndef MAP_NOCORE #ifndef MAP_NOCORE
#define MAP_NOCORE 0 #define MAP_NOCORE 0
#endif #endif
#ifndef MADV_FREE
#define MADV_FREE MADV_DONTNEED
#endif
void DecommitVectorPage(uintptr_t base) noexcept {
#if defined(_WIN32)
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
#elif defined(__linux__)
// Linux's MADV_DONTNEED zeros out pages for us
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
#else
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
#endif
}
void* AllocateMemoryPages(std::size_t size) noexcept { void* AllocateMemoryPages(std::size_t size) noexcept {
if (auto page = HostPageSize; size % page != 0) { if (auto page = HostPageSize; size % page != 0) {
+17 -3
View File
@@ -35,6 +35,7 @@ const u64 HostPageMask = ~(HostPageSize - 1);
void* AllocateMemoryPages(std::size_t size) noexcept; void* AllocateMemoryPages(std::size_t size) noexcept;
void FreeMemoryPages(void* base, std::size_t size) noexcept; void FreeMemoryPages(void* base, std::size_t size) noexcept;
void DecommitVectorPage(uintptr_t base) noexcept;
/// A large page-aligned buffer that has optimized memory usage for zero-writes. /// A large page-aligned buffer that has optimized memory usage for zero-writes.
template <typename T> template <typename T>
@@ -114,7 +115,7 @@ public:
const u64 end_page = AlignUp(base, HostPageSize); const u64 end_page = AlignUp(base, HostPageSize);
const u64 first_size = (std::min)(end_page, end) - base; const u64 first_size = (std::min)(end_page, end) - base;
if (IsCommittedPage(start / sizeof(T))) { if (IsCommittedPage(start)) {
std::memset(reinterpret_cast<void*>(base), 0, first_size); std::memset(reinterpret_cast<void*>(base), 0, first_size);
} }
@@ -124,11 +125,16 @@ public:
base = end_page; base = end_page;
for (u64 page = base; page < end; page += HostPageSize) { for (u64 page = base; page < end; page += HostPageSize) {
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) { auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
if (!IsCommittedPage(index)) {
continue; continue;
} }
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page)); if (end - page >= HostPageSize) {
DecommitPage(index);
} else {
std::memset(reinterpret_cast<void*>(page), 0, end - page);
}
} }
} }
@@ -183,6 +189,14 @@ private:
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release); committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
} }
constexpr void DecommitPage(std::size_t index) noexcept {
auto page_index = (index * sizeof(T)) >> HostPageBits;
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
DecommitVectorPage(page);
}
std::size_t alloc_size{}; std::size_t alloc_size{};
T* base_ptr{}; T* base_ptr{};
+5
View File
@@ -23,6 +23,8 @@ add_library(core STATIC
core_timing.h core_timing.h
cpu_manager.cpp cpu_manager.cpp
cpu_manager.h cpu_manager.h
launch_params.cpp
launch_params.h
crypto/aes_util.cpp crypto/aes_util.cpp
crypto/aes_util.h crypto/aes_util.h
crypto/ctr_encryption_layer.cpp crypto/ctr_encryption_layer.cpp
@@ -1203,6 +1205,9 @@ endif()
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS}) target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
if (MSVC)
target_link_libraries(core PRIVATE getopt)
endif()
if (BOOST_NO_HEADERS) if (BOOST_NO_HEADERS)
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc) target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
+18 -34
View File
@@ -22,21 +22,15 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} , m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{} {}
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) { u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr); switch (size) {
} case sizeof(u64): return m_memory.Read64(vaddr);
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) { case sizeof(u32): return m_memory.Read32(vaddr);
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); case sizeof(u16): return m_memory.Read16(vaddr);
return m_memory.Read16(vaddr); case sizeof(u8): return m_memory.Read8(vaddr);
} default: UNREACHABLE();
u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) { }
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
} }
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) { std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
@@ -50,27 +44,17 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
} }
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) { void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value); switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, value);
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
} }
} }
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
}
}
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) { bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
m_memory.WriteExclusive8(vaddr, value, expected); m_memory.WriteExclusive8(vaddr, value, expected);
+2 -8
View File
@@ -30,18 +30,12 @@ class System;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public: public:
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process); explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
u8 MemoryRead8(u32 vaddr) override; u64 MemoryRead(u32 vaddr, size_t size) override;
u16 MemoryRead16(u32 vaddr) override;
u32 MemoryRead32(u32 vaddr) override;
u64 MemoryRead64(u32 vaddr) override;
std::optional<u32> MemoryReadCode(u32 vaddr) override; std::optional<u32> MemoryReadCode(u32 vaddr) override;
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
void MemoryWrite8(u32 vaddr, u8 value) override; void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
void MemoryWrite16(u32 vaddr, u16 value) override;
void MemoryWrite32(u32 vaddr, u32 value) override;
void MemoryWrite64(u32 vaddr, u64 value) override;
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override; bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override; bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override; bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
+18 -32
View File
@@ -10,6 +10,7 @@
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "dynarmic/interface/A64/config.h"
namespace Core { namespace Core {
@@ -21,21 +22,15 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} , m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{} {}
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) { u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr); switch (size) {
} case sizeof(u64): return m_memory.Read64(vaddr);
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) { case sizeof(u32): return m_memory.Read32(vaddr);
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); case sizeof(u16): return m_memory.Read16(vaddr);
return m_memory.Read16(vaddr); case sizeof(u8): return m_memory.Read8(vaddr);
} default: UNREACHABLE();
u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) { }
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
} }
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) { Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
@@ -53,24 +48,15 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
} }
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) { void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value); switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
} }
}
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
} }
} }
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) { void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
+3 -8
View File
@@ -16,6 +16,7 @@
#include "common/hash.h" #include "common/hash.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "dynarmic/interface/A64/config.h"
namespace Core::Memory { namespace Core::Memory {
class Memory; class Memory;
@@ -36,19 +37,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public: public:
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process); explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
u8 MemoryRead8(u64 vaddr) override; u64 MemoryRead(u64 vaddr, size_t size) override;
u16 MemoryRead16(u64 vaddr) override;
u32 MemoryRead32(u64 vaddr) override;
u64 MemoryRead64(u64 vaddr) override;
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override; Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
std::optional<u32> MemoryReadCode(u64 vaddr) override; std::optional<u32> MemoryReadCode(u64 vaddr) override;
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
void MemoryWrite8(u64 vaddr, u8 value) override; void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
void MemoryWrite16(u64 vaddr, u16 value) override;
void MemoryWrite32(u64 vaddr, u32 value) override;
void MemoryWrite64(u64 vaddr, u64 value) override;
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override; void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override; bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override; bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
+7 -4
View File
@@ -35,19 +35,22 @@ static IPSFileType IdentifyMagic(std::span<const u8> magic) {
} }
static bool IsEOF(IPSFileType type, std::span<const u8> magic) { static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0) return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0); || (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
} }
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
if (in == nullptr || ips == nullptr) if (in == nullptr || ips == nullptr)
return nullptr; return nullptr;
auto in_data = in->ReadAllBytes(); const auto type = IdentifyMagic(ips->ReadBytes(0x5));
auto const type = IdentifyMagic(in_data);
if (type == IPSFileType::Error) if (type == IPSFileType::Error)
return nullptr; return nullptr;
auto in_data = in->ReadAllBytes();
if (in_data.size() == 0)
return nullptr;
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
u64 offset = 5; // After header u64 offset = 5; // After header
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
+50 -18
View File
@@ -9,6 +9,7 @@
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include "common/assert.h"
#include "common/hex_util.h" #include "common/hex_util.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/settings.h" #include "common/settings.h"
@@ -156,12 +157,15 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
NACP nacp{nacp_file}; NACP nacp{nacp_file};
return nacp.GetVersionString(); return nacp.GetVersionString();
} }
YUZU_NO_INLINE std::optional<u64> GetParentApplicationId(const ContentProvider* provider, u64 title_id) {
return provider == nullptr ? std::nullopt : provider->GetParentApplicationId(title_id);
}
} // Anonymous namespace } // Anonymous namespace
PatchManager::PatchManager(u64 title_id_, PatchManager::PatchManager(u64 title_id_,
const Service::FileSystem::FileSystemController& fs_controller_, const Service::FileSystem::FileSystemController& fs_controller_,
const ContentProvider& content_provider_) const ContentProvider& content_provider_)
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {} : title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
PatchManager::~PatchManager() = default; PatchManager::~PatchManager() = default;
@@ -169,13 +173,35 @@ u64 PatchManager::GetTitleID() const {
return title_id; return title_id;
} }
VirtualDir PatchManager::GetModificationLoadRoot(bool sdmc) const {
const auto get_root = [&](u64 id) {
return sdmc ? fs_controller.GetSDMCModificationLoadRoot(id) : fs_controller.GetModificationLoadRoot(id);
};
auto root = get_root(title_id);
if (!parent_title_id) {
return root;
}
std::vector<VirtualDir> roots{std::move(root), get_root(*parent_title_id)};
std::erase(roots, nullptr);
return LayeredVfsDirectory::MakeLayeredDirectory(std::move(roots));
}
std::vector<std::string> PatchManager::GetDisabledAddons() const {
auto disabled = Settings::values.disabled_addons[title_id];
if (parent_title_id) {
const auto& shared = Settings::values.disabled_addons[*parent_title_id];
disabled.insert(disabled.end(), shared.begin(), shared.end());
}
return disabled;
}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr) if (exefs == nullptr)
return exefs; return exefs;
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto disabled = GetDisabledAddons();
bool update_disabled = true; bool update_disabled = true;
std::optional<u32> enabled_version; std::optional<u32> enabled_version;
@@ -303,8 +329,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
} }
// LayeredExeFS // LayeredExeFS
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dir = GetModificationLoadRoot();
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); const auto sdmc_load_dir = GetModificationLoadRoot(true);
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
if (load_dir != nullptr) { if (load_dir != nullptr) {
@@ -346,7 +372,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
} }
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const { std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto disabled = GetDisabledAddons();
const auto nso_build_id = fmt::format("{:0<64}", build_id); const auto nso_build_id = fmt::format("{:0<64}", build_id);
std::vector<VirtualFile> out; std::vector<VirtualFile> out;
@@ -405,7 +431,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dir = GetModificationLoadRoot();
if (load_dir == nullptr) { if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso; return nso;
@@ -448,7 +474,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name); LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dir = GetModificationLoadRoot();
if (load_dir == nullptr) { if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false; return false;
@@ -462,13 +488,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
} }
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const { std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dir = GetModificationLoadRoot();
if (load_dir == nullptr) { if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {}; return {};
} }
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto disabled = GetDisabledAddons();
auto patch_dirs = load_dir->GetSubdirectories(); auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); }); std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
@@ -502,17 +528,16 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
return out; return out;
} }
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type, void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
const Service::FileSystem::FileSystemController& fs_controller) { const auto load_dir = GetModificationLoadRoot();
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto sdmc_load_dir = GetModificationLoadRoot(true);
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data && if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
type != ContentRecordType::HtmlDocument) || type != ContentRecordType::HtmlDocument) ||
(load_dir == nullptr && sdmc_load_dir == nullptr)) { (load_dir == nullptr && sdmc_load_dir == nullptr)) {
return; return;
} }
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto disabled = GetDisabledAddons();
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories(); std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) { if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
patch_dirs.push_back(sdmc_load_dir); patch_dirs.push_back(sdmc_load_dir);
@@ -591,7 +616,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// Game Updates // Game Updates
const auto update_tid = GetUpdateTitleID(title_id); const auto update_tid = GetUpdateTitleID(title_id);
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto disabled = GetDisabledAddons();
bool update_disabled = true; bool update_disabled = true;
std::optional<u32> enabled_version; std::optional<u32> enabled_version;
@@ -698,7 +723,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// LayeredFS // LayeredFS
if (apply_layeredfs) { if (apply_layeredfs) {
ApplyLayeredFS(romfs, title_id, type, fs_controller); ApplyLayeredFS(romfs, type);
} }
return romfs; return romfs;
@@ -1072,15 +1097,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::optional<u32> PatchManager::GetGameVersion() const { std::optional<u32> PatchManager::GetGameVersion() const {
const auto update_tid = GetUpdateTitleID(title_id); const auto update_tid = GetUpdateTitleID(title_id);
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) { if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
return content_provider.GetEntryVersion(update_tid); const auto version = content_provider.GetEntryVersion(update_tid);
return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id));
} }
return content_provider.GetEntryVersion(title_id); const auto version = content_provider.GetEntryVersion(title_id);
return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id);
} }
PatchManager::Metadata PatchManager::GetControlMetadata() const { PatchManager::Metadata PatchManager::GetControlMetadata() const {
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control); const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr) { if (base_control_nca == nullptr) {
if (parent_title_id) {
const auto control_id = content_provider.HasEntry(*parent_title_id, ContentRecordType::Control) ? *parent_title_id : GetUpdateTitleID(*parent_title_id);
const PatchManager parent{control_id, fs_controller, content_provider};
return parent.GetControlMetadata();
}
return {}; return {};
} }
+4
View File
@@ -109,10 +109,14 @@ public:
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept; [[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
private: private:
[[nodiscard]] VirtualDir GetModificationLoadRoot(bool sdmc = false) const;
[[nodiscard]] std::vector<std::string> GetDisabledAddons() const;
void ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const;
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const; const std::string& build_id) const;
u64 title_id; u64 title_id;
std::optional<u64> parent_title_id;
const Service::FileSystem::FileSystemController& fs_controller; const Service::FileSystem::FileSystemController& fs_controller;
const ContentProvider& content_provider; const ContentProvider& content_provider;
}; };
+17
View File
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <limits>
#include <random> #include <random>
#include <regex> #include <regex>
#include <openssl/evp.h> #include <openssl/evp.h>
@@ -348,6 +349,22 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
} }
std::optional<u64> ContentProvider::GetParentApplicationId(u64 program_id) const {
const auto application_id = GetBaseTitleID(program_id);
const auto program_index = program_id - application_id;
if (program_index == 0 || program_index > std::numeric_limits<u8>::max()) {
return std::nullopt;
}
if (!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, program_id).empty()) {
return std::nullopt;
}
if ((!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, application_id).empty() && HasEntry(program_id, ContentRecordType::Program))
|| (!ListEntriesFilter(TitleType::Update, ContentRecordType::Meta, GetUpdateTitleID(application_id)).empty() && HasEntry(GetUpdateTitleID(program_id), ContentRecordType::Program))) {
return application_id;
}
return std::nullopt;
}
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {} PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
bool PlaceholderCache::Create(const NcaID& id, u64 size) const { bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
+2
View File
@@ -100,6 +100,8 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const = 0; std::optional<u64> title_id = {}) const = 0;
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
protected: protected:
// A single instance of KeyManager to be used by GetEntry() // A single instance of KeyManager to be used by GetEntry()
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
+3
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
+3 -4
View File
@@ -109,8 +109,7 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
auto reference = std::make_unique<FileReference>(); auto reference = std::make_unique<FileReference>();
this->InsertReferenceIntoListLocked(*reference); this->InsertReferenceIntoListLocked(*reference);
auto file = std::shared_ptr<RealVfsFile>( auto file = std::make_shared<RealVfsFile>(*this, std::move(reference), path, perms, size, std::move(parent_path));
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
cache[path] = file; cache[path] = file;
return file; return file;
@@ -177,7 +176,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); return std::make_shared<RealVfsDirectory>(*this, path, perms);
} }
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
@@ -185,7 +184,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p
if (!FS::CreateDirs(path)) { if (!FS::CreateDirs(path)) {
return nullptr; return nullptr;
} }
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); return std::make_shared<RealVfsDirectory>(*this, path, perms);
} }
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
+5 -6
View File
@@ -82,6 +82,9 @@ class RealVfsFile : public VfsFile {
friend class RealVfsFilesystem; friend class RealVfsFilesystem;
public: public:
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
const std::string& path, OpenMode perms = OpenMode::Read,
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
~RealVfsFile() override; ~RealVfsFile() override;
std::string GetName() const override; std::string GetName() const override;
@@ -95,9 +98,6 @@ public:
bool Rename(std::string_view name) override; bool Rename(std::string_view name) override;
private: private:
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
const std::string& path, OpenMode perms = OpenMode::Read,
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
RealVfsFilesystem& base; RealVfsFilesystem& base;
std::unique_ptr<FileReference> reference; std::unique_ptr<FileReference> reference;
@@ -113,6 +113,8 @@ class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem; friend class RealVfsFilesystem;
public: public:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
OpenMode perms = OpenMode::Read);
~RealVfsDirectory() override; ~RealVfsDirectory() override;
VirtualFile GetFileRelative(std::string_view relative_path) const override; VirtualFile GetFileRelative(std::string_view relative_path) const override;
@@ -138,9 +140,6 @@ public:
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
private: private:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
OpenMode perms = OpenMode::Read);
template <typename T, typename R> template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const; std::vector<std::shared_ptr<R>> IterateEntries() const;
+8 -1
View File
@@ -29,6 +29,7 @@
#include "core/hle/service/am/frontend/applet_web_browser.h" #include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/service/storage.h" #include "core/hle/service/am/service/storage.h"
#include "core/hle/service/am/window_system.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
namespace Service::AM::Frontend { namespace Service::AM::Frontend {
@@ -72,10 +73,16 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
void FrontendApplet::Exit() { void FrontendApplet::Exit() {
auto applet_ = applet.lock(); auto applet_ = applet.lock();
{
std::scoped_lock lk{applet_->lock}; std::scoped_lock lk{applet_->lock};
applet_->is_completed = true; applet_->is_completed = true;
applet_->state_changed_event.Signal(system.Kernel()); applet_->state_changed_event.Signal(system.Kernel());
}
if (auto caller_applet = applet_->caller_applet.lock()) {
std::scoped_lock lk{caller_applet->lock};
std::erase(caller_applet->child_applets, applet_);
}
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) window_system->RequestUpdate();
} }
FrontendAppletSet::FrontendAppletSet() = default; FrontendAppletSet::FrontendAppletSet() = default;
@@ -92,6 +92,13 @@ public:
} }
} }
void RequestFocusStateChangedNotification(Kernel::KernelCore& kernel) {
if (m_focus_state_changed_notification_enabled) {
m_has_focus_state_changed = true;
this->SignalSystemEventIfNeeded(kernel);
}
}
void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel); void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel);
public: public:
@@ -164,14 +164,14 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) { Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel());
caller_applet->lifecycle_manager.RequestResumeNotification();
caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel());
caller_applet->lifecycle_manager.UpdateRequestedFocusState();
}
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get())); R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
std::scoped_lock lk{caller_applet->lock};
const bool focus_state_changed = caller_applet->lifecycle_manager.UpdateRequestedFocusState();
const bool is_front_app = m_applet->frontend && caller_applet->lifecycle_manager.IsApplication();
if (focus_state_changed) caller_applet->lifecycle_manager.SignalSystemEventIfNeeded(system.Kernel());
else if (is_front_app) caller_applet->lifecycle_manager.RequestFocusStateChangedNotification(system.Kernel());
}
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) { if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
auto impl = (*out_storage)->GetImpl(); auto impl = (*out_storage)->GetImpl();
@@ -122,7 +122,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
auto broker = std::make_shared<AppletDataBroker>(system); auto broker = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet; applet->caller_applet = caller_applet;
applet->caller_applet_broker = broker; applet->caller_applet_broker = broker;
{
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet); caller_applet->child_applets.push_back(applet);
}
window_system.TrackApplet(applet, false); window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
} }
@@ -148,10 +151,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
applet->caller_applet = caller_applet; applet->caller_applet = caller_applet;
applet->caller_applet_broker = storage; applet->caller_applet_broker = storage;
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode); applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
{
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet); caller_applet->child_applets.push_back(applet);
}
window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet); return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
} }
+1 -1
View File
@@ -517,7 +517,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity. // Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
s32 z_index = Background; s32 z_index = Background;
if (is_overlay) { if (is_overlay) {
z_index = Overlay; z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background;
} else if (inherited_foreground) { } else if (inherited_foreground) {
z_index = is_obscured ? Foreground : ForegroundVisible; z_index = is_obscured ? Foreground : ForegroundVisible;
} }
+1 -1
View File
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30); server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30); server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30); server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
@@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{547, nullptr, "GetAllowedBluetoothLinksCount"}, {547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"}, {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
{549, nullptr, "GetConnectableRegisteredDevices"}, {549, nullptr, "GetConnectableRegisteredDevices"},
{551, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ {551, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ //mocked via 548 for Diablo 3 (at least)
{700, nullptr, "ActivateUniquePad"}, {700, nullptr, "ActivateUniquePad"},
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"}, {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"}, {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
@@ -758,7 +758,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
} }
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) { void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called"); LOG_WARNING(Service_HID, "(STUBBED) called, command={}", ctx.GetCommand()); //548 or 551
struct RegisterData { struct RegisterData {
std::array<u8, 0x68> data; std::array<u8, 0x68> data;
+26 -32
View File
@@ -71,17 +71,14 @@ public:
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
u8 MemoryRead8(u64 vaddr) override { u64 MemoryRead(u64 vaddr, size_t size) override {
return ReadMemory<u8>(vaddr); switch (size) {
case sizeof(u64): return ReadMemory<u64>(vaddr);
case sizeof(u32): return ReadMemory<u32>(vaddr);
case sizeof(u16): return ReadMemory<u16>(vaddr);
case sizeof(u8): return ReadMemory<u8>(vaddr);
default: UNREACHABLE();
} }
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 { u128 MemoryRead128(u64 vaddr) override {
return ReadMemory<u128>(vaddr); return ReadMemory<u128>(vaddr);
@@ -89,22 +86,19 @@ public:
std::string MemoryReadCString(u64 vaddr) { std::string MemoryReadCString(u64 vaddr) {
std::string result{}; std::string result{};
u8 next; u8 next;
while ((next = MemoryRead8(vaddr++)) != 0) while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
result += char(next); result += char(next);
return result; return result;
} }
void MemoryWrite8(u64 vaddr, u8 value) override { void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
WriteMemory<u8>(vaddr, value); switch (size) {
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
default: UNREACHABLE();
} }
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 { void MemoryWrite128(u64 vaddr, u128 value) override {
WriteMemory<u128>(vaddr, value); WriteMemory<u128>(vaddr, value);
@@ -193,14 +187,14 @@ public:
// The loaded NRO file has ELF relocations that must be processed before it can run. // The loaded NRO file has ELF relocations that must be processed before it can run.
// Normally this would be processed by RTLD, but in HLE context, we don't have // Normally this would be processed by RTLD, but in HLE context, we don't have
// the linker available, so we have to do it ourselves. // the linker available, so we have to do it ourselves.
const VAddr mod_offset{callbacks->MemoryRead32(4)}; const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
return false; return false;
// For more info about dynamic entries, see the ELF ABI specification: // For more info about dynamic entries, see the ELF ABI specification:
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
VAddr rela_dyn = 0, relr_dyn = 0; VAddr rela_dyn = 0, relr_dyn = 0;
size_t num_rela = 0, num_relr = 0; size_t num_rela = 0, num_relr = 0;
while (true) { while (true) {
@@ -222,8 +216,8 @@ public:
for (size_t i = 0; i < num_rela; i++) { for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) { if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64));
} }
} }
@@ -231,7 +225,7 @@ public:
for (size_t i = 0; i < num_relr; i++) { for (size_t i = 0; i < num_relr; i++) {
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr)); const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
const auto incr = [&](VAddr where) { const auto incr = [&](VAddr where) {
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase); callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64));
}; };
if ((relr & 1) == 0) { if ((relr & 1) == 0) {
// where pointer // where pointer
@@ -294,7 +288,7 @@ public:
if (argument_stack.size() > 8) { if (argument_stack.size() > 8) {
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); 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++) for (size_t i = 8; i < argument_stack.size(); i++)
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]); callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64));
jit->SetSP(new_sp); jit->SetSP(new_sp);
} }
// Reset the call state for the next invocation // Reset the call state for the next invocation
@@ -385,17 +379,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
if (dest < src) { if (dest < src) {
for (size_t i = 0; i < n; i++) for (size_t i = 0; i < n; i++)
MemoryWrite8(dest + i, MemoryRead8(src + i)); MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8));
} else { } else {
for (size_t i = n; i > 0; i--) for (size_t i = n; i > 0; i--)
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1)); MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8));
} }
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) { } else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
const VAddr dest{parent.jit->GetRegister(0)}; const VAddr dest{parent.jit->GetRegister(0)};
const u64 c{parent.jit->GetRegister(1)}; const u64 c{parent.jit->GetRegister(1)};
const size_t n{parent.jit->GetRegister(2)}; const size_t n{parent.jit->GetRegister(2)};
for (size_t i = 0; i < n; i++) for (size_t i = 0; i < n; i++)
MemoryWrite8(dest + i, u8(c)); MemoryWrite(dest + i, u8(c), sizeof(u8));
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) { } else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
// X0 contains a char* for a symbol to resolve // X0 contains a char* for a symbol to resolve
const auto name{MemoryReadCString(parent.jit->GetRegister(0))}; const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
@@ -422,7 +416,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
} }
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) { void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
auto const inst = MemoryRead32(pc); auto const inst = MemoryRead(pc, sizeof(u32));
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst); LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
parent.jit->HaltExecution(); parent.jit->HaltExecution();
} }
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -24,7 +24,7 @@ IReceiverService::~IReceiverService() = default;
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) { Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
LOG_DEBUG(Service_PSC, "called"); LOG_DEBUG(Service_PSC, "called");
*out_receiver = std::shared_ptr<IReceiver>(new IReceiver(system)); *out_receiver = std::make_shared<IReceiver>(system);
R_SUCCEED(); R_SUCCEED();
} }
+205
View File
@@ -0,0 +1,205 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <regex>
#include <cctype>
#include "common/assert.h"
#include "common/logging.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "common/scm_rev.h"
#include "core/core.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/launch_params.h"
#undef _UNICODE
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
namespace Core {
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept {
LaunchParams p{};
int option_index = 0;
static struct option long_options[] = {
// clang-format off
{"debug", no_argument, 0, 'd'},
{"config", required_argument, 0, 'c'},
{"fullscreen", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{"game", required_argument, 0, 'g'},
{"multiplayer", required_argument, 0, 'm'},
{"program", optional_argument, 0, 'p'},
{"user", required_argument, 0, 'u'},
{"version", no_argument, 0, 'v'},
{"input-profile", no_argument, 0, 'i'},
{"null-render", no_argument, 0, 'n'},
{"singlecore", no_argument, 0, 's'},
{"filter", no_argument, 0, 'x'},
{0, 0, 0, 0},
// clang-format on
};
while (optind < argc) {
if (int arg = getopt_long(argc, argv, "dc:fhg:m:p:u:vinsx", long_options, &option_index); arg != -1) {
switch (char(arg)) {
case 'd':
p.override_gdb_port = uint16_t(atoi(optarg));
break;
case 'c':
p.config_path = optarg;
break;
case 'f':
p.fullscreen = true;
LOG_INFO(Frontend, "Starting in fullscreen mode...");
break;
case 'h':
p.print_help = true;
break;
case 'g':
p.filepath = std::string(optarg);
break;
case 'i': {
p.input_profile = std::string(optarg);
break;
}
case 'm': {
p.use_multiplayer = true;
const std::string str_arg(optarg);
// regex to check if the format is nickname:password@ip:port
// with optional :password
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
if (std::regex_match(str_arg, re)) {
std::smatch match;
std::regex_search(str_arg, match, re);
ASSERT(match.size() == 5);
p.nickname = match[1];
p.password = match[2];
p.address = match[3];
if (!match[4].str().empty()) {
p.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
}
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
ASSERT(std::regex_match(p.nickname, nickname_re) && "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
ASSERT(!p.address.empty() && "Address is empty");
}
break;
}
case 'p':
p.program_args = argv[optind];
++optind;
break;
case 'u': {
// Launch game with a specific user
bool argument_ok = isdigit(optarg[0]);
p.selected_user = atoi(optarg);
if (!argument_ok) {
// try to look it up by username, only finds the first username that matches.
auto const user_idx = system.GetProfileManager().GetUserIndex(optarg);
if (user_idx != std::nullopt) {
p.selected_user = user_idx.value();
} else {
LOG_ERROR(Frontend, "Invalid user argument '{}'", optarg);
break;
}
}
if (system.GetProfileManager().UserExistsIndex(*p.selected_user)) {
Settings::values.current_user = s32(*p.selected_user);
} else {
LOG_ERROR(Frontend, "Selected user {} doesn't exist", *p.selected_user);
}
break;
}
case 'v':
p.print_version = true;
break;
case 'n':
p.force_null_render = true;
break;
case 's':
p.force_single_core = true;
break;
case 'x':
p.log_filter = argv[optind];
++optind;
break;
}
} else {
// only kept due to shortcuts made by Qt frontend which use "-qlaunch"
if (strcmp(argv[optind], "-hlaunch") == 0) {
p.launch_hlaunch = true;
} else if (strcmp(argv[optind], "-qlaunch") == 0) {
p.launch_qlaunch = true;
} else if (strcmp(argv[optind], "-setup") == 0) {
p.launch_setup = true;
} else {
// qt feeds utf8 data, sdl frontend feeds raw utf16 data
#ifdef _WIN32
p.filepath = argv_w != nullptr
? Common::UTF16ToUTF8(argv_w[optind])
: argv[optind];
#else
p.filepath = argv[optind];
#endif
}
optind++;
}
}
p.argv0 = argv[0];
return p;
}
void ApplyLaunchParams(LaunchParams const& lp) noexcept {
// apply the log_filter setting
// the logger was initialized before and doesn't pick up the filter on its own
Common::Log::Filter filter{};
filter.ParseFilterString(lp.log_filter.value_or(Settings::values.log_filter.GetValue()));
Common::Log::SetGlobalFilter(filter);
if (!lp.program_args.empty()) {
Settings::values.program_args = lp.program_args;
}
if (!lp.input_profile.empty()) {
auto& players = Settings::values.players.GetValue();
players[0].profile_name = lp.input_profile;
}
if (lp.selected_user.has_value()) {
Settings::values.current_user = std::clamp(*lp.selected_user, 0, 7);
}
if (lp.override_gdb_port.has_value()) {
Settings::values.use_gdbstub = true;
Settings::values.gdbstub_port = *lp.override_gdb_port;
}
if (lp.force_single_core) {
Settings::values.use_multi_core = false;
}
if (lp.force_null_render) {
Settings::values.renderer_backend = Settings::RendererBackend::Null;
}
if (lp.print_version) {
LOG_INFO(Frontend, "Eden {} {}", Common::g_scm_branch, Common::g_scm_desc);
}
if (lp.print_help) {
LOG_INFO(Frontend,
"Usage: {}"
" [options] <filename>\n"
"-c, --config Load the specified configuration file\n"
"-f, --fullscreen Start in fullscreen mode\n"
"-g, --game File path of the game to load\n"
"-h, --help Display this help and exit\n"
"-m, --multiplayer=nick:password@address:port"
" Nickname, password, address and port for multiplayer\n"
"-p, --program Pass following string as arguments to executable\n"
"-u, --user Select a specific user profile from 0 to 7\n"
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
"-v, --version Output version information and exit\n",
lp.argv0
);
}
}
}
+43
View File
@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <string>
#include <optional>
#include "common/common_types.h"
#include "network/room.h"
namespace Core {
class System;
struct LaunchParams {
std::string argv0{};
std::string filepath{};
std::string nickname{};
std::string password{};
std::string address{};
std::string input_profile{};
std::string program_args{};
std::optional<std::string> config_path{};
std::optional<int> selected_user{};
std::optional<u16> override_gdb_port{};
std::optional<std::string> log_filter{};
u16 port = Network::DefaultRoomPort;
bool use_multiplayer = false;
bool fullscreen = false;
bool force_null_render = false;
bool force_single_core = false;
// print
bool print_version = false;
bool print_help = false;
// qt
bool launch_hlaunch = false;
bool launch_qlaunch = false;
bool launch_setup = false;
};
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept;
void ApplyLaunchParams(LaunchParams const& lp) noexcept;
}
+4 -12
View File
@@ -33,18 +33,10 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) { void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
switch (width) { switch (width) {
case 1: case sizeof(u64): return memory.Write64(addr, value);
memory.Write8(addr, static_cast<u8>(value)); case sizeof(u32): return memory.Write32(addr, u32(value));
break; case sizeof(u16): return memory.Write16(addr, u16(value));
case 2: case sizeof(u8): return memory.Write8(addr, u8(value));
memory.Write16(addr, static_cast<u16>(value));
break;
case 4:
memory.Write32(addr, static_cast<u32>(value));
break;
case 8:
memory.Write64(addr, value);
break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
@@ -24,29 +24,65 @@
namespace Dynarmic::Backend::Arm64 { namespace Dynarmic::Backend::Arm64 {
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this; oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>(); void* target = code.xptr<void*>();
// params = { this, vaddr, bitsize }
code.LDR(X0, l_this); code.LDR(X0, l_this);
// X1 = vaddr
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0); code.BR(Xscratch0);
code.align(8); code.align(8);
code.l(l_this); code.l(l_this);
code.dx(info.this_ptr); code.dx(info.this_ptr);
code.l(l_addr); code.l(l_addr);
code.dx(info.fn_ptr); code.dx(info.fn_ptr);
return target; return target;
} }
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>();
// params = { this, vaddr, value, bitsize }
code.LDR(X0, l_this);
// X1 = vaddr
// X2 = value
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
code.align(8);
code.l(l_this);
code.dx(info.this_ptr);
code.l(l_addr);
code.dx(info.fn_ptr);
return target;
}
template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>();
code.LDR(X0, l_this);
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
code.align(8);
code.l(l_this);
code.dx(info.this_ptr);
code.l(l_addr);
code.dx(info.fn_ptr);
return target;
}
template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
@@ -59,6 +95,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
ABI_PushRegisters(code, save_regs, 0); ABI_PushRegisters(code, save_regs, 0);
code.LDR(X0, l_this); code.LDR(X0, l_this);
code.MOV(X1, Xscratch0); code.MOV(X1, Xscratch0);
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0); code.BLR(Xscratch0);
code.MOV(Xscratch0, X0); code.MOV(Xscratch0, X0);
@@ -82,7 +119,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T { auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T { return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr); return (conf.callbacks->*callback)(vaddr, sizeof(T));
}); });
}; };
@@ -101,7 +138,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
} }
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
@@ -115,6 +152,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
code.LDR(X0, l_this); code.LDR(X0, l_this);
code.MOV(X1, Xscratch0); code.MOV(X1, Xscratch0);
code.MOV(X2, Xscratch1); code.MOV(X2, Xscratch1);
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0); code.BLR(Xscratch0);
ABI_PopRegisters(code, save_regs, 0); ABI_PopRegisters(code, save_regs, 0);
@@ -136,12 +174,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
oaknut::Label l_addr, l_this; oaknut::Label l_addr, l_this;
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 { auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected); return (conf.callbacks->*callback)(vaddr, value, expected);
}) }) ? 0 : 1;
? 0
: 1;
}; };
void* target = code.xptr<void*>(); void* target = code.xptr<void*>();
@@ -179,26 +214,27 @@ void A32AddressSpace::EmitPrelude() {
UnprotectCodeMemory(); UnprotectCodeMemory();
prelude_info.read_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks); prelude_info.read_memory_8 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks); prelude_info.read_memory_16 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks); prelude_info.read_memory_32 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks); prelude_info.read_memory_64 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks); prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks); prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks); prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf); prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf); prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf); prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf); prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf);
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks); prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf);
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks); prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks); prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks); prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks); prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks); prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks); prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks); prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf); prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf); prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf); prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
@@ -23,29 +23,65 @@
namespace Dynarmic::Backend::Arm64 { namespace Dynarmic::Backend::Arm64 {
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this; oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>(); void* target = code.xptr<void*>();
// params = { this, vaddr, bitsize }
code.LDR(X0, l_this); code.LDR(X0, l_this);
// X1 = vaddr
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0); code.BR(Xscratch0);
code.align(8); code.align(8);
code.l(l_this); code.l(l_this);
code.dx(info.this_ptr); code.dx(info.this_ptr);
code.l(l_addr); code.l(l_addr);
code.dx(info.fn_ptr); code.dx(info.fn_ptr);
return target; return target;
} }
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>();
// params = { this, vaddr, value, bitsize }
code.LDR(X0, l_this);
// X1 = vaddr
// X2 = value
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
code.align(8);
code.l(l_this);
code.dx(info.this_ptr);
code.l(l_addr);
code.dx(info.fn_ptr);
return target;
}
template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>();
code.LDR(X0, l_this);
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
code.align(8);
code.l(l_this);
code.dx(info.this_ptr);
code.l(l_addr);
code.dx(info.fn_ptr);
return target;
}
template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
@@ -58,6 +94,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
ABI_PushRegisters(code, save_regs, 0); ABI_PushRegisters(code, save_regs, 0);
code.LDR(X0, l_this); code.LDR(X0, l_this);
code.MOV(X1, Xscratch0); code.MOV(X1, Xscratch0);
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0); code.BLR(Xscratch0);
code.MOV(Xscratch0, X0); code.MOV(Xscratch0, X0);
@@ -81,7 +118,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T { auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T { return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr); return (conf.callbacks->*callback)(vaddr, sizeof(T));
}); });
}; };
@@ -100,7 +137,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
} }
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
@@ -114,6 +151,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
code.LDR(X0, l_this); code.LDR(X0, l_this);
code.MOV(X1, Xscratch0); code.MOV(X1, Xscratch0);
code.MOV(X2, Xscratch1); code.MOV(X2, Xscratch1);
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0); code.BLR(Xscratch0);
ABI_PopRegisters(code, save_regs, 0); ABI_PopRegisters(code, save_regs, 0);
@@ -133,14 +171,10 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
using namespace oaknut::util; using namespace oaknut::util;
oaknut::Label l_addr, l_this; oaknut::Label l_addr, l_this;
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 { auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected); return (conf.callbacks->*callback)(vaddr, value, expected);
}) }) ? 0 : 1;
? 0
: 1;
}; };
void* target = code.xptr<void*>(); void* target = code.xptr<void*>();
@@ -346,30 +380,30 @@ void A64AddressSpace::EmitPrelude() {
UnprotectCodeMemory(); UnprotectCodeMemory();
prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks); prelude_info.read_memory_8 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks); prelude_info.read_memory_16 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks); prelude_info.read_memory_32 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks); prelude_info.read_memory_64 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks); prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks);
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks); prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks); prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks); prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks); prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks); prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead8, u8>(code, conf); prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead16, u16>(code, conf); prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead32, u32>(code, conf); prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead64, u64>(code, conf); prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf);
prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf); prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf);
prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks); prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks); prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks); prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks); prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks); prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks);
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks); prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks); prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks); prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks); prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks); prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks);
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf); prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf); prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
@@ -145,6 +145,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::ReturnFromRunCode: case LinkTarget::ReturnFromRunCode:
c.B(prelude_info.return_from_run_code); c.B(prelude_info.return_from_run_code);
break; break;
// { this, vaddr, size }
case LinkTarget::ReadMemory8: case LinkTarget::ReadMemory8:
c.BL(prelude_info.read_memory_8); c.BL(prelude_info.read_memory_8);
break; break;
@@ -160,6 +161,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::ReadMemory128: case LinkTarget::ReadMemory128:
c.BL(prelude_info.read_memory_128); c.BL(prelude_info.read_memory_128);
break; break;
// { this, vaddr, size }
case LinkTarget::WrappedReadMemory8: case LinkTarget::WrappedReadMemory8:
c.BL(prelude_info.wrapped_read_memory_8); c.BL(prelude_info.wrapped_read_memory_8);
break; break;
@@ -175,6 +177,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::WrappedReadMemory128: case LinkTarget::WrappedReadMemory128:
c.BL(prelude_info.wrapped_read_memory_128); c.BL(prelude_info.wrapped_read_memory_128);
break; break;
//
case LinkTarget::ExclusiveReadMemory8: case LinkTarget::ExclusiveReadMemory8:
c.BL(prelude_info.exclusive_read_memory_8); c.BL(prelude_info.exclusive_read_memory_8);
break; break;
@@ -190,6 +193,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::ExclusiveReadMemory128: case LinkTarget::ExclusiveReadMemory128:
c.BL(prelude_info.exclusive_read_memory_128); c.BL(prelude_info.exclusive_read_memory_128);
break; break;
// { this, vaddr, value, size }
case LinkTarget::WriteMemory8: case LinkTarget::WriteMemory8:
c.BL(prelude_info.write_memory_8); c.BL(prelude_info.write_memory_8);
break; break;
@@ -205,6 +209,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::WriteMemory128: case LinkTarget::WriteMemory128:
c.BL(prelude_info.write_memory_128); c.BL(prelude_info.write_memory_128);
break; break;
//
case LinkTarget::WrappedWriteMemory8: case LinkTarget::WrappedWriteMemory8:
c.BL(prelude_info.wrapped_write_memory_8); c.BL(prelude_info.wrapped_write_memory_8);
break; break;
@@ -220,6 +225,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::WrappedWriteMemory128: case LinkTarget::WrappedWriteMemory128:
c.BL(prelude_info.wrapped_write_memory_128); c.BL(prelude_info.wrapped_write_memory_128);
break; break;
//
case LinkTarget::ExclusiveWriteMemory8: case LinkTarget::ExclusiveWriteMemory8:
c.BL(prelude_info.exclusive_write_memory_8); c.BL(prelude_info.exclusive_write_memory_8);
break; break;
@@ -31,16 +31,16 @@ using namespace Xbyak::util;
void A32EmitX64::GenFastmemFallbacks() { void A32EmitX64::GenFastmemFallbacks() {
const std::initializer_list<int> idxes{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; const std::initializer_list<int> idxes{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{ const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryRead8>(conf.callbacks)}, {8, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryRead16>(conf.callbacks)}, {16, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryRead32>(conf.callbacks)}, {32, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryRead64>(conf.callbacks)}, {64, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
}}; }};
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{ const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite8>(conf.callbacks)}, {8, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite16>(conf.callbacks)}, {16, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite32>(conf.callbacks)}, {32, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite64>(conf.callbacks)}, {64, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
}}; }};
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{ const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)}, {8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
@@ -56,12 +56,12 @@ void A32EmitX64::GenFastmemFallbacks() {
code.align(); code.align();
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>(); read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx)); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
// params = { this, vaddr, size }
if (vaddr_idx != code.ABI_PARAM2.getIdx()) { if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx}); code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
} }
if (ordered) { code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
code.mfence(); if (ordered) code.mfence();
}
callback.EmitCall(code); callback.EmitCall(code);
if (value_idx != code.ABI_RETURN.getIdx()) { if (value_idx != code.ABI_RETURN.getIdx()) {
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN); code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
@@ -76,6 +76,7 @@ void A32EmitX64::GenFastmemFallbacks() {
code.align(); code.align();
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>(); write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
// params = { this, vaddr, value, size }
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) { if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3); code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) { } else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
@@ -92,10 +93,9 @@ void A32EmitX64::GenFastmemFallbacks() {
} }
} }
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3); code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
callback.EmitCall(code); callback.EmitCall(code);
if (ordered) { if (ordered) code.mfence();
code.mfence();
}
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code.ret(); code.ret();
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize)); PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize));
@@ -138,35 +138,35 @@ void A32EmitX64::GenFastmemFallbacks() {
#undef Axx #undef Axx
void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} }
void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} }
void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} }
void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} }
void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst); EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite16>(ctx, inst); EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst); EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite64>(ctx, inst); EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) { void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
@@ -175,33 +175,33 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) { if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} else { } else {
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} }
} }
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) { if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} else { } else {
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} }
} }
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) { if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} else { } else {
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} }
} }
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) { if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} else { } else {
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} }
} }
@@ -115,16 +115,16 @@ void A64EmitX64::GenMemory128Accessors() {
void A64EmitX64::GenFastmemFallbacks() { void A64EmitX64::GenFastmemFallbacks() {
const std::initializer_list<int> idxes{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; const std::initializer_list<int> idxes{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{ const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryRead8>(conf.callbacks)}, {8, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryRead16>(conf.callbacks)}, {16, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryRead32>(conf.callbacks)}, {32, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryRead64>(conf.callbacks)}, {64, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
}}; }};
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{ const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite8>(conf.callbacks)}, {8, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite16>(conf.callbacks)}, {16, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite32>(conf.callbacks)}, {32, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite64>(conf.callbacks)}, {64, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
}}; }};
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{ const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)}, {8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
@@ -204,12 +204,12 @@ void A64EmitX64::GenFastmemFallbacks() {
code.align(); code.align();
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>(); read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx)); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
// params = { this, vaddr, size }
if (vaddr_idx != code.ABI_PARAM2.getIdx()) { if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx}); code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
} }
if (ordered) { code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
code.mfence(); if (ordered) code.mfence();
}
callback.EmitCall(code); callback.EmitCall(code);
if (value_idx != code.ABI_RETURN.getIdx()) { if (value_idx != code.ABI_RETURN.getIdx()) {
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN); code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
@@ -224,6 +224,7 @@ void A64EmitX64::GenFastmemFallbacks() {
code.align(); code.align();
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>(); write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
// params = { this, vaddr, value, size }
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) { if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3); code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) { } else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
@@ -240,10 +241,9 @@ void A64EmitX64::GenFastmemFallbacks() {
} }
} }
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3); code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
callback.EmitCall(code); callback.EmitCall(code);
if (ordered) { if (ordered) code.mfence();
code.mfence();
}
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code.ret(); code.ret();
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize)); PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize));
@@ -286,19 +286,19 @@ void A64EmitX64::GenFastmemFallbacks() {
#undef Axx #undef Axx
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} }
void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} }
void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} }
void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} }
void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
@@ -306,23 +306,23 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
} }
void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite8>(ctx, inst); EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite16>(ctx, inst); EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite32>(ctx, inst); EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite64>(ctx, inst); EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite64>(ctx, inst); EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
} }
void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) { void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
@@ -331,33 +331,33 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) { if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} else { } else {
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} }
} }
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) { if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} else { } else {
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} }
} }
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) { if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} else { } else {
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} }
} }
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) { if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} else { } else {
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} }
} }
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage * Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
@@ -56,16 +59,13 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
// Neither fastmem nor page table: Use callbacks // Neither fastmem nor page table: Use callbacks
if constexpr (bitsize == 128) { if constexpr (bitsize == 128) {
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]); ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]);
if (ordered) { if (ordered) code.mfence();
code.mfence();
}
code.CallFunction(memory_read_128); code.CallFunction(memory_read_128);
ctx.reg_alloc.DefineValue(code, inst, xmm1); ctx.reg_alloc.DefineValue(code, inst, xmm1);
} else { } else {
ctx.reg_alloc.HostCall(code, inst, {}, args[1]); ctx.reg_alloc.HostCall(code, inst, {}, args[1]);
if (ordered) { code.mov(code.ABI_PARAM3.cvt32(), bitsize / CHAR_BIT);
code.mfence(); if (ordered) code.mfence();
}
Devirtualize<callback>(conf.callbacks).EmitCall(code); Devirtualize<callback>(conf.callbacks).EmitCall(code);
code.ZeroExtendFrom(bitsize, code.ABI_RETURN); code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
} }
@@ -148,12 +148,12 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.HostCall(code, nullptr); ctx.reg_alloc.HostCall(code, nullptr);
code.CallFunction(memory_write_128); code.CallFunction(memory_write_128);
} else { } else {
// { this, vaddr, value, size }
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]); ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]);
code.mov(code.ABI_PARAM4.cvt32(), bitsize / CHAR_BIT);
Devirtualize<callback>(conf.callbacks).EmitCall(code); Devirtualize<callback>(conf.callbacks).EmitCall(code);
} }
if (ordered) { if (ordered) code.mfence();
code.mfence();
}
EmitCheckMemoryAbort(ctx, inst); EmitCheckMemoryAbort(ctx, inst);
return; return;
} }
@@ -230,10 +230,9 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) { if (ordered) {
code.mfence(); code.mfence();
} }
code.CallLambda( code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
[](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T { return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr); return (conf.callbacks->*callback)(vaddr, bitsize / CHAR_BIT);
}); });
}); });
code.ZeroExtendFrom(bitsize, code.ABI_RETURN); code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
@@ -250,8 +249,7 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) { if (ordered) {
code.mfence(); code.mfence();
} }
code.CallLambda( code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
[](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector { ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
return (conf.callbacks->*callback)(vaddr); return (conf.callbacks->*callback)(vaddr);
}); });
@@ -66,7 +66,9 @@ struct UserCallbacks : public TranslateCallbacks {
// All reads through this callback are 4-byte aligned. // All reads through this callback are 4-byte aligned.
// Memory must be interpreted as little endian. // Memory must be interpreted as little endian.
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); } std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t)));
}
// This function is called before the instruction at pc is read. // This function is called before the instruction at pc is read.
// IR code can be emitted by the callee prior to instruction handling. // IR code can be emitted by the callee prior to instruction handling.
@@ -80,16 +82,10 @@ struct UserCallbacks : public TranslateCallbacks {
// Reads through these callbacks may not be aligned. // Reads through these callbacks may not be aligned.
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT. // Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0; virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0;
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
// Writes through these callbacks may not be aligned. // Writes through these callbacks may not be aligned.
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0; virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0;
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
// Writes through these callbacks may not be aligned. // Writes through these callbacks may not be aligned.
virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; } virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; }
@@ -89,20 +89,16 @@ struct UserCallbacks {
// All reads through this callback are 4-byte aligned. // All reads through this callback are 4-byte aligned.
// Memory must be interpreted as little endian. // Memory must be interpreted as little endian.
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); } virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) {
return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t)));
}
// Reads through these callbacks may not be aligned. // Reads through these callbacks may not be aligned.
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0; virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0;
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
virtual Vector MemoryRead128(VAddr vaddr) = 0; virtual Vector MemoryRead128(VAddr vaddr) = 0;
// Writes through these callbacks may not be aligned. // Writes through these callbacks may not be aligned.
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0; virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0;
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0; virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0;
// Writes through these callbacks may not be aligned. // Writes through these callbacks may not be aligned.
+4 -4
View File
@@ -40,7 +40,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) { if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32(); const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) { if (cb->IsReadOnlyMemory(vaddr)) {
const u8 value_from_memory = cb->MemoryRead8(vaddr); const u8 value_from_memory = u8(cb->MemoryRead(vaddr, sizeof(u8)));
inst.ReplaceUsesWith(IR::Value{value_from_memory}); inst.ReplaceUsesWith(IR::Value{value_from_memory});
} }
} }
@@ -51,7 +51,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) { if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32(); const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) { if (cb->IsReadOnlyMemory(vaddr)) {
const u16 value_from_memory = cb->MemoryRead16(vaddr); const u16 value_from_memory = u16(cb->MemoryRead(vaddr, sizeof(u16)));
inst.ReplaceUsesWith(IR::Value{value_from_memory}); inst.ReplaceUsesWith(IR::Value{value_from_memory});
} }
} }
@@ -62,7 +62,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) { if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32(); const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) { if (cb->IsReadOnlyMemory(vaddr)) {
const u32 value_from_memory = cb->MemoryRead32(vaddr); const u32 value_from_memory = u32(cb->MemoryRead(vaddr, sizeof(u32)));
inst.ReplaceUsesWith(IR::Value{value_from_memory}); inst.ReplaceUsesWith(IR::Value{value_from_memory});
} }
} }
@@ -73,7 +73,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) { if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32(); const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) { if (cb->IsReadOnlyMemory(vaddr)) {
const u64 value_from_memory = cb->MemoryRead64(vaddr); const u64 value_from_memory = u64(cb->MemoryRead(vaddr, sizeof(u64)));
inst.ReplaceUsesWith(IR::Value{value_from_memory}); inst.ReplaceUsesWith(IR::Value{value_from_memory});
} }
} }
+1 -1
View File
@@ -503,7 +503,7 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
} }
} }
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") { TEST_CASE("Verify fix for off by one error in MemoryRead<32> worked", "[Thumb][Thumb16]") {
ThumbTestEnv test_env; ThumbTestEnv test_env;
// Prepare test subjects // Prepare test subjects
@@ -553,9 +553,9 @@ TEST_CASE("arm: Memory access (fastmem)", "[arm][A32]") {
memset(backing_memory, 0, memory_size); memset(backing_memory, 0, memory_size);
memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57); memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57);
env.MemoryWrite32(0, 0xE5904000); // LDR R4, [R0] env.MemoryWrite(0, 0xE5904000, sizeof(u32)); // LDR R4, [R0]
env.MemoryWrite32(4, 0xE5814000); // STR R4, [R1] env.MemoryWrite(4, 0xE5814000, sizeof(u32)); // STR R4, [R1]
env.MemoryWrite32(8, 0xEAFFFFFE); // B . env.MemoryWrite(8, 0xEAFFFFFE, sizeof(u32)); // B .
jit.Regs()[0] = 0x100; jit.Regs()[0] = 0x100;
jit.Regs()[1] = 0x1F0; jit.Regs()[1] = 0x1F0;
jit.Regs()[15] = 0; // PC = 0 jit.Regs()[15] = 0; // PC = 0
+60 -55
View File
@@ -16,6 +16,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
#include "dynarmic/interface/A32/a32.h" #include "dynarmic/interface/A32/a32.h"
template<typename InstructionType_, u32 infinite_loop_u32> template<typename InstructionType_, u32 infinite_loop_u32>
@@ -59,42 +60,51 @@ public:
return infinite_loop_u32; // B . return infinite_loop_u32; // B .
} }
std::uint8_t MemoryRead8(u32 vaddr) override { u64 MemoryRead(u32 vaddr, size_t size) override {
if (IsInCodeMem(vaddr)) { switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (IsInCodeMem(vaddr))
return reinterpret_cast<u8*>(code_mem.data())[vaddr]; return reinterpret_cast<u8*>(code_mem.data())[vaddr];
} if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end())
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
return iter->second; return iter->second;
return u8(vaddr);
} }
return static_cast<u8>(vaddr); default:
std::abort();
} }
std::uint16_t MemoryRead16(u32 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
std::uint32_t MemoryRead32(u32 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u32 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
} }
void MemoryWrite8(u32 vaddr, std::uint8_t value) override { void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override {
if (vaddr < code_mem.size() * sizeof(u32)) { switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
if (vaddr < code_mem.size() * sizeof(u32))
code_mem_modified_by_guest = true; code_mem_modified_by_guest = true;
}
modified_memory[vaddr] = value; modified_memory[vaddr] = value;
break;
default:
std::abort();
} }
void MemoryWrite16(u32 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, static_cast<u8>(value));
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
}
void MemoryWrite32(u32 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, static_cast<u16>(value));
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
}
void MemoryWrite64(u32 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, static_cast<u32>(value));
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
} }
void CallSVC(std::uint32_t swi) override { void CallSVC(std::uint32_t swi) override {
@@ -143,46 +153,41 @@ public:
return read<std::uint32_t>(vaddr); return read<std::uint32_t>(vaddr);
} }
std::uint8_t MemoryRead8(std::uint32_t vaddr) override { u64 MemoryRead(u32 vaddr, size_t size) override {
return read<std::uint8_t>(vaddr); switch (size) {
case sizeof(u64): return read<u64>(vaddr);
case sizeof(u32): return read<u32>(vaddr);
case sizeof(u16): return read<u16>(vaddr);
case sizeof(u8): return read<u8>(vaddr);
default:
std::abort();
} }
std::uint16_t MemoryRead16(std::uint32_t vaddr) override {
return read<std::uint16_t>(vaddr);
}
std::uint32_t MemoryRead32(std::uint32_t vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint64_t MemoryRead64(std::uint32_t vaddr) override {
return read<std::uint64_t>(vaddr);
} }
void MemoryWrite8(std::uint32_t vaddr, std::uint8_t value) override { void MemoryWrite(Dynarmic::A32::VAddr vaddr, std::uint64_t value, size_t size) override {
write(vaddr, value); switch (size) {
case sizeof(u64): return write<u64>(vaddr, u64(value));
case sizeof(u32): return write<u32>(vaddr, u32(value));
case sizeof(u16): return write<u16>(vaddr, u16(value));
case sizeof(u8): return write<u8>(vaddr, u8(value));
default: std::abort();
} }
void MemoryWrite16(std::uint32_t vaddr, std::uint16_t value) override {
write(vaddr, value);
}
void MemoryWrite32(std::uint32_t vaddr, std::uint32_t value) override {
write(vaddr, value);
}
void MemoryWrite64(std::uint32_t vaddr, std::uint64_t value) override {
write(vaddr, value);
} }
bool MemoryWriteExclusive8(std::uint32_t vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { bool MemoryWriteExclusive8(Dynarmic::A32::VAddr vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value); MemoryWrite(vaddr, value, sizeof(u8));
return true; return true;
} }
bool MemoryWriteExclusive16(std::uint32_t vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { bool MemoryWriteExclusive16(Dynarmic::A32::VAddr vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value); MemoryWrite(vaddr, value, sizeof(u16));
return true; return true;
} }
bool MemoryWriteExclusive32(std::uint32_t vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { bool MemoryWriteExclusive32(Dynarmic::A32::VAddr vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value); MemoryWrite(vaddr, value, sizeof(u32));
return true; return true;
} }
bool MemoryWriteExclusive64(std::uint32_t vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { bool MemoryWriteExclusive64(Dynarmic::A32::VAddr vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value); MemoryWrite(vaddr, value, sizeof(u64));
return true; return true;
} }
File diff suppressed because one or more lines are too long
+40 -33
View File
@@ -25,48 +25,55 @@ public:
u64 ticks_left = 0; u64 ticks_left = 0;
::Common::unordered_map<u64, u8> memory{}; ::Common::unordered_map<u64, u8> memory{};
u8 MemoryRead8(u64 vaddr) override { u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8):
return memory[vaddr]; return memory[vaddr];
default:
std::abort();
} }
u16 MemoryRead16(u64 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
u32 MemoryRead32(u64 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
u64 MemoryRead64(u64 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
} }
std::array<u64, 2> MemoryRead128(u64 vaddr) override { std::array<u64, 2> MemoryRead128(u64 vaddr) override {
return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)}; return {
MemoryRead(vaddr, sizeof(u64)),
MemoryRead(vaddr + sizeof(u64), sizeof(u64))
};
} }
void MemoryWrite8(u64 vaddr, u8 value) override { void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
memory[vaddr] = value; memory[vaddr] = value;
break;
default:
std::abort();
} }
void MemoryWrite16(u64 vaddr, u16 value) override {
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
} }
void MemoryWrite32(u64 vaddr, u32 value) override {
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
void MemoryWrite64(u64 vaddr, u64 value) override {
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
}
void MemoryWrite128(u64 vaddr, std::array<u64, 2> value) override { void MemoryWrite128(u64 vaddr, std::array<u64, 2> value) override {
MemoryWrite64(vaddr, value[0]); MemoryWrite(vaddr, value[0], sizeof(u64));
MemoryWrite64(vaddr + 8, value[1]); MemoryWrite(vaddr + 8, value[1], sizeof(u64));
} }
void CallSVC(u32) override { void CallSVC(u32) override {
@@ -135,9 +142,9 @@ TEST_CASE("A64: fibonacci", "[a64]") {
code.RET(); code.RET();
for (size_t i = 0; i < 1024; i++) { for (size_t i = 0; i < 1024; i++) {
env.MemoryWrite32(i * 4, instructions[i]); env.MemoryWrite(i * 4, instructions[i], sizeof(u32));
} }
env.MemoryWrite32(8888, 0xd4200000); env.MemoryWrite(8888, 0xd4200000, sizeof(u32));
cpu.SetRegister(30, 8888); cpu.SetRegister(30, 8888);
cpu.SetRegister(0, 10); cpu.SetRegister(0, 10);
+65 -55
View File
@@ -12,6 +12,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "dynarmic/interface/A64/a64.h" #include "dynarmic/interface/A64/a64.h"
#include "dynarmic/interface/A64/config.h"
using Vector = Dynarmic::A64::Vector; using Vector = Dynarmic::A64::Vector;
@@ -34,64 +35,79 @@ public:
return code_mem[index]; return code_mem[index];
} }
std::uint8_t MemoryRead8(u64 vaddr) override { u64 MemoryRead(u64 vaddr, size_t size) override {
if (IsInCodeMem(vaddr)) { switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (IsInCodeMem(vaddr))
return reinterpret_cast<u8*>(code_mem.data())[vaddr - code_mem_start_address]; return reinterpret_cast<u8*>(code_mem.data())[vaddr - code_mem_start_address];
}
if (auto const it = modified_memory.find(vaddr); it != modified_memory.end()) if (auto const it = modified_memory.find(vaddr); it != modified_memory.end())
return it->second; return it->second;
return u8(vaddr); return u8(vaddr);
} }
std::uint16_t MemoryRead16(u64 vaddr) override { default:
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; std::abort();
} }
std::uint32_t MemoryRead32(u64 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u64 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
Vector MemoryRead128(u64 vaddr) override {
return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)};
} }
void MemoryWrite8(u64 vaddr, std::uint8_t value) override { Vector MemoryRead128(u64 vaddr) override {
return {
MemoryRead(vaddr, sizeof(u64)),
MemoryRead(vaddr + 8, sizeof(u64))
};
}
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
if (IsInCodeMem(vaddr)) { if (IsInCodeMem(vaddr)) {
code_mem_modified_by_guest = true; code_mem_modified_by_guest = true;
} }
modified_memory[vaddr] = value; modified_memory[vaddr] = value;
break;
default:
std::abort();
} }
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
} }
void MemoryWrite128(u64 vaddr, Vector value) override { void MemoryWrite128(u64 vaddr, Vector value) override {
MemoryWrite64(vaddr, value[0]); MemoryWrite(vaddr, value[0], sizeof(u64));
MemoryWrite64(vaddr + 8, value[1]); MemoryWrite(vaddr + 8, value[1], sizeof(u64));
} }
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value); MemoryWrite(vaddr, value, sizeof(u8));
return true; return true;
} }
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value); MemoryWrite(vaddr, value, sizeof(u16));
return true; return true;
} }
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value); MemoryWrite(vaddr, value, sizeof(u32));
return true; return true;
} }
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value); MemoryWrite(vaddr, value, sizeof(u64));
return true; return true;
} }
bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override { bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override {
@@ -145,52 +161,46 @@ public:
return read<std::uint32_t>(vaddr); return read<std::uint32_t>(vaddr);
} }
std::uint8_t MemoryRead8(u64 vaddr) override { u64 MemoryRead(u64 vaddr, size_t size) override {
return read<std::uint8_t>(vaddr); switch (size) {
case sizeof(u64): return read<u64>(vaddr);
case sizeof(u32): return read<u32>(vaddr);
case sizeof(u16): return read<u16>(vaddr);
case sizeof(u8): return read<u8>(vaddr);
default: std::abort();
} }
std::uint16_t MemoryRead16(u64 vaddr) override {
return read<std::uint16_t>(vaddr);
}
std::uint32_t MemoryRead32(u64 vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint64_t MemoryRead64(u64 vaddr) override {
return read<std::uint64_t>(vaddr);
} }
Vector MemoryRead128(u64 vaddr) override { Vector MemoryRead128(u64 vaddr) override {
return read<Vector>(vaddr); return read<Vector>(vaddr);
} }
void MemoryWrite8(u64 vaddr, std::uint8_t value) override { void MemoryWrite(u64 vaddr, std::uint64_t value, size_t size) override {
write(vaddr, value); switch (size) {
case sizeof(u64): return write<u64>(vaddr, u64(value));
case sizeof(u32): return write<u32>(vaddr, u32(value));
case sizeof(u16): return write<u16>(vaddr, u16(value));
case sizeof(u8): return write<u8>(vaddr, u8(value));
default: std::abort();
} }
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
write(vaddr, value);
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
write(vaddr, value);
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
write(vaddr, value);
} }
void MemoryWrite128(u64 vaddr, Vector value) override { void MemoryWrite128(u64 vaddr, Vector value) override {
write(vaddr, value); write(vaddr, value);
} }
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value); MemoryWrite(vaddr, value, sizeof(u8));
return true; return true;
} }
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value); MemoryWrite(vaddr, value, sizeof(u16));
return true; return true;
} }
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value); MemoryWrite(vaddr, value, sizeof(u32));
return true; return true;
} }
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value); MemoryWrite(vaddr, value, sizeof(u64));
return true; return true;
} }
bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override { bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override {
+38 -26
View File
@@ -18,6 +18,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <fmt/ostream.h> #include <fmt/ostream.h>
#include <fmt/ranges.h> #include <fmt/ranges.h>
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
#include "dynarmic/mcl/bit.hpp" #include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h" #include "common/common_types.h"
@@ -118,36 +119,47 @@ public:
u64 ticks_left = 0; u64 ticks_left = 0;
std::map<u32, u8> memory; std::map<u32, u8> memory;
std::uint8_t MemoryRead8(u32 vaddr) override { u64 MemoryRead(Dynarmic::A32::VAddr vaddr, size_t size) override {
if (auto iter = memory.find(vaddr); iter != memory.end()) { switch (size) {
return iter->second; case sizeof(u64):
} return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (auto const it = memory.find(vaddr); it != memory.end())
return it->second;
return 0; return 0;
} }
std::uint16_t MemoryRead16(u32 vaddr) override { default:
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; std::abort();
} }
std::uint32_t MemoryRead32(u32 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u32 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
} }
void MemoryWrite8(u32 vaddr, std::uint8_t value) override { void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
memory[vaddr] = value; memory[vaddr] = value;
break;
default:
std::abort();
} }
void MemoryWrite16(u32 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, static_cast<u8>(value));
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
}
void MemoryWrite32(u32 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, static_cast<u16>(value));
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
}
void MemoryWrite64(u32 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, static_cast<u32>(value));
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
} }
void CallSVC(std::uint32_t swi) override { void CallSVC(std::uint32_t swi) override {
@@ -231,7 +243,7 @@ void ExecuteA32Instruction(u32 instruction) {
if (const auto address = get_value()) { if (const auto address = get_value()) {
fmt::print("value: "); fmt::print("value: ");
if (const auto value = get_value()) { if (const auto value = get_value()) {
env.MemoryWrite32(*address, *value); env.MemoryWrite(*address, *value, sizeof(u32));
fmt::print("> mem[{:#08x}] = {:#08x}\n", *address, *value); fmt::print("> mem[{:#08x}] = {:#08x}\n", *address, *value);
} }
} }
@@ -247,8 +259,8 @@ void ExecuteA32Instruction(u32 instruction) {
cpu.SetFpscr(fpscr); cpu.SetFpscr(fpscr);
const u32 initial_pc = regs[15]; const u32 initial_pc = regs[15];
env.MemoryWrite32(initial_pc + 0, instruction); env.MemoryWrite(initial_pc + 0, instruction, sizeof(u32));
env.MemoryWrite32(initial_pc + 4, 0xEAFFFFFE); // B +0 env.MemoryWrite(initial_pc + 4, 0xEAFFFFFE, sizeof(u32)); // B +0
cpu.Run(); cpu.Run();
fmt::print("{}", fmt::join(cpu.Disassemble(), "\n")); fmt::print("{}", fmt::join(cpu.Disassemble(), "\n"));
+2 -19
View File
@@ -290,7 +290,7 @@ bool A32Unicorn<TestEnvironment>::UnmappedMemoryHook(uc_engine* uc, uc_mem_type
auto page = std::make_unique<Page>(); auto page = std::make_unique<Page>();
page->address = base_address; page->address = base_address;
for (size_t i = 0; i < page->data.size(); ++i) for (size_t i = 0; i < page->data.size(); ++i)
page->data[i] = this_->testenv.MemoryRead8(static_cast<u32>(base_address + i)); page->data[i] = u8(this_->testenv.MemoryRead(u32(base_address + i), sizeof(u8)));
uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data()); uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data());
if (err == UC_ERR_MAP) if (err == UC_ERR_MAP)
@@ -321,24 +321,7 @@ bool A32Unicorn<TestEnvironment>::UnmappedMemoryHook(uc_engine* uc, uc_mem_type
template<class TestEnvironment> template<class TestEnvironment>
bool A32Unicorn<TestEnvironment>::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) { bool A32Unicorn<TestEnvironment>::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) {
auto* this_ = static_cast<A32Unicorn*>(user_data); auto* this_ = static_cast<A32Unicorn*>(user_data);
this_->testenv.MemoryWrite(start_address, value, size);
switch (size) {
case 1:
this_->testenv.MemoryWrite8(start_address, static_cast<u8>(value));
break;
case 2:
this_->testenv.MemoryWrite16(start_address, static_cast<u16>(value));
break;
case 4:
this_->testenv.MemoryWrite32(start_address, static_cast<u32>(value));
break;
case 8:
this_->testenv.MemoryWrite64(start_address, value);
break;
default:
UNREACHABLE();
}
return true; return true;
} }
+2 -19
View File
@@ -197,7 +197,7 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta
auto page = std::make_unique<Page>(); auto page = std::make_unique<Page>();
page->address = base_address; page->address = base_address;
for (size_t i = 0; i < page->data.size(); ++i) for (size_t i = 0; i < page->data.size(); ++i)
page->data[i] = this_->testenv.MemoryRead8(base_address + i); page->data[i] = u8(this_->testenv.MemoryRead(base_address + i, sizeof(u8)));
uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data()); uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data());
if (err == UC_ERR_MAP) if (err == UC_ERR_MAP)
@@ -227,23 +227,6 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta
bool A64Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) { bool A64Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) {
auto* this_ = static_cast<A64Unicorn*>(user_data); auto* this_ = static_cast<A64Unicorn*>(user_data);
this_->testenv.MemoryWrite(start_address, value, size);
switch (size) {
case 1:
this_->testenv.MemoryWrite8(start_address, static_cast<u8>(value));
break;
case 2:
this_->testenv.MemoryWrite16(start_address, static_cast<u16>(value));
break;
case 4:
this_->testenv.MemoryWrite32(start_address, static_cast<u32>(value));
break;
case 8:
this_->testenv.MemoryWrite64(start_address, value);
break;
default:
UNREACHABLE();
}
return true; return true;
} }
+21 -18
View File
@@ -45,6 +45,19 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
AbstractPad{hid_core_.kernel}, AbstractPad{hid_core_.kernel},
}} }}
{ {
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
auto& controller = controller_data[aruid_index][i];
controller.device = hid_core.GetEmulatedControllerByIndex(i);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
ControllerUpdate(*kernel, type, i);
},
.is_npad_service = true,
};
controller.callback_key = controller.device->SetCallback(engine_callback);
}
}
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) { for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i)); abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
} }
@@ -93,16 +106,6 @@ Result NPad::Activate(u64 aruid) {
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
auto& controller = controller_data[aruid_index][i]; auto& controller = controller_data[aruid_index][i];
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state; controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
controller.device = hid_core.GetEmulatedControllerByIndex(i);
if (!controller.callback_key) {
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
ControllerUpdate(hid_core.kernel, type, i);
},
.is_npad_service = true,
};
controller.callback_key = controller.device->SetCallback(engine_callback);
}
} }
// Prefill controller buffers // Prefill controller buffers
@@ -398,21 +401,21 @@ void NPad::InitNewlyAddedController(Kernel::KernelCore& kernel, u64 aruid, Core:
void NPad::WriteEmptyEntry(NpadInternalState* npad) { void NPad::WriteEmptyEntry(NpadInternalState* npad) {
NPadGenericState dummy_pad_state{}; NPadGenericState dummy_pad_state{};
NpadGcTriggerState dummy_gc_state{}; NpadGcTriggerState dummy_gc_state{};
dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->fullkey_lifo.WriteNextEntry(dummy_pad_state); npad->fullkey_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1; dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->handheld_lifo.WriteNextEntry(dummy_pad_state); npad->handheld_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state); npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1; dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->joy_left_lifo.WriteNextEntry(dummy_pad_state); npad->joy_left_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1; dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->joy_right_lifo.WriteNextEntry(dummy_pad_state); npad->joy_right_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1; dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->palma_lifo.WriteNextEntry(dummy_pad_state); npad->palma_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1; dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->system_ext_lifo.WriteNextEntry(dummy_pad_state); npad->system_ext_lifo.WriteNextEntry(dummy_pad_state);
dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
} }
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -28,11 +31,6 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>; using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
}; };
template <auto func, typename... Args>
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
}
template <typename ArgType> template <typename ArgType>
auto Arg(EmitContext& ctx, const IR::Value& arg) { auto Arg(EmitContext& ctx, const IR::Value& arg) {
if constexpr (std::is_same_v<ArgType, std::string_view>) { if constexpr (std::is_same_v<ArgType, std::string_view>) {
@@ -53,22 +51,11 @@ auto Arg(EmitContext& ctx, const IR::Value& arg) {
template <auto func, bool is_first_arg_inst, size_t... I> template <auto func, bool is_first_arg_inst, size_t... I>
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) { void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
using Traits = FuncTraits<decltype(func)>; using Traits = FuncTraits<decltype(func)>;
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
if constexpr (is_first_arg_inst) {
SetDefinition<func>(
ctx, inst, *inst,
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
} else {
SetDefinition<func>(
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
}
} else {
if constexpr (is_first_arg_inst) { if constexpr (is_first_arg_inst) {
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...); func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
} else { } else {
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...); func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
} }
}
} }
template <auto func> template <auto func>
+15 -8
View File
@@ -121,7 +121,7 @@ void BufferCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size
template <class P> template <class P>
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) { void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
if (memory_tracker.IsRegionGpuModified(device_addr, size)) { if (IsRegionGpuModified(device_addr, size)) {
ClearDownload(device_addr, size); ClearDownload(device_addr, size);
gpu_modified_ranges.Subtract(device_addr, size); gpu_modified_ranges.Subtract(device_addr, size);
} }
@@ -311,11 +311,8 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
MarkWrittenBuffer(buffer_id, device_addr, size); MarkWrittenBuffer(buffer_id, device_addr, size);
break; break;
case ObtainBufferOperation::DiscardWrite: { case ObtainBufferOperation::DiscardWrite: {
const DAddr device_addr_start = Common::AlignDown(device_addr, 64); ClearDownload(device_addr, size);
const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64); gpu_modified_ranges.Subtract(device_addr, size);
const size_t new_size = device_addr_end - device_addr_start;
ClearDownload(device_addr_start, new_size);
gpu_modified_ranges.Subtract(device_addr_start, new_size);
break; break;
} }
default: default:
@@ -1742,14 +1739,24 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
u64 total_size_bytes = 0; u64 total_size_bytes = 0;
u64 largest_copy = 0; u64 largest_copy = 0;
const DAddr buffer_start = buffer.cpu_addr_cached; const DAddr buffer_start = buffer.cpu_addr_cached;
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { const auto add_upload = [&](DAddr start, DAddr end) {
if (start == end) return;
const u64 range_size = end - start;
upload_copies.push_back(BufferCopy{ upload_copies.push_back(BufferCopy{
.src_offset = total_size_bytes, .src_offset = total_size_bytes,
.dst_offset = device_addr_out - buffer_start, .dst_offset = start - buffer_start,
.size = range_size, .size = range_size,
}); });
total_size_bytes += range_size; total_size_bytes += range_size;
largest_copy = (std::max)(largest_copy, range_size); largest_copy = (std::max)(largest_copy, range_size);
};
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
DAddr upload_start = device_addr_out;
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, [&](DAddr gpu_start, DAddr gpu_end) {
add_upload(upload_start, gpu_start);
upload_start = gpu_end;
});
add_upload(upload_start, device_addr_out + range_size);
}); });
if (total_size_bytes == 0) { if (total_size_bytes == 0) {
return true; return true;
+10 -5
View File
@@ -16,7 +16,7 @@
namespace Tegra { namespace Tegra {
constexpr u32 MacroRegistersStart = 0xE00; constexpr u32 MacroRegistersStart = 0xE00;
[[maybe_unused]] constexpr u32 ComputeInline = 0x6D; constexpr u32 ComputeInline = 0x6D;
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_) DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
: system{system_} : system{system_}
@@ -73,11 +73,16 @@ bool DmaPusher::Step() {
synced = false; synced = false;
} }
if (header.size > 0 && dma_state.method >= MacroRegistersStart && subchannels[dma_state.subchannel]) {
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, header.size * sizeof(u32));
}
if (header.size > 0) { if (header.size > 0) {
if (subchannels[dma_state.subchannel] && dma_state.method_count) {
const auto engine = subchannel_type[dma_state.subchannel];
const bool kepler_payload = engine == Engines::EngineTypes::KeplerCompute && dma_state.method == ComputeInline && dma_state.non_incrementing;
const bool macro_payload = engine == Engines::EngineTypes::Maxwell3D && dma_state.method >= MacroRegistersStart;
if (kepler_payload || macro_payload) {
const size_t words = std::min<size_t>(dma_state.method_count, header.size);
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, words * sizeof(u32));
}
}
const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe(); const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe();
if (use_safe) { if (use_safe) {
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers); Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
+10 -3
View File
@@ -49,13 +49,16 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
case KEPLER_COMPUTE_REG_INDEX(exec_upload): { case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
UploadInfo info{.upload_address = upload_address, UploadInfo info{.upload_address = upload_address,
.exec_address = upload_state.ExecTargetAddress(), .exec_address = upload_state.ExecTargetAddress(),
.copy_size = upload_state.GetUploadSize()}; .copy_size = upload_state.GetUploadSize(),
.was_dirty = upload_dirty};
uploads.push_back(info); uploads.push_back(info);
upload_state.ProcessExec(regs.exec_upload.linear != 0); upload_state.ProcessExec(regs.exec_upload.linear != 0);
break; break;
} }
case KEPLER_COMPUTE_REG_INDEX(data_upload): { case KEPLER_COMPUTE_REG_INDEX(data_upload): {
upload_address = current_dma_segment; upload_address = current_dma_segment;
upload_dirty = current_dirty;
current_dirty = false;
upload_state.ProcessData(method_argument, is_last_call); upload_state.ProcessData(method_argument, is_last_call);
break; break;
} }
@@ -64,11 +67,13 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
for (auto& data : uploads) { for (auto& data : uploads) {
const GPUVAddr offset = data.exec_address - launch_desc_loc; const GPUVAddr offset = data.exec_address - launch_desc_loc;
if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) && if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x)) {
memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) { const bool source_dirty = memory_manager.IsMemoryDirty(data.upload_address, data.copy_size);
if (data.was_dirty || source_dirty) {
indirect_compute = {data.upload_address}; indirect_compute = {data.upload_address};
} }
} }
}
uploads.clear(); uploads.clear();
ProcessLaunch(); ProcessLaunch();
indirect_compute = std::nullopt; indirect_compute = std::nullopt;
@@ -83,6 +88,8 @@ void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32*
switch (method) { switch (method) {
case KEPLER_COMPUTE_REG_INDEX(data_upload): case KEPLER_COMPUTE_REG_INDEX(data_upload):
upload_address = current_dma_segment; upload_address = current_dma_segment;
upload_dirty = current_dirty;
current_dirty = false;
upload_state.ProcessData(base_start, amount); upload_state.ProcessData(base_start, amount);
return; return;
default: default:
+2
View File
@@ -226,11 +226,13 @@ private:
VideoCore::RasterizerInterface* rasterizer = nullptr; VideoCore::RasterizerInterface* rasterizer = nullptr;
Upload::State upload_state; Upload::State upload_state;
GPUVAddr upload_address; GPUVAddr upload_address;
bool upload_dirty{};
struct UploadInfo { struct UploadInfo {
GPUVAddr upload_address; GPUVAddr upload_address;
GPUVAddr exec_address; GPUVAddr exec_address;
u32 copy_size; u32 copy_size;
bool was_dirty;
}; };
std::vector<UploadInfo> uploads; std::vector<UploadInfo> uploads;
std::optional<GPUVAddr> indirect_compute{}; std::optional<GPUVAddr> indirect_compute{};
@@ -94,11 +94,14 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width; const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
const bool use_accelerated = texture_info.has_value(); const bool use_accelerated = texture_info.has_value();
const bool is_applet =
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
Service::Nvnflinger::LayerStackId::Recording)) == 0;
RefreshResources(device, framebuffer); RefreshResources(device, framebuffer);
SetAntiAliasPass(device); SetAntiAliasPass(device);
#ifdef HAS_RESHADE #ifdef HAS_RESHADE
SetPostProcessPass(device); SetPostProcessPass(device, is_applet);
#endif #endif
// Finish any pending renderpass // Finish any pending renderpass
@@ -138,9 +141,12 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect); source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
crop_rect = {0, 0, 1, 1}; crop_rect = {0, 0, 1, 1};
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) { } else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect); if (!is_applet) {
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image,
source_image_view, render_extent, crop_rect);
crop_rect = {0, 0, 1, 1}; crop_rect = {0, 0, 1, 1};
} }
}
SetMatrixData(device, *out_push_constants, layout); SetMatrixData(device, *out_push_constants, layout);
SetVertexData(device, *out_push_constants, layout, crop_rect); SetVertexData(device, *out_push_constants, layout, crop_rect);
@@ -228,7 +234,11 @@ void Layer::SetAntiAliasPass(const Device& device) {
} }
#ifdef HAS_RESHADE #ifdef HAS_RESHADE
void Layer::SetPostProcessPass(const Device& device) { void Layer::SetPostProcessPass(const Device& device, bool is_applet) {
if (is_applet) {
post_process.reset();
return;
}
const VkExtent2D render_area{ const VkExtent2D render_area{
.width = Settings::values.resolution_info.ScaleUp(raw_width), .width = Settings::values.resolution_info.ScaleUp(raw_width),
.height = Settings::values.resolution_info.ScaleUp(raw_height), .height = Settings::values.resolution_info.ScaleUp(raw_height),
@@ -70,7 +70,7 @@ private:
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer); void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
void SetAntiAliasPass(const Device& device); void SetAntiAliasPass(const Device& device);
#ifdef HAS_RESHADE #ifdef HAS_RESHADE
void SetPostProcessPass(const Device& device); void SetPostProcessPass(const Device& device, bool is_applet);
#endif #endif
void ReleaseRawImages(); void ReleaseRawImages();
@@ -200,7 +200,7 @@ public:
if (host_visible) { if (host_visible) {
return StagingBufferRef{}; return StagingBufferRef{};
} }
return staging_pool.Request(size_bytes, MemoryUsage::Upload); return staging_pool.Request(device, size_bytes, MemoryUsage::Upload);
}(); }();
u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data(); u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
@@ -375,11 +375,11 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
} }
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) { StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
return staging_pool.Request(size, MemoryUsage::Upload); return staging_pool.Request(device, size, MemoryUsage::Upload);
} }
StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) { StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
return staging_pool.Request(size, MemoryUsage::Download, deferred); return staging_pool.Request(device, size, MemoryUsage::Download, deferred);
} }
VkFormat BufferCacheRuntime::TexelBufferFormat(VideoCore::Surface::PixelFormat format) const { VkFormat BufferCacheRuntime::TexelBufferFormat(VideoCore::Surface::PixelFormat format) const {
@@ -162,7 +162,7 @@ public:
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage, std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
[[maybe_unused]] u32 binding_index, [[maybe_unused]] u32 binding_index,
u32 size) { u32 size) {
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload); const StagingBufferRef ref = staging_pool.Request(device, size, MemoryUsage::Upload);
guest_descriptor_queue.AddBuffer(ref.buffer, ref.device_address, guest_descriptor_queue.AddBuffer(ref.buffer, ref.device_address,
static_cast<u32>(ref.offset), size); static_cast<u32>(ref.offset), size);
return ref.mapped_span; return ref.mapped_span;
@@ -287,7 +287,7 @@ Uint8Pass::~Uint8Pass() = default;
std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
u32 src_offset) { u32 src_offset) {
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16)); const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); const auto staging = staging_buffer_pool.Request(device, staging_size, MemoryUsage::DeviceLocal);
compute_pass_descriptor_queue.Acquire(scheduler, 2); compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
@@ -345,7 +345,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6; const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6;
const std::size_t staging_size = num_tri_vertices * sizeof(u32); const std::size_t staging_size = num_tri_vertices * sizeof(u32);
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); const auto staging = staging_buffer_pool.Request(device, staging_size, MemoryUsage::DeviceLocal);
compute_pass_descriptor_queue.Acquire(scheduler, 2); compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
@@ -852,7 +852,7 @@ public:
void PushUnsyncedQueries() override { void PushUnsyncedQueries() override {
CloseCounter(); CloseCounter();
auto staging_ref = staging_pool.Request( auto staging_ref = staging_pool.Request(device,
pending_flush_queries.size() * TFBQueryBank::QUERY_SIZE, MemoryUsage::Download, true); pending_flush_queries.size() * TFBQueryBank::QUERY_SIZE, MemoryUsage::Download, true);
size_t offset_base = staging_ref.offset; size_t offset_base = staging_ref.offset;
for (auto q : pending_flush_queries) { for (auto q : pending_flush_queries) {
@@ -1657,7 +1657,7 @@ void QueryCacheRuntime::SyncValues(std::span<SyncValuesType> values, VkBuffer ba
impl->copies_setup.clear(); impl->copies_setup.clear();
impl->copies_setup.resize(impl->little_cache.size()); impl->copies_setup.resize(impl->little_cache.size());
if constexpr (SyncValuesType::GeneratesBaseBuffer) { if constexpr (SyncValuesType::GeneratesBaseBuffer) {
ref = impl->staging_pool.Request(total_size, MemoryUsage::Upload); ref = impl->staging_pool.Request(impl->device, total_size, MemoryUsage::Upload);
size_t current_offset = ref.offset; size_t current_offset = ref.offset;
size_t accumulated_size = 0; size_t accumulated_size = 0;
for (size_t i = 0; i < values.size(); i++) { for (size_t i = 0; i < values.size(); i++) {
@@ -28,55 +28,42 @@ using namespace Common::Literals;
// Maximum potential alignment of a Vulkan buffer // Maximum potential alignment of a Vulkan buffer
constexpr VkDeviceSize MAX_ALIGNMENT = 256; constexpr VkDeviceSize MAX_ALIGNMENT = 256;
// Stream buffer size in bytes size_t GetStreamBufferSize(const Device& device, size_t max_stream_buffer_size, size_t max_alignment) {
// *NIX drivers are more sensitive to increased buffers for streaming.
// Windows ones however, can intake bigger buffers and generally do not OOM.
// - GTX 960 on Windows will not OOM with 256mib
// - GT 1030 on ^NIX will OOM with 256mib
#if defined(__FreeBSD__)
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
#else
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
#endif
size_t GetStreamBufferSize(const Device& device) {
if (!device.HasDebuggingToolAttached()) { if (!device.HasDebuggingToolAttached()) {
return MAX_STREAM_BUFFER_SIZE; return max_stream_buffer_size;
} }
VkDeviceSize size{0}; VkDeviceSize size{0};
bool has_device_local_host_visible_heap{}; bool has_device_local_host_visible_heap{};
ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap]( ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap](size_t index, VkMemoryHeap& heap) {
size_t index, VkMemoryHeap& heap) {
has_device_local_host_visible_heap = true; has_device_local_host_visible_heap = true;
size = (std::max)(size, heap.size); size = std::max<size_t>(size, heap.size);
}); });
if (has_device_local_host_visible_heap) { if (has_device_local_host_visible_heap) {
// If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be // If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be
// loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue // loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue
// as the heap will be much larger. // as the heap will be much larger.
if (size <= MAX_STREAM_BUFFER_SIZE) { if (size <= max_stream_buffer_size) {
size = size * 40 / 100; size = size * 40 / 100;
} }
} else { } else {
size = MAX_STREAM_BUFFER_SIZE; size = max_stream_buffer_size;
} }
return (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); return std::min<size_t>(Common::AlignUp(size, max_alignment), max_stream_buffer_size);
} }
} // Anonymous namespace } // Anonymous namespace
StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, StagingBufferPool::StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator_, Scheduler& scheduler_)
Scheduler& scheduler_) : memory_allocator{memory_allocator_}, scheduler{scheduler_}
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, , stream_buffer_size{GetStreamBufferSize(device, 256_MiB, MAX_ALIGNMENT)}
stream_buffer_size{GetStreamBufferSize(device)}, region_size{stream_buffer_size / {
StagingBufferPool::NUM_SYNCS} {
VkBufferCreateInfo stream_ci = { VkBufferCreateInfo stream_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.size = stream_buffer_size, .size = stream_buffer_size,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,
@@ -87,7 +74,16 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
if (device.IsBufferDeviceAddressSupported()) { if (device.IsBufferDeviceAddressSupported()) {
stream_ci.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; stream_ci.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
} }
// Some drivers are more sensitive to increased buffer sizes
try {
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream); stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
} catch (vk::Exception& e) {
LOG_ERROR(Render_Vulkan, "Can't fit {} bytes buffer, halving", stream_ci.size);
stream_buffer_size = GetStreamBufferSize(device, 128_MiB, MAX_ALIGNMENT);
stream_ci.size = stream_buffer_size;
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
}
region_size = stream_buffer_size / StagingBufferPool::NUM_SYNCS;
if (device.HasDebuggingToolAttached()) { if (device.HasDebuggingToolAttached()) {
stream_buffer.SetObjectNameEXT("Stream Buffer"); stream_buffer.SetObjectNameEXT("Stream Buffer");
} }
@@ -100,11 +96,10 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
StagingBufferPool::~StagingBufferPool() = default; StagingBufferPool::~StagingBufferPool() = default;
StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) { StagingBufferRef StagingBufferPool::Request(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
if (!deferred && usage == MemoryUsage::Upload && size <= region_size) { return (!deferred && usage == MemoryUsage::Upload && size <= region_size)
return GetStreamBuffer(size); ? GetStreamBuffer(device, size)
} : GetStagingBuffer(device, size, usage, deferred);
return GetStagingBuffer(size, usage, deferred);
} }
void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) { void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) {
@@ -127,11 +122,10 @@ void StagingBufferPool::TickFrame() {
ReleaseCache(MemoryUsage::Download); ReleaseCache(MemoryUsage::Download);
} }
StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t size) {
if (AreRegionsActive(Region(free_iterator) + 1, if (AreRegionsActive(Region(free_iterator) + 1, (std::min)(Region(iterator + size) + 1, NUM_SYNCS))) {
(std::min)(Region(iterator + size) + 1, NUM_SYNCS))) {
// Avoid waiting for the previous usages to be free // Avoid waiting for the previous usages to be free
return GetStagingBuffer(size, MemoryUsage::Upload); return GetStagingBuffer(device, size, MemoryUsage::Upload);
} }
const u64 current_tick = scheduler.CurrentTick(); const u64 current_tick = scheduler.CurrentTick();
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator), std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator),
@@ -140,15 +134,14 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
free_iterator = (std::max)(free_iterator, iterator + size); free_iterator = (std::max)(free_iterator, iterator + size);
if (iterator + size >= stream_buffer_size) { if (iterator + size >= stream_buffer_size) {
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, current_tick);
current_tick);
used_iterator = 0; used_iterator = 0;
iterator = 0; iterator = 0;
free_iterator = size; free_iterator = size;
if (AreRegionsActive(0, Region(size) + 1)) { if (AreRegionsActive(0, Region(size) + 1)) {
// Avoid waiting for the previous usages to be free // Avoid waiting for the previous usages to be free
return GetStagingBuffer(size, MemoryUsage::Upload); return GetStagingBuffer(device, size, MemoryUsage::Upload);
} }
} }
const size_t offset = iterator; const size_t offset = iterator;
@@ -156,7 +149,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
return StagingBufferRef{ return StagingBufferRef{
.buffer = *stream_buffer, .buffer = *stream_buffer,
.device_address = stream_buffer_address, .device_address = stream_buffer_address,
.offset = static_cast<VkDeviceSize>(offset), .offset = VkDeviceSize(offset),
.mapped_span = stream_pointer.subspan(offset, size), .mapped_span = stream_pointer.subspan(offset, size),
.usage{}, .usage{},
.log2_level{}, .log2_level{},
@@ -166,21 +159,18 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const { bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const {
const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick(); const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick();
return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end, return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end, [gpu_tick](u64 sync_tick) {
[gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; }); return gpu_tick < sync_tick;
});
}; };
StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage, StagingBufferRef StagingBufferPool::GetStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
bool deferred) { if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred))
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred)) {
return *ref; return *ref;
} return CreateStagingBuffer(device, size, usage, deferred);
return CreateStagingBuffer(size, usage, deferred);
} }
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, MemoryUsage usage, bool deferred) {
MemoryUsage usage,
bool deferred) {
StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil(size)]; StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil(size)];
const auto is_free = [this](const StagingBuffer& entry) { const auto is_free = [this](const StagingBuffer& entry) {
@@ -202,7 +192,7 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
return it->Ref(); return it->Ref();
} }
StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred) { StagingBufferRef StagingBufferPool::CreateStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
auto const log2_size = Common::Log2Ceil<u32>(u32(size)); auto const log2_size = Common::Log2Ceil<u32>(u32(size));
VkBufferCreateInfo buffer_ci = { VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@@ -33,11 +33,10 @@ class StagingBufferPool {
public: public:
static constexpr size_t NUM_SYNCS = 16; static constexpr size_t NUM_SYNCS = 16;
explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator, explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler);
Scheduler& scheduler);
~StagingBufferPool(); ~StagingBufferPool();
StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); StagingBufferRef Request(const Device& device, size_t size, MemoryUsage usage, bool deferred = false);
void FreeDeferred(StagingBufferRef& ref); void FreeDeferred(StagingBufferRef& ref);
[[nodiscard]] VkBuffer StreamBuf() const noexcept { [[nodiscard]] VkBuffer StreamBuf() const noexcept {
@@ -84,27 +83,18 @@ private:
static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT; static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT;
using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>; using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>;
StagingBufferRef GetStreamBuffer(size_t size); StagingBufferRef GetStreamBuffer(const Device& device, size_t size);
bool AreRegionsActive(size_t region_begin, size_t region_end) const; bool AreRegionsActive(size_t region_begin, size_t region_end) const;
StagingBufferRef GetStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred = false);
StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false); std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage, bool deferred);
StagingBufferRef CreateStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred);
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage,
bool deferred);
StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred);
StagingBuffersCache& GetCache(MemoryUsage usage); StagingBuffersCache& GetCache(MemoryUsage usage);
void ReleaseCache(MemoryUsage usage); void ReleaseCache(MemoryUsage usage);
void ReleaseLevel(StagingBuffersCache& cache, size_t log2); void ReleaseLevel(StagingBuffersCache& cache, size_t log2);
size_t Region(size_t iter) const noexcept { size_t Region(size_t iter) const noexcept {
return iter / region_size; return iter / region_size;
} }
const Device& device;
MemoryAllocator& memory_allocator; MemoryAllocator& memory_allocator;
Scheduler& scheduler; Scheduler& scheduler;
@@ -975,11 +975,11 @@ void TextureCacheRuntime::Finish() {
} }
StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) { StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) {
return staging_buffer_pool.Request(size, MemoryUsage::Upload, deferred); return staging_buffer_pool.Request(device, size, MemoryUsage::Upload, deferred);
} }
StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) { StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred); return staging_buffer_pool.Request(device, size, MemoryUsage::Download, deferred);
} }
void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) { void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {

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