Compare commits

..

20 Commits

Author SHA1 Message Date
CamilleLaVey 1ee7b981a6 Fix license headers 2026-07-07 20:28:53 +02:00
CamilleLaVey 2e1f4cb84b [spir-v] Added fallback for unsupported quads + shader-stage checks 2026-07-07 20:28:53 +02:00
CamilleLaVey baaf9a628c [spir-v] Correcting output components 2026-07-07 20:28:53 +02:00
CamilleLaVey bcaeece578 Another try 2026-07-07 20:28:53 +02:00
CamilleLaVey 7a3ab68439 [spir-v] Quick workaround for glasm/ glsl instructions 2026-07-07 20:28:53 +02:00
CamilleLaVey c784f06704 [spir-v] Early stage quad control implementation 2026-07-07 20:28:53 +02:00
lizzie e662108584 [fs] use recursive_directory_iterator since MSVC CRT no longer should bug out (#3670)
SURELY, microslop already fixed its megaslop?

basically read the comment (now gone); MSVC is evil, but it cant still be this evil right... right?!?!?! it's been 2 years surely not!

mainly because this helps deep scan (in theory); android should see slight improvments for big game libraries

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3670
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 20:26:10 +02:00
lizzie 29f712d3b5 [core/device_memory_manager] use more pointer/iterator stable std::vector<> instead of deque<> for multi address scatter/gather (#4098)
this SHOULD fix a specific issue where a deque can be reallocated causing spurious crashes

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4098
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-07 20:17:29 +02:00
lizzie ae75ba4883 [shader_recompiler/ir] fix potential CBuf collision when count mismatches (#4097)
this should make disjoint counts be handled as different cbufs for the cbuf cache

this fixes TIC asserts on PKZA

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4097
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 20:16:21 +02:00
lizzie 7b750bf5bb [hid_core, input_common] use std::numbers::pi{_v} instead of re-defining (#4160)
don't reinvent the wheel, simple as that.

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4160
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 20:13:33 +02:00
lizzie 94e9c2bd74 [common] remove presumed 'hot path' for QPC of 10MHz on SteadyClock::Now() (#4162)
while this may be true, it isn't required to have this quick path for this function because:
a) arm64
b) counter/freq and counter%freq is a single idiv
c) 2 muls, 2 div isnt that insanely expensive
d) `static_assert(period::den % TenMHz == 0)`

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4162
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 20:03:27 +02:00
lizzie 9677522e6c [core/hle/kernel/svc] add missing CanContain check for {Un}MapProcessCodeMemory, remove 4KB align func (#4164)
does what it says, adds a missing check for that specific SVC
also removes that 4kb "helper" function which is just a fancy "IsAligned"

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4164
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 20:02:26 +02:00
lizzie f423471cc0 [*, docs] remove UTF8 and make docs ASCII only (#4166)
except for the AI policy doc which uses output for demonstrative purposes

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4166
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 20:01:11 +02:00
lizzie ce043aceb9 [core/hle/service] remove duplicate Core::System& from some ServiceFramework (#4171)
services inheriting from ServiceFramework already have an accessible
system pointer from the inheritance
adding an extra pointer (m_system, often) isn't required and tis
entirely redundant.

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4171
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 19:59:58 +02:00
lizzie 1643d8766f [vk] invalidate shader cache automatically if !=driverId or !=driverVersion (#4174)
this integrates neatly into the existing GraphicsPipelineKey system
no need for extra funcs :)

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

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4174
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 19:57:45 +02:00
lizzie fe1b8ff0ab [input_common] Fix GetButtonMappingForDevice() when joystick doesn't have a gamepad associated with it (#4172)
from PS4 PR - this should help fix the case where a controller
doesn't have an associated gamepad and thus its not automatically
configured.

this should fix that :)

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4172
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 19:54:15 +02:00
lizzie f22fd1714b [dynarmic] use LinkBlockFast for branches which are guarnateed to be unconditional (or conditionally unconditional) (#4177)
CBZ is basically
```c
if (thing) {
  jump(A);
} else  {
 jump(B);
}
```
those jumps are locally unconditional so we can add the LinkBlockFast
without much repercussions for a small win in JIT opts

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4177
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 19:53:13 +02:00
lizzie 6119e8fad2 [hid_core] fix LM2 crash due to callbacks not being removed properly (#4178)
the function returned the key AFTER this one, which points to an invalid object
basically its like "oh im returning the index AFTER Myself"
off-by-1 error basically; now it returns OUR own key.

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4178
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 19:52:22 +02:00
lizzie bfc0e60333 [externals/stb] only include JPEG functions, disable all others (#4179)
we use JPEG only with STB, dont need the others
which are included in the final binary, after gc+lto

this doesnt affect stb_dxt, which still includes all required BCn stuff

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4179
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 19:51:51 +02:00
lizzie 072be286d1 [shader_recompiler] fix META_TABLE/NUM_ARGS table being duplicated per TU that included opcodes.h (#4180)
small oopsie; we dont need 6 copies of the same 22KB table
(removes 5*22 = 110 KB worth of redundant stuff)

this also enforces static on the types defined in the header so they arent memoized into a non-inlined value (aka. compiler just grabs value instead of well, yknow, assuming its a constant area)

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4180
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-07 19:50:56 +02:00
99 changed files with 705 additions and 760 deletions
+3 -2
View File
@@ -311,8 +311,9 @@ dispatcher, which will return control to the host.
SetTerm(IR::Term::LinkBlockFast{next})
```
This terminal instruction jumps to the basic block described by `next` unconditionally.
This promises guarantees that must be held at runtime - i.e that the program wont hang,
This terminal instruction jumps to the basic block described by `next` unconditionally. This promises guarantees that must be held at runtime - i.e that the program wont hang,
This is a valid expression if contained within another block (say a `Terminal::If`) and the semantics allow it to be so.
### Terminal: PopRSBHint
+1 -1
View File
@@ -14,7 +14,7 @@ Use this guide whenever you want to modify the Date or Time that Eden reports to
## Steps
1. Navigate to *Emulation Configure*.
1. Navigate to *Emulation > Configure*.
2. Click on the **System** item on the left-hand side navigation, then check the *Custom RTC Date* box.
3. The Date/Time option now becomes editable. Set it to the value you want and hit **OK**.
4. GREAT SCOTT! We have time traveled! You can of course go forward or backward in time (as long as it is not before the year 1970) and your game should update accordingly (e.g. certain *Super Mario Odyssey* moons that take time for flowers to grow will now be fully grown.).
+1 -1
View File
@@ -30,7 +30,7 @@ Use this guide for when you want to configure specific controller settings to be
#### Steps
1. Launch Eden and wait for it to load.
2. Navigate to *Emulation > Configure*
2. Navigate to *Emulation > Configure...*
3. Select **Controls** from the left-hand menu and configure your controller for the way you want it to be in game.
4. Select **New** and enter a name for the profile in the box that appears. Press **OK** to save the profile settings.
5. Select **OK** to close the settings menu.
+7 -7
View File
@@ -16,14 +16,14 @@ Use this guide when you want to use the Steam Deck's native gyro functionality f
### Steps
1. Go into Steam Deck's Desktop Mode, and use the shortcut to launch EmuDeck.
2. Install [SteamDeckGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU/releases) by going to *3rd Party Tools > Gyroscope* and clicking **Install.**
2. Install [SteamDeckGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU/releases) by going to *3rd Party Tools > Gyroscope* and clicking **Install.**
a. Alternatively you can install [SteamDeckGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU/releases) manually following the GitHub page instructions.
3. Upon completion of the installation. You will need to reboot your Steam Deck. Do so before continuing on.
4. Go back into the Steam Deck Desktop Mode and open the Dolphin File Explorer.
5. Navigate to the following directory to see you controller configuration: `/home/deck/.config/Eden`
6. *Right-Click* the **qt-config.ini** file and open it with ***Kate***
7. Look for the following line: `player_0_motionleft=[empty]`.
8. Change the line to now say: `player_0_motionleft="motion:0,pad:0,port:26760,guid:0000000000000000000000007f000001,engine:cemuhookudp"`
5. Navigate to the following directory to see you controller configuration: `/home/deck/.config/Eden`
6. *Right-Click* the **qt-config.ini** file and open it with ***Kate***
7. Look for the following line: `player_0_motionleft=[empty]`.
8. Change the line to now say: `player_0_motionleft="motion:0,pad:0,port:26760,guid:0000000000000000000000007f000001,engine:cemuhookudp"`
9. Save the file and open Eden.
10. Launch a compatible title, like *The Legend of Zelda: Breath of the Wild*.
11. Test the gyro capabilities, for the above mentioned title, it is accessed by holding down the **R Trigger** and moving the Steam Deck around.
10. Launch a compatible title, like *The Legend of Zelda: Breath of the Wild*.
11. Test the gyro capabilities, for the above mentioned title, it is accessed by holding down the **R Trigger** and moving the Steam Deck around.
+1 -1
View File
@@ -4,7 +4,7 @@ Use this guide when you want to install Updates or DLC for your games in Eden.
<aside>
***NOTE***: This applies to separate Update/DLC files, not merged NSP/XCIs which include the base game and Updates/DLC applied on top of them in a single file. These files work in Eden, but would not require the following steps.
***NOTE***: This applies to separate Update/DLC files, not "merged" NSP/XCI's which include the base game and Updates/DLC applied on top of them in a single file. These files work in Eden, but would not require the following steps.
</aside>
+9 -9
View File
@@ -10,7 +10,7 @@ This FAQ will serve as a general quick question and answer simple questions.
No - The only emulator that has this kind of functionality is *Ryujinx* and it's forks. This solution requires loading a custom module on a modded switch console to work.
### Can I Play Online Games?
No - This would require hijacking requests to Nintendo's official servers to a custom server infrastructure built to emulate that functionality. This is how services like [*Pretendo*](https://pretendo.network/) operate. As such, you would not be able to play Online, you can however play multiplayer games.
No - This would require hijacking requests to Nintendo's official servers to a custom server infrastructure built to emulate that functionality. This is how services like [*Pretendo*](https://pretendo.network/) operate. As such, you would not be able to play "Online", you can however play multiplayer games.
### What's the Difference Between Online and Multiplayer?
I have chosen the wording carefully here for a reason.
@@ -21,7 +21,7 @@ I have chosen the wording carefully here for a reason.
The rule of thumb here is simple: If a game supports the ability to communicate without a server (Local Wireless, LAN, etc.) you will be able to play with other users. If it requires a server to function - it will not. You will need to look up if your title support Local Wireless/LAN play as an option.
### How Does Multiplayer Work on Eden Exactly?
Eden's multiplayer works by emulating the Switch's local wireless (LDN) system, then tunneling that traffic over the internet through rooms that act like lobbies. Each player runs their own instance of the emulator, and as long as everyone joins the same room and the game supports local wireless multiplayer, the emulated consoles see each other as if they were on the same local network. This design avoids typical one-save netplay issues because every user keeps an independent save and console state while only the in-game wireless packets are forwarded through the room server. In practice, you pick or host a room, configure your network interface/port forwarding if needed, then launch any LDN-capable game; from the game's perspective it is just doing standard local wireless, while the emulator handles discovery and communication over the internet or LAN.
Eden's multiplayer works by emulating the Switch's local wireless (LDN) system, then tunneling that traffic over the internet through "rooms" that act like lobbies. Each player runs their own instance of the emulator, and as long as everyone joins the same room and the game supports local wireless multiplayer, the emulated consoles see each other as if they were on the same local network. This design avoids typical one-save netplay issues because every user keeps an independent save and console state while only the in-game wireless packets are forwarded through the room server. In practice, you pick or host a room, configure your network interface/port forwarding if needed, then launch any LDN-capable game; from the game's perspective it is just doing standard local wireless, while the emulator handles discovery and communication over the internet or LAN.
### What Do I Need to Do?
That depends entirely on what your goal is and your level of technical ability, you have a 2 options on how to proceed.
@@ -71,7 +71,7 @@ There are 2 primary methods that you can use to connect to an existing room, dep
</aside>
## Joining a Public Lobby
1. Open Eden and navigate to *Multiplayer Browse Public Game Lobby*.
1. Open Eden and navigate to *Multiplayer > Browse Public Game Lobby*.
2. The **Public Room Browser** will now open and display a list of publicly accessible rooms. Find one you want to connect to and double click it.
<aside>
@@ -84,7 +84,7 @@ There are 2 primary methods that you can use to connect to an existing room, dep
### Direct Connecting to a Room
If the hoster has not made the lobby public, or you don't want to find it in the public game browser - use this option to connect.
1. Open Eden and navigate to *Multiplayer Direct Connect*.
1. Open Eden and navigate to *Multiplayer > Direct Connect*.
2. Enter the *Server Address, Port*, *Nickname* (what your user will be called in the room), and a *Password* (if the hoster set one, otherwise leave it blank) and hit **Connect.**
3. You will now see a window showing everyone on the lobby, or an error message.
@@ -101,7 +101,7 @@ Use this guide for when you want to host a multiplayer lobby to play with others
- Ability to allow programs through the firewall on your device.
### Steps
1. Open Eden and navigate to *Emulation Multiplayer Create Room.*
1. Open Eden and navigate to *Emulation > Multiplayer > Create Room.*
2. Fill out the following information in the popup dialog box.
@@ -144,7 +144,7 @@ Quite often the person with whom you want to play is located off of your interna
Port forwarding is a networking technique that directs incoming traffic arriving at a specific port on a router or firewall to a designated device and port inside a private local network. When an external client contacts the public IP address of the router on that port, the router rewrites the packet's destination information (IP address and sometimes port number) and forwards it to the internal host that is listening on the corresponding service. This allows services such as web servers, game servers, or remote desktop sessions hosted behind NAT (Network Address Translation) to be reachable from the wider Internet despite the devices themselves having non-routable private addresses.
The process works by creating a static mappingoften called a port-forward rule”—in the router's configuration. The rule specifies three pieces of data: the external (public) port, the internal (private) IP address of the target machine, and the internal port on which that machine expects the traffic. When a packet arrives, the router checks its NAT table, matches the external port to a rule, and then translates the packet's destination to the internal address before sending it onward. Responses from the internal host are similarly rewritten so they appear to come from the router's public IP, completing the bidirectional communication loop. This mechanism enables seamless access to services inside a protected LAN without exposing the entire network.
The process works by creating a static mapping-often called a "port-forward rule"-in the router's configuration. The rule specifies three pieces of data: the external (public) port, the internal (private) IP address of the target machine, and the internal port on which that machine expects the traffic. When a packet arrives, the router checks its NAT table, matches the external port to a rule, and then translates the packet's destination to the internal address before sending it onward. Responses from the internal host are similarly rewritten so they appear to come from the router's public IP, completing the bidirectional communication loop. This mechanism enables seamless access to services inside a protected LAN without exposing the entire network.
For our purposes we would pick the port we want to expose (*e.g. 24872*) and we would access our router's configuration and create a port-forward rule to send the traffic from an external connection to your local machine over our specified port (*24872)*. The exact way to do so, varies greatly by router manufacturer - and sometimes require contacting your ISP to do so depending on your agreement. You can look up your router on [*portforward.com*](https://portforward.com/router.htm) which may have instructions on how to do so for your specific equipment. If it is not there, you will have to use Google/ChatGPT to determine the steps for your equipment.
@@ -160,7 +160,7 @@ Remember you can't have one port open for multiple devices at the same time - yo
</aside>
Using a Tunnelling service may be the solution to avoid port forward, but also avoid worrying about your users setup. A tunnelling service works by having a lightweight client run on the machine that hosts the game server. That client immediately opens an **outbound** encrypted connection (typically over TLS/QUIC) to a relay node operated by the tunnel provider's cloud infrastructure. Because outbound traffic is almost always allowed through NAT routers and ISP firewalls, the tunnel can be established even when the host sits behind carrier-grade NAT or a strict firewall. The tunnel provider then assigns a public address (e.g.,`mygame.playit.gg:12345`). When a remote player connects to that address, the traffic reaches the the tunnel provider relay, which forwards it through the already-established tunnel back to the client on the private network, and finally onto the local game server's port. In effect, the server appears to the Internet as if it were listening on the public address, while the host never needs to configure port-forwarding rules or expose its own IP directly.
Using a Tunnelling service may be the solution to avoid port forward, but also avoid worrying about your users setup. A tunnelling service works by having a lightweight client run on the machine that hosts the game server. That client immediately opens an **outbound** encrypted connection (typically over TLS/QUIC) to a relay node operated by the tunnel provider's cloud infrastructure. Because outbound traffic is almost always allowed through NAT routers and ISP firewalls, the tunnel can be established even when the host sits behind carrier-grade NAT or a strict firewall. The tunnel provider then assigns a public address (e.g., `mygame.playit.gg:12345`). When a remote player connects to that address, the traffic reaches the the tunnel provider relay, which forwards it through the already-established tunnel back to the client on the private network, and finally onto the local game server's port. In effect, the server appears to the Internet as if it were listening on the public address, while the host never needs to configure port-forwarding rules or expose its own IP directly.
For our purposes we would spawn the listener for the port that way chose when hosting our room. The user would connect to our assigned public address/port combination, and it would be routed to our machine. The tunnel must remain active for as long as you want the connection to remain open. Closing the terminal will kill the tunnel and disconnect the users.
@@ -204,7 +204,7 @@ Use this guide when you need to determine the connection information for the Pub
### Method 1: Grabbing the Address from the Log File
1. Open Eden and Connect to the room you want to identify.
1. See the *Joining a Multiplayer Room* section for instructions on how to do so if you need them.
2. Go to *File Open Eden Folder*, then open the **config** folder.
2. Go to *File > Open Eden Folder*, then open the **config** folder.
3. Open the the **qt-config.ini** file in a text editor.
4. Search for the following keys:
1. `Multiplayer\ip=`
@@ -286,7 +286,7 @@ This guide will assume you are the one hosting the game and go over things *Pars
2. If you are joining a game, you will have to send a connection request the host will have to accept.
3. Verify that the remote player can see the screen and that there is no issues with the connection.
4. Launch Eden.
5. Navigate to *Emulation Configure*.
5. Navigate to *Emulation > Configure*.
6. Select the **Controls** tab.
7. Set up your controller, if necessary.
8. Select the **Player 2** tab and select the **Connect Controller** checkbox. This enables inputs from another device to be seen as a second controller.
+5 -5
View File
@@ -33,12 +33,12 @@ Use this guide to get starting using the Eden emulator.
<aside>
***INFO***: You may get a *Windows protected your PC* SmartScreen message that appears. This is just Windows Defender saying it did not recognize the application and did not run it - Eden is completely safe. Click **More info** and then **Run anyway** to dismiss this message.
***INFO***: You may get a "*Windows protected your PC"* SmartScreen message that appears. This is just Windows Defender saying it did not recognize the application and did not run it - Eden is completely safe. Click **More info** and then **Run anyway** to dismiss this message.
</aside>
4. Eden will now launch and notify you about missing Decryption keys. Close the dialog box by hitting **OK**.
5. Navigate to **Tools Install Decryption Keys**, navigate to the folder containing your key files and select the file, you should only be able to select one.
6. Navigate to **Tools Install Firmware**, *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
5. Navigate to **Tools > Install Decryption Keys**, navigate to the folder containing your key files and select the file, you should only be able to select one.
6. Navigate to **Tools > Install Firmware**, *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
7. Double-Click the main window to add the folder containing your games.
8. Go to *Emulation > Configure > Input* and set up your controller of choice. Click **OK** to close the dialog window.
9. Double-Click a game to run it.
@@ -72,8 +72,8 @@ Use this guide to get starting using the Eden emulator.
4. If you have had a different Switch emulator installed, it will detect and ask if you want to import those settings. Make your selection to close the screen.
5. Eden will now launch and notify you about missing Encryption keys. Close the dialog box by hitting **OK**.
6. Navigate to **Tools Install Decryption Keys**, navigate to the folder containing your ***prod.keys*** file and select the file and hit **Open**.
7. Navigate to **Tools Install Firmware ** *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
6. Navigate to **Tools > Install Decryption Keys**, navigate to the folder containing your ***prod.keys*** file and select the file and hit **Open**.
7. Navigate to **Tools > Install Firmware >** *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
8. Double-Click the main window to add the folder containing your games.
9. Go to *Emulation > Configure > Input* and set up your controller. Click **OK** to close the dialog window.
10. Double-Click a game to run it.
+1 -1
View File
@@ -22,7 +22,7 @@ Use this guide when you need to allow Eden to run on a Mac system, but are being
### Why am I Seeing This?
Recent versions of MacOS (Catalina & newer) introduced the **Gatekeeper** security functionality, requiring software to be signed by Apple or a trusted (aka - paying) developer. If the signature isnt on the list of trusted ones, it will stop the program from executing and display the message above.
Recent versions of MacOS (Catalina & newer) introduced the **Gatekeeper** security functionality, requiring software to be signed by Apple or a trusted (aka - paying) developer. If the signature isn't on the list of trusted ones, it will stop the program from executing and display the message above.
---
+9 -9
View File
@@ -20,7 +20,7 @@ Use this when you want to import the Eden AppImage into your Steam Library along
#### Initial Setup
1. Press the **STEAM** button and then go to *Power Switch to Desktop* to enter the Desktop mode.
1. Press the **STEAM** button and then go to *Power > Switch to Desktop* to enter the Desktop mode.
2. Install ***Steam ROM Manager*** (if needed), there are 2 ways you can accomplish this, either manually or through [*EmuDeck*](https://www.emudeck.com/#downloads).
@@ -53,8 +53,8 @@ Use this when you want to import the Eden AppImage into your Steam Library along
EmuDeck will automatically create an *Emulators - Emulators* parser for ***Steam ROM Manager*** that uses shell scripts to launch them. We will follow this convention.
1. In the file explorer go to your **EmuDeck installation folder tools launchers**
2. Right-Click some empty space and hit **Create New Text File,** call this new file ***eden.sh*** instead of ***Text File.txt***
1. In the file explorer go to your **EmuDeck installation folder > tools > launchers**
2. Right-Click some empty space and hit **Create New > Text File,** call this new file ***eden.sh*** instead of ***Text File.txt***
3. Right-Click the ***eden.sh*** file you created and hit ***Open with Kate***.
4. Paste the following code into the contents of the file, save and close the file.
@@ -103,7 +103,7 @@ We will need to create a new parser for the Emulators. Unlike with the EmuDeck
<aside>
***TIP***: You may need to go to **Settings Theme** and set it to *Classic* to view this option.
***TIP***: You may need to go to **Settings > Theme** and set it to *Classic* to view this option.
</aside>
@@ -119,7 +119,7 @@ We will need to create a new parser for the Emulators. Unlike with the EmuDeck
2. Parser Specific Configuration
1. **Search Glob**: *${title}@(.AppImage|.APPIMAGE|.appimage)*
3. Executable Configuration
1. **Executable Modifier**: *"${exePath}*
1. **Executable Modifier**: *"${exePath}"*
4. Title Modification Configuration
1. **Title Modifier**: *${fuzzyTitle}*
@@ -181,7 +181,7 @@ Use this when you want to import your games inside Eden into Steam to launch wit
### Steps
1. Press the **STEAM** button and then go to *Power Switch to Desktop* to enter the Desktop mode.
1. Press the **STEAM** button and then go to *Power > Switch to Desktop* to enter the Desktop mode.
1. Install ***Steam ROM Manager***, there are 2 ways you can accomplish this, either manually or through [*EmuDeck*](https://www.emudeck.com/#downloads).
@@ -215,7 +215,7 @@ Use this when you want to import your games inside Eden into Steam to launch wit
<aside>
***TIP***: Your layout may look different depending on how you installed *Steam ROM Manager*. You may need to go to **Settings Theme** and change it to *Classic* to follow along.
***TIP***: Your layout may look different depending on how you installed *Steam ROM Manager*. You may need to go to **Settings > Theme** and change it to *Classic* to follow along.
</aside>
@@ -229,13 +229,13 @@ Use this when you want to import your games inside Eden into Steam to launch wit
2. Change the **Parser title** from *Nintendo Switch - Yuzu* to *Nintendo Switch - Eden.*
3. Hit the **Browse** option under the *ROMs directory* section. Select the directory containing your Switch ROMs.
4. Under *Steam collections*, you can add a Steam category name. This just organizes the games under a common category in your Steam Library, this is optional but recommended.
5. Scroll down slightly to the **Executable Configuration Executable**, select **Browse** and select the Eden AppImage.
5. Scroll down slightly to the **Executable Configuration > Executable**, select **Browse** and select the Eden AppImage.
6. Leave everything else the same and hit **Save** to save the parser.
---
4. Click the Eden parser to view the options on the right, select **Test** at the bottom of the screen to ensure that *Steam ROM Manager* detects your games correctly.
1. *Steam ROM Manager* will start to scan the specified ROMs directory and match them to games. Look over the results to ensure they are accurate. If you do not see any entries - check your parsers ROMs directory field.
1. When you are happy with the results, click the **Add Games** **Parse** to start the actual Parsing.
1. When you are happy with the results, click the **Add Games** > **Parse** to start the actual Parsing.
1. The program will now identify the games and pull artwork from [*SteamGridDB*](https://www.steamgriddb.com/).
2. Review the game matches and ensure everything is there.
+10 -10
View File
@@ -30,15 +30,15 @@ Use this guide for when you want to configure automated backup/syncing of your E
## Overview
Rather than giving a breakdown of all the platforms and configurations, those will be in the platforms specific guides - this will serve as a general overview of Syncthing.
Rather than giving a breakdown of all the platforms and configurations, those will be in the platform's specific guides - this will serve as a general overview of Syncthing.
### What is Syncthing Anyway?
Syncthing is a continuous file synchronization program (in the laymans - make sure 2 or more systems with the same files are always up to date). This is perfect for game saves where we would want to play on 1 device, save our game, and then continue playing it on another device. This technology is what Epic/Steam/etc. use to allow you to do this on games run through their respective services. Syncthing is an open source implementation of this technology that you control, rather than relying on a 3rd party. This has a few key benefits, most notably - better security, privacy, and speed (when on your LAN).
Syncthing is a continuous file synchronization program (in the layman's - make sure 2 or more systems with the same files are always up to date). This is perfect for game saves where we would want to play on 1 device, save our game, and then continue playing it on another device. This technology is what Epic/Steam/etc. use to allow you to do this on games run through their respective services. Syncthing is an open source implementation of this technology that you control, rather than relying on a 3rd party. This has a few key benefits, most notably - better security, privacy, and speed (when on your LAN).
### What are some common issues?
Syncthing is fairly robust and doesnt have many issues luckily, but there are some things you should watch out for (almost all of them a user issue).
Syncthing is fairly robust and doesn't have many issues luckily, but there are some things you should watch out for (almost all of them a user issue).
- Sync conflicts
- If for whatever reason you update the same file on 2 different machines, the system does not know which updated file is considered the one to sync across. This results in a ***sync conflict*** where it may not sync the files as you would expect. Worst case scenario, this can result in your save progress being lost if you are not careful. When one of these occurs, it will create a copy of the file and store it with a specific name, like this example, *Paper Mario.sync-conflict-20251102-072925-TZBBN6S.srm.* To resolve this, you must remove the other files and remove the *.sync-conflict-<TIMESTAMP>-<Syncthing Device ID>* from the file name of the file you want to keep.
@@ -86,7 +86,7 @@ Use this when you want to set this machine as the initial source of truth (push
1. Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing.**
2. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
3. Well start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
3. We'll start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
4. A pop-up window will appear, fill in the Folder label field with whatever you want to call it, like Switch Saves.
5. Enter the Full folder path to where your save files are stored on this machine.
@@ -105,7 +105,7 @@ Use this when you want to set this machine as the initial source of truth (push
Use this when you want to set this machine up as a child (pull files from the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Install Syncthing Tray on the client device following the section above. Copy the childs ID and store it so it is accessible to the Parent.
1. Install Syncthing Tray on the client device following the section above. Copy the child's ID and store it so it is accessible to the Parent.
2. ***ON THE PARENT***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
3. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
4. Navigate down to **+ Add Remote Device**, we are going to add our Child device, so I hope you have its ID handy. If not, go back and get it.
@@ -126,7 +126,7 @@ Use this when you want to set this machine up as a child (pull files from the ot
</aside>
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered *Parents* for other devices you want to add. Repeat these steps for as many devices you want.
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered "*Parents*" for other devices you want to add. Repeat these steps for as many devices you want.
## Linux
@@ -166,9 +166,9 @@ Use this when you want to set this machine up as a child (pull files from the ot
Use this when you want to set this machine as the initial source of truth (push files out to all the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing.**
1. If you dont have a taskbar in your distro, you can also reach it directly by opening a web browser to: *http://127.0.0.1:8384/.*
1. If you don't have a taskbar in your distro, you can also reach it directly by opening a web browser to: *http://127.0.0.1:8384/.*
2. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
3. Well start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
3. We'll start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
4. A pop-up window will appear, fill in the Folder label field with whatever you want to call it, like Switch Saves.
5. Enter the Full folder path to where your save files are stored on this machine.
@@ -187,7 +187,7 @@ Use this when you want to set this machine as the initial source of truth (push
Use this when you want to set this machine up as a child (pull files from the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Install Syncthing Tray on the client device following the section above. Copy the childs ID and store it so it is accessible to the Parent.
1. Install Syncthing Tray on the client device following the section above. Copy the child's ID and store it so it is accessible to the Parent.
2. ***ON THE PARENT***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
3. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
4. Navigate down to **+ Add Remote Device**, we are going to add our Child device, so I hope you have its ID handy. If not, go back and get it.
@@ -208,4 +208,4 @@ Use this when you want to set this machine up as a child (pull files from the ot
</aside>
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered *Parents* for other devices you want to add. Repeat these steps for as many devices you want.
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered "*Parents*" for other devices you want to add. Repeat these steps for as many devices you want.
+1 -1
View File
@@ -68,7 +68,7 @@ Faulting package-relative application ID:
2. Confirm there are no logs created in the log directory.
1. See the [How to Access Logs](./HowToAccessLogs.md) page for the log location if you need it.
2. If there are any entries in here since you tried step 1, this is likely not your issue.
3. Navigate to your *Windows Event Viewer* (Start Menu **eventvwr.msc)**.
3. Navigate to your *Windows Event Viewer* (Start Menu > **eventvwr.msc)**.
4. Expand **Windows Logs** and select **Application.**
5. Look for an entry with the Level of Error, and look for a message similar to the following
+3 -3
View File
@@ -30,14 +30,14 @@ TBD
</aside>
1. Navigate to the Amiibo section of the game. The method for initiating the scanning varies from game to game, for *Captain Toads Treasure Tracker*, you need to go to the press the **+** button when on the level select. You will need to look up how to do so with your specific game.
1. Navigate to the Amiibo section of the game. The method for initiating the scanning varies from game to game, for *Captain Toad's Treasure Tracker*, you need to go to the press the **+** button when on the level select. You will need to look up how to do so with your specific game.
2. Upon activating the Amiibo scan functionality, you should get a Scan page. Eden is now looking for an Amiibo file to be loaded, which emulates scanning an Amiibo on actual hardware.
3. Navigate to **File > Load/Remove Amiibo**, or press the hotkey to do the same (**F2** on keyboard by default).
3. Navigate to **File > Load/Remove Amiibo...**, or press the hotkey to do the same (**F2** on keyboard by default).
4. In the file explorer that opens, navigate to the amiibo file you want to use.
<aside>
***NOTE***: It seems the scanning functionality is spotty and will sometimes throw a "*The current game is not looking for amiibos* message, even though it is. Usually you just need to try loading it again or restarting the scanning from the game. In some situations it was only resolved by restarting the game.
***NOTE***: It seems the scanning functionality is spotty and will sometimes throw a "*The current game is not looking for amiibos*" message, even though it is. Usually you just need to try loading it again or restarting the scanning from the game. In some situations it was only resolved by restarting the game.
</aside>
+10 -10
View File
@@ -58,24 +58,24 @@ Community Member [Ninjistix](https://github.com/Ninjistix) created a utility (Wi
```
[Super Mario Bros. Wonder - Various] <- Optional
[ 1. Always Star Power]
[# 1. Always Star Power]
040E0000 00880580 52800035
[ 2. Star Power + Bubble Mode (Invincible)]
[# 2. Star Power + Bubble Mode (Invincible)]
040E0000 00880580 52800075
[ 3. Can Fast Travel to Any Course and World]
[# 3. Can Fast Travel to Any Course and World]
040E0000 00935E10 52800036
040E0000 0048A528 52800028
040E0000 005D9F58 52800028
[ 4. Got All Top of Flag Poles]
[# 4. Got All Top of Flag Poles]
040E0000 0048A818 52800028
```
### Step 3: Enabling/Disabling Cheats
Cheats are enabled by default, but can be disabled so they dont affect gameplay fairly easily using the game properties.
Cheats are enabled by default, but can be disabled so they don't affect gameplay fairly easily using the game properties.
1. Open Eden and press and hold the game you want to apply the cheat to.
2. Scroll down on the properties until you see **Add-ons**, select this option.
@@ -130,24 +130,24 @@ Community Member [Ninjistix](https://github.com/Ninjistix) created a utility (Wi
```
[Super Mario Bros. Wonder - Various] <- Optional
[ 1. Always Star Power]
[# 1. Always Star Power]
040E0000 00880580 52800035
[ 2. Star Power + Bubble Mode (Invincible)]
[# 2. Star Power + Bubble Mode (Invincible)]
040E0000 00880580 52800075
[ 3. Can Fast Travel to Any Course and World]
[# 3. Can Fast Travel to Any Course and World]
040E0000 00935E10 52800036
040E0000 0048A528 52800028
040E0000 005D9F58 52800028
[ 4. Got All Top of Flag Poles]
[# 4. Got All Top of Flag Poles]
040E0000 0048A818 52800028
```
### Step 3: Enabling/Disabling Cheats
Cheats are enabled by default, but can be disabled so they dont affect gameplay fairly easily using the game properties.
Cheats are enabled by default, but can be disabled so they don't affect gameplay fairly easily using the game properties.
1. *Right-Click* the game and select *Configure Game*.
2. In the **Add-Ons** section, locate the cheat you wish to enable.
@@ -269,7 +269,7 @@ class SettingsFragmentPresenter(
// TODO(crueter): sub-submenus?
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(IntSetting.RENDERER_NVDEC_EMULATION.key)
// add(IntSetting.RENDERER_NVDEC_EMULATION.key)
add(IntSetting.RENDERER_RESOLUTION.key)
add(IntSetting.RENDERER_VSYNC.key)
+1 -1
View File
@@ -27,7 +27,7 @@ if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools)
endif()
target_link_libraries(yuzu-android PRIVATE FFmpeg::FFmpeg OpenSSL::SSL cpp-jwt::cpp-jwt)
target_link_libraries(yuzu-android PRIVATE OpenSSL::SSL cpp-jwt::cpp-jwt)
if (ENABLE_UPDATE_CHECKER)
target_compile_definitions(yuzu-android PUBLIC ENABLE_UPDATE_CHECKER)
endif()
-15
View File
@@ -36,12 +36,6 @@
#include <frontend_common/content_manager.h>
#include <jni.h>
extern "C" {
// Required for FFmpeg mediacodec
#include <libavcodec/jni.h>
#include <libavutil/log.h>
}
#include "common/android/multiplayer/multiplayer.h"
#include "common/android/android_common.h"
#include "common/android/id_cache.h"
@@ -686,15 +680,6 @@ const char* fallback_cpu_detection() {
} // namespace
// referenced by common
extern "C" {
jint InitFFmpegOnLoad(JavaVM *vm) {
av_jni_set_java_vm(vm, NULL);
av_log_set_level(AV_LOG_DEBUG);
return 0;
}
}
extern "C" {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -181,8 +184,9 @@ MemoryPoolInfo::ResultState PoolMapper::Update(MemoryPoolInfo& pool,
return MemoryPoolInfo::ResultState::Success;
}
if (in_params.address == 0 || in_params.size == 0 || !Common::Is4KBAligned(in_params.address) ||
!Common::Is4KBAligned(in_params.size)) {
if (in_params.address == 0 || in_params.size == 0
|| !Common::IsAligned(in_params.address, Core::Memory::YUZU_PAGESIZE)
|| !Common::IsAligned(in_params.size, Core::Memory::YUZU_PAGESIZE)) {
return MemoryPoolInfo::ResultState::BadParam;
}
+1 -1
View File
@@ -155,7 +155,7 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
std::memcpy(&last_frame[0], &input_buffer[(frames_written - 1) * frame_size], frame_size_bytes);
}
// update sample counts für audio-ins
// update sample counts fuer audio-ins
{
std::scoped_lock lk{sample_count_lock};
last_sample_count_update_time = system.CoreTiming().GetGlobalTimeNs();
+5 -8
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Jannik Vogel <email@jannikvogel.de>
// SPDX-License-Identifier: CC0-1.0
@@ -34,12 +37,6 @@ template <typename T>
return static_cast<T>(value - value % size);
}
template <typename T>
requires std::is_unsigned_v<T>
[[nodiscard]] constexpr bool Is4KBAligned(T value) {
return (value & 0xFFF) == 0;
}
template <typename T>
requires std::is_unsigned_v<T>
[[nodiscard]] constexpr bool IsWordAligned(T value) {
@@ -48,9 +45,9 @@ template <typename T>
template <typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) noexcept {
using U = typename std::make_unsigned_t<T>;
const U mask = static_cast<U>(alignment - 1);
const U mask = U(alignment - 1);
return (value & mask) == 0;
}
+1 -4
View File
@@ -12,6 +12,7 @@
#include "common/android/multiplayer/multiplayer.h"
#include <network/network.h>
static JavaVM *s_java_vm;
static jclass s_native_library_class;
static jclass s_disk_cache_progress_class;
@@ -426,12 +427,8 @@ namespace Common::Android {
extern "C" {
#endif
// see on src/android/app/src/main/jni/native.cpp
jint InitFFmpegOnLoad(JavaVM *vm);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
s_java_vm = vm;
InitFFmpegOnLoad(vm);
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK)
+2 -8
View File
@@ -455,23 +455,17 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path, const DirEn
return;
}
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
std::error_code ec;
bool callback_error = false;
for (const auto& entry : fs::directory_iterator(path, ec)) {
// MSVC should now be fixed... right... right?!?!?!
for (const auto& entry : fs::recursive_directory_iterator(path, ec)) {
if ((True(filter & DirEntryFilter::File) && entry.status().type() == fs::file_type::regular)
|| (True(filter & DirEntryFilter::Directory) && entry.status().type() == fs::file_type::directory)) {
if (!callback(entry)) {
callback_error = true;
}
}
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
if (entry.status().type() == fs::file_type::directory) {
IterateDirEntriesRecursively(entry.path(), callback, filter);
}
}
if (callback_error || ec) {
LOG_ERROR(Common_Filesystem, "Failed to visit all the directory entries of path={}, ec_message={}, callback_error={}", PathToUTF8String(path), ec.message(), callback_error);
} else {
+1 -3
View File
@@ -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-FileCopyrightText: 2013 Dolphin Emulator Project
@@ -13,8 +13,6 @@
namespace Common {
constexpr float PI = 3.1415926535f;
template <class T>
struct Rectangle {
T left{};
+2 -2
View File
@@ -791,8 +791,8 @@ struct Values {
"program_args",
Category::System,
Specialization::Default,
true, // save_ persist in config file
false}; // runtime_modifiable_ startup-only
true, // save_ - persist in config file
false}; // runtime_modifiable_ - startup-only
Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
Setting<bool> dump_guest_shaders{
+7 -3
View File
@@ -1,8 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION 1
#define STB_IMAGE_RESIZE_IMPLEMENTATION 1
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
#define STBI_ONLY_JPEG 1
#include "common/stb.h"
+4
View File
@@ -1,8 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define STBI_ONLY_JPEG 1
#include <stb_image.h>
#include <stb_image_resize.h>
#include <stb_image_write.h>
+4 -16
View File
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -48,22 +48,10 @@ static s64 GetSystemTimeNS() {
SteadyClock::time_point SteadyClock::Now() noexcept {
#if defined(_WIN32)
static const auto freq = WindowsQueryPerformanceFrequency();
static const auto timer_freq = WindowsQueryPerformanceFrequency();
const auto counter = WindowsQueryPerformanceCounter();
// 10 MHz is a very common QPC frequency on modern PCs.
// Optimizing for this specific frequency can double the performance of
// this function by avoiding the expensive frequency conversion path.
static constexpr s64 TenMHz = 10'000'000;
if (freq == TenMHz) [[likely]] {
static_assert(period::den % TenMHz == 0);
static constexpr s64 Multiplier = period::den / TenMHz;
return time_point{duration{counter * Multiplier}};
}
const auto whole = (counter / freq) * period::den;
const auto part = (counter % freq) * period::den / freq;
const auto whole = (counter / timer_freq) * period::den;
const auto part = (counter % timer_freq) * period::den / timer_freq;
return time_point{duration{whole + part}};
#elif defined(__APPLE__)
return time_point{duration{clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)}};
+1 -1
View File
@@ -108,7 +108,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, hid_core{system.Kernel()}, cpu_manager{system},
: kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
u64 program_id;
+13 -9
View File
@@ -11,6 +11,8 @@
#include <algorithm>
#include <vector>
#include <boost/container/deque.hpp>
#include "common/address_space.h"
#include "common/address_space.inc"
#include "common/alignment.h"
@@ -33,21 +35,23 @@ public:
void GatherValues(u32 start_entry, Common::ScratchBuffer<u32>& buffer) {
buffer.resize(8);
buffer.resize(0);
size_t index = 0;
const auto add_value = [&](u32 value) {
buffer.resize(index + 1);
const auto add_value = [&buffer](u32 value, size_t index) {
if (buffer.size() < index + 1)
buffer.resize(index + 8);
buffer[index++] = value;
return index;
};
size_t index = 0;
u32 iter_entry = start_entry;
Entry* current = &storage[iter_entry - 1];
add_value(current->value);
index = add_value(current->value, index);
while (current->next_entry != 0) {
iter_entry = current->next_entry;
current = &storage[iter_entry - 1];
add_value(current->value);
index = add_value(current->value, index);
}
buffer.resize(index);
}
u32 Register(u32 value) {
@@ -124,8 +128,8 @@ private:
u32 value{};
};
std::deque<Entry> storage;
std::deque<u32> free_entries;
std::vector<Entry> storage;
boost::container::deque<u32> free_entries;
};
struct EmptyAllocator {
@@ -619,7 +623,7 @@ template <typename Traits>
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
Common::ScopedRangeLock lk(counter_guard, addr, size);
UpdatePagesCachedCountNoLock(addr, size, delta);
}
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta) {
+1 -1
View File
@@ -106,7 +106,7 @@ void PhysicalCore::RunThread(KernelCore& kernel, Kernel::KThread* thread) {
}
// Determine why we stopped.
// If a step completed successfully, skip other halt reason handlers
// If a step completed successfully, skip other halt reason handlers
// the step takes priority (e.g. step may also set InstructionBreakpoint
// if the next instruction happens to be a breakpoint).
const bool step_completed = True(hr & Core::HaltReason::StepThread)
+5 -6
View File
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -32,14 +32,13 @@ constexpr bool IsValidAddressRange(u64 address, u64 size) {
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr,
u64 size) {
if (!Common::Is4KBAligned(dst_addr)) {
Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr, u64 size) {
if (!Common::IsAligned(dst_addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
R_THROW(ResultInvalidAddress);
}
if (!Common::Is4KBAligned(src_addr)) {
if (!Common::IsAligned(src_addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
R_THROW(ResultInvalidSize);
}
@@ -49,7 +48,7 @@ Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr
R_THROW(ResultInvalidSize);
}
if (!Common::Is4KBAligned(size)) {
if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
R_THROW(ResultInvalidSize);
}
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -33,12 +33,12 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
R_THROW(ResultInvalidAddress);
}
if (!Common::Is4KBAligned(size)) {
if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#X}", size);
R_THROW(ResultInvalidSize);
}
@@ -82,12 +82,12 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
R_THROW(ResultInvalidAddress);
}
if (!Common::Is4KBAligned(size)) {
if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#X}", size);
R_THROW(ResultInvalidSize);
}
@@ -140,19 +140,19 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
"src_address=0x{:016X}, size=0x{:016X}",
process_handle, dst_address, src_address, size);
if (!Common::Is4KBAligned(src_address)) {
if (!Common::IsAligned(src_address, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
src_address);
R_THROW(ResultInvalidAddress);
}
if (!Common::Is4KBAligned(dst_address)) {
if (!Common::IsAligned(dst_address, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
dst_address);
R_THROW(ResultInvalidAddress);
}
if (size == 0 || !Common::Is4KBAligned(size)) {
if (size == 0 || !Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
R_THROW(ResultInvalidSize);
}
@@ -190,6 +190,7 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
R_THROW(ResultInvalidCurrentMemory);
}
R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidCurrentMemory);
R_RETURN(page_table.MapCodeMemory(dst_address, src_address, size));
}
@@ -200,19 +201,19 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
"size=0x{:016X}",
process_handle, dst_address, src_address, size);
if (!Common::Is4KBAligned(dst_address)) {
if (!Common::IsAligned(dst_address, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
dst_address);
R_THROW(ResultInvalidAddress);
}
if (!Common::Is4KBAligned(src_address)) {
if (!Common::IsAligned(src_address, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
src_address);
R_THROW(ResultInvalidAddress);
}
if (size == 0 || !Common::Is4KBAligned(size)) {
if (size == 0 || !Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
R_THROW(ResultInvalidSize);
}
@@ -250,6 +251,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
R_THROW(ResultInvalidCurrentMemory);
}
R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidCurrentMemory);
R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size));
}
+9 -8
View File
@@ -23,11 +23,13 @@ TimeZoneService::TimeZoneService(
Core::System& system_, FileTimestampWorker& file_timestamp_worker,
bool can_write_timezone_device_location, TimeZoneBinary& time_zone_binary,
std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service)
: ServiceFramework{system_, "ITimeZoneService"}, m_system{system},
m_can_write_timezone_device_location{can_write_timezone_device_location},
m_file_timestamp_worker{file_timestamp_worker}, m_wrapped_service{std::move(
time_zone_service)},
m_operation_event{m_system}, m_time_zone_binary{time_zone_binary} {
: ServiceFramework{system_, "ITimeZoneService"}
, m_can_write_timezone_device_location{can_write_timezone_device_location}
, m_file_timestamp_worker{file_timestamp_worker}
, m_wrapped_service{std::move(time_zone_service)}
, m_operation_event{system}
, m_time_zone_binary{time_zone_binary}
{
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&TimeZoneService::GetDeviceLocationName>, "GetDeviceLocationName"},
@@ -48,8 +50,7 @@ TimeZoneService::TimeZoneService(
// clang-format on
RegisterHandlers(functions);
m_set_sys =
m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
}
TimeZoneService::~TimeZoneService() = default;
@@ -89,7 +90,7 @@ Result TimeZoneService::SetDeviceLocationName(
std::scoped_lock m{m_list_mutex};
for (auto& operation_event : m_list_nodes) {
operation_event.m_event->Signal(m_system.Kernel());
operation_event.m_event->Signal(system.Kernel());
}
R_SUCCEED();
}
+3 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -77,9 +80,7 @@ public:
const Service::PSC::Time::CalendarTime& calendar_time);
private:
Core::System& m_system;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
bool m_can_write_timezone_device_location;
FileTimestampWorker& m_file_timestamp_worker;
std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service;
@@ -7,10 +7,12 @@
#include <algorithm>
#include <vector>
#include <optional>
#include <string>
#define STBI_ONLY_JPEG 1
#include <stb_image.h>
#include <stb_image_resize.h>
#include <stb_image_write.h>
#include <string>
#include "common/settings.h"
#include "core/file_sys/control_metadata.h"
+10 -6
View File
@@ -32,11 +32,13 @@ Alarm::~Alarm() {
m_ctx.CloseEvent(m_event);
}
Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
PowerStateRequestManager& power_state_request_manager)
: m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock},
m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent(
"Psc:Alarms:Event")} {}
Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, PowerStateRequestManager& power_state_request_manager)
: m_system{system}
, m_ctx{system, "Psc:Alarms"}
, m_steady_clock{steady_clock}
, m_power_state_request_manager{power_state_request_manager}
, m_event{m_ctx.CreateEvent("Psc:Alarms:Event")}
{}
Alarms::~Alarms() {
m_ctx.CloseEvent(m_event);
@@ -132,7 +134,9 @@ Result Alarms::UpdateClosestAndSignal(Kernel::KernelCore& kernel) {
}
IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr<TimeManager> manager)
: ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} {
: ServiceFramework{system_, "time:al"}
, m_alarms{manager->m_alarms}
{
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"},
+1 -6
View File
@@ -74,8 +74,7 @@ struct Alarm : public Common::IntrusiveListBaseNode<Alarm> {
class Alarms {
public:
explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
PowerStateRequestManager& power_state_request_manager);
explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, PowerStateRequestManager& power_state_request_manager);
~Alarms();
Kernel::KEvent& GetEvent() {
@@ -110,21 +109,17 @@ private:
class IAlarmService final : public ServiceFramework<IAlarmService> {
public:
explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager);
~IAlarmService() override = default;
private:
void CreateWakeupAlarm(HLERequestContext& ctx);
void CreateBackgroundTaskAlarm(HLERequestContext& ctx);
Core::System& m_system;
Alarms& m_alarms;
};
class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> {
public:
explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type);
~ISteadyClockAlarm() override = default;
private:
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,8 +11,9 @@ namespace Service::PSC::Time {
IPowerStateRequestHandler::IPowerStateRequestHandler(
Core::System& system_, PowerStateRequestManager& power_state_request_manager)
: ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{
power_state_request_manager} {
: ServiceFramework{system_, "time:p"}
, m_power_state_request_manager{power_state_request_manager}
{
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle>, "GetPowerStateRequestEventReadableHandle"},
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -17,16 +20,13 @@ namespace Service::PSC::Time {
class IPowerStateRequestHandler final : public ServiceFramework<IPowerStateRequestHandler> {
public:
explicit IPowerStateRequestHandler(Core::System& system,
PowerStateRequestManager& power_state_request_manager);
explicit IPowerStateRequestHandler(Core::System& system, PowerStateRequestManager& power_state_request_manager);
~IPowerStateRequestHandler() override = default;
Result GetPowerStateRequestEventReadableHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetAndClearPowerStateRequest(Out<bool> out_cleared, Out<u32> out_priority);
private:
Core::System& m_system;
PowerStateRequestManager& m_power_state_request_manager;
};
@@ -15,7 +15,7 @@ namespace Service::PSC::Time {
ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManager> time,
ServerManager* server_manager)
: ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)},
: ServiceFramework{system_, "time:m"}, m_time{std::move(time)},
m_server_manager{*server_manager},
m_local_system_clock{m_time->m_standard_local_system_clock},
m_user_system_clock{m_time->m_standard_user_system_clock},
@@ -26,7 +26,7 @@ ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManage
m_local_system_context_writer{m_time->m_local_system_clock_context_writer},
m_network_system_context_writer{m_time->m_network_system_clock_context_writer},
m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer},
m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} {
m_local_operation{system_}, m_network_operation{system_}, m_ephemeral_operation{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&ServiceManager::GetStaticServiceAsUser>, "GetStaticServiceAsUser"},
@@ -93,7 +93,7 @@ Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected,
m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset,
is_rtc_reset_detected);
auto time = m_steady_clock.GetRawTime();
auto ticks = m_system.CoreTiming().GetClockTicks();
auto ticks = system.CoreTiming().GetClockTicks();
auto boot_time = time - ConvertToTimeSpan(ticks).count();
m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time);
m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time);
@@ -219,7 +219,7 @@ Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) {
m_steady_clock.SetRtcOffset(base_time);
auto time = m_steady_clock.GetRawTime();
auto ticks = m_system.CoreTiming().GetClockTicks();
auto ticks = system.CoreTiming().GetClockTicks();
auto diff = time - ConvertToTimeSpan(ticks).count();
m_shared_memory.UpdateBaseTime(diff);
m_steady_clock.UpdateContinuousAdjustmentTime(diff);
@@ -241,7 +241,7 @@ Result ServiceManager::GetClosestAlarmUpdatedEvent(
Result ServiceManager::CheckAndSignalAlarms() {
LOG_DEBUG(Service_Time, "called.");
m_alarms.CheckAndSignal(m_system.Kernel());
m_alarms.CheckAndSignal(system.Kernel());
R_SUCCEED();
}
@@ -277,16 +277,16 @@ void ServiceManager::SetupSAndP() {
m_is_s_and_p_setup = true;
m_server_manager.RegisterNamedService(
"time:s", std::make_shared<StaticService>(
m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s"));
system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s"));
m_server_manager.RegisterNamedService("time:p",
std::make_shared<IPowerStateRequestHandler>(
m_system, m_time->m_power_state_request_manager));
system, m_time->m_power_state_request_manager));
}
}
Result ServiceManager::GetStaticService(OutInterface<StaticService> out_service,
StaticServiceSetupInfo setup_info, const char* name) {
*out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name);
*out_service = std::make_shared<StaticService>(system, setup_info, m_time, name);
R_SUCCEED();
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -60,10 +63,8 @@ public:
private:
void CheckAndSetupServicesSAndP();
void SetupSAndP();
Result GetStaticService(OutInterface<StaticService> out_service,
StaticServiceSetupInfo setup_info, const char* name);
Result GetStaticService(OutInterface<StaticService> out_service, StaticServiceSetupInfo setup_info, const char* name);
Core::System& m_system;
std::shared_ptr<TimeManager> m_time;
ServerManager& m_server_manager;
bool m_is_s_and_p_setup{};
+8 -8
View File
@@ -35,7 +35,7 @@ constexpr Result GetTimeFromTimePointAndContext(s64* out_time, SteadyClockTimePo
StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup_info,
std::shared_ptr<TimeManager> time, const char* name)
: ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time},
: ServiceFramework{system_, name}, m_setup_info{setup_info}, m_time{time},
m_local_system_clock{m_time->m_standard_local_system_clock},
m_user_system_clock{m_time->m_standard_user_system_clock},
m_network_system_clock{m_time->m_standard_network_system_clock},
@@ -72,7 +72,7 @@ StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup
Result StaticService::GetStandardUserSystemClock(OutInterface<SystemClock> out_service) {
LOG_DEBUG(Service_Time, "called.");
*out_service = std::make_shared<SystemClock>(m_system, m_user_system_clock,
*out_service = std::make_shared<SystemClock>(system, m_user_system_clock,
m_setup_info.can_write_user_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
@@ -81,7 +81,7 @@ Result StaticService::GetStandardUserSystemClock(OutInterface<SystemClock> out_s
Result StaticService::GetStandardNetworkSystemClock(OutInterface<SystemClock> out_service) {
LOG_DEBUG(Service_Time, "called.");
*out_service = std::make_shared<SystemClock>(m_system, m_network_system_clock,
*out_service = std::make_shared<SystemClock>(system, m_network_system_clock,
m_setup_info.can_write_network_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
@@ -91,7 +91,7 @@ Result StaticService::GetStandardSteadyClock(OutInterface<SteadyClock> out_servi
LOG_DEBUG(Service_Time, "called.");
*out_service =
std::make_shared<SteadyClock>(m_system, m_time, m_setup_info.can_write_steady_clock,
std::make_shared<SteadyClock>(system, m_time, m_setup_info.can_write_steady_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
}
@@ -100,7 +100,7 @@ Result StaticService::GetTimeZoneService(OutInterface<TimeZoneService> out_servi
LOG_DEBUG(Service_Time, "called.");
*out_service =
std::make_shared<TimeZoneService>(m_system, m_time->m_standard_steady_clock, m_time_zone,
std::make_shared<TimeZoneService>(system, m_time->m_standard_steady_clock, m_time_zone,
m_setup_info.can_write_timezone_device_location);
R_SUCCEED();
}
@@ -108,7 +108,7 @@ Result StaticService::GetTimeZoneService(OutInterface<TimeZoneService> out_servi
Result StaticService::GetStandardLocalSystemClock(OutInterface<SystemClock> out_service) {
LOG_DEBUG(Service_Time, "called.");
*out_service = std::make_shared<SystemClock>(m_system, m_local_system_clock,
*out_service = std::make_shared<SystemClock>(system, m_local_system_clock,
m_setup_info.can_write_local_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
@@ -117,7 +117,7 @@ Result StaticService::GetStandardLocalSystemClock(OutInterface<SystemClock> out_
Result StaticService::GetEphemeralNetworkSystemClock(OutInterface<SystemClock> out_service) {
LOG_DEBUG(Service_Time, "called.");
*out_service = std::make_shared<SystemClock>(m_system, m_ephemeral_network_clock,
*out_service = std::make_shared<SystemClock>(system, m_ephemeral_network_clock,
m_setup_info.can_write_network_clock,
m_setup_info.can_write_uninitialized_clock);
R_SUCCEED();
@@ -222,7 +222,7 @@ Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
auto one_second_ns{
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
auto ticks{m_system.CoreTiming().GetClockTicks()};
auto ticks{system.CoreTiming().GetClockTicks()};
auto current_time_ns{ConvertToTimeSpan(ticks).count()};
*out_time = ((context.offset + time_point.time_point) - (current_time_ns / one_second_ns));
+3 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -68,8 +71,6 @@ private:
Result GetClockSnapshotImpl(OutClockSnapshot out_snapshot,
const SystemClockContext& user_context,
const SystemClockContext& network_context, TimeType type);
Core::System& m_system;
StaticServiceSetupInfo m_setup_info;
std::shared_ptr<TimeManager> m_time;
StandardLocalSystemClockCore& m_local_system_clock;
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -13,10 +13,11 @@ namespace Service::PSC::Time {
SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> manager,
bool can_write_steady_clock, bool can_write_uninitialized_clock)
: ServiceFramework{system_, "ISteadyClock"}, m_system{system},
m_clock_core{manager->m_standard_steady_clock},
m_can_write_steady_clock{can_write_steady_clock}, m_can_write_uninitialized_clock{
can_write_uninitialized_clock} {
: ServiceFramework{system_, "ISteadyClock"}
, m_clock_core{manager->m_standard_steady_clock}
, m_can_write_steady_clock{can_write_steady_clock}
, m_can_write_uninitialized_clock{can_write_uninitialized_clock}
{
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&SteadyClock::GetCurrentTimePoint>, "GetCurrentTimePoint"},
+3 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -32,8 +35,6 @@ public:
Result GetInternalOffset(Out<s64> out_internal_offset);
private:
Core::System& m_system;
StandardSteadyClockCore& m_clock_core;
bool m_can_write_steady_clock;
bool m_can_write_uninitialized_clock;
+10 -6
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,11 +11,12 @@
namespace Service::PSC::Time {
SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock,
bool can_write_uninitialized_clock)
: ServiceFramework{system_, "ISystemClock"}, m_system{system}, m_clock_core{clock_core},
m_can_write_clock{can_write_clock}, m_can_write_uninitialized_clock{
can_write_uninitialized_clock} {
SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock, bool can_write_uninitialized_clock)
: ServiceFramework{system_, "ISystemClock"}
, m_clock_core{clock_core}
, m_can_write_clock{can_write_clock}
, m_can_write_uninitialized_clock{can_write_uninitialized_clock}
{
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&SystemClock::GetCurrentTime>, "GetCurrentTime"},
@@ -72,7 +76,7 @@ Result SystemClock::GetOperationEventReadableHandle(
LOG_DEBUG(Service_Time, "called.");
if (!m_operation_event) {
m_operation_event = std::make_unique<OperationEvent>(m_system);
m_operation_event = std::make_unique<OperationEvent>(system);
R_UNLESS(m_operation_event != nullptr, ResultFailed);
m_clock_core.LinkOperationEvent(*m_operation_event);
+4 -5
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,9 +21,7 @@ namespace Service::PSC::Time {
class SystemClock final : public ServiceFramework<SystemClock> {
public:
explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core,
bool can_write_clock, bool can_write_uninitialized_clock);
explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core, bool can_write_clock, bool can_write_uninitialized_clock);
~SystemClock() override = default;
Result GetCurrentTime(Out<s64> out_time);
@@ -30,8 +31,6 @@ public:
Result GetOperationEventReadableHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
private:
Core::System& m_system;
SystemClockCore& m_clock_core;
bool m_can_write_clock;
bool m_can_write_uninitialized_clock;
@@ -15,9 +15,11 @@ namespace Service::PSC::Time {
TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& clock_core,
TimeZone& time_zone, bool can_write_timezone_device_location)
: ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_clock_core{clock_core},
m_time_zone{time_zone}, m_can_write_timezone_device_location{
can_write_timezone_device_location} {
: ServiceFramework{system_, "ITimeZoneService"}
, m_clock_core{clock_core}
, m_time_zone{time_zone}
, m_can_write_timezone_device_location{can_write_timezone_device_location}
{
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&TimeZoneService::GetDeviceLocationName>, "GetDeviceLocationName"},
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -56,8 +59,6 @@ public:
const CalendarTime& calendar_time);
private:
Core::System& m_system;
StandardSteadyClockCore& m_clock_core;
TimeZone& m_time_zone;
bool m_can_write_timezone_device_location;
@@ -17,10 +17,14 @@ bool TranslatorVisitor::arm_B(Cond cond, Imm<24> imm24) {
if (!ArmConditionPassed(cond)) {
return true;
}
const u32 imm32 = mcl::bit::sign_extend<26, u32>(imm24.ZeroExtend() << 2) + 8;
const auto new_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{new_location});
// pattern match => b .
if (imm32 == 0) {
ir.SetTerm(IR::Term::LinkBlock{new_location});
} else {
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
}
return false;
}
@@ -35,7 +39,7 @@ bool TranslatorVisitor::arm_BL(Cond cond, Imm<24> imm24) {
const u32 imm32 = mcl::bit::sign_extend<26, u32>(imm24.ZeroExtend() << 2) + 8;
const auto new_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{new_location});
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
return false;
}
@@ -46,7 +50,7 @@ bool TranslatorVisitor::arm_BLX_imm(bool H, Imm<24> imm24) {
const u32 imm32 = mcl::bit::sign_extend<26, u32>((imm24.ZeroExtend() << 2)) + (H ? 2 : 0) + 8;
const auto new_location = ir.current_location.AdvancePC(imm32).SetTFlag(true);
ir.SetTerm(IR::Term::LinkBlock{new_location});
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
return false;
}
@@ -56,11 +56,10 @@ bool TranslatorVisitor::arm_MSR_imm(Cond cond, unsigned mask, int rotate, Imm<8>
if (write_e) {
const bool E = (imm32 & 0x00000200) != 0;
if (E != ir.current_location.EFlag()) {
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)});
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).SetEFlag(E)});
return false;
}
}
return true;
}
@@ -112,7 +111,7 @@ bool TranslatorVisitor::arm_RFE() {
// SETEND <endian_specifier>
bool TranslatorVisitor::arm_SETEND(bool E) {
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)});
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).SetEFlag(E)});
return false;
}
@@ -803,7 +803,7 @@ bool TranslatorVisitor::thumb16_SETEND(bool E) {
return true;
}
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(2).SetEFlag(E).AdvanceIT()});
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).SetEFlag(E).AdvanceIT()});
return false;
}
@@ -906,9 +906,8 @@ bool TranslatorVisitor::thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Re
ir.SetCheckBit(ir.IsZero(rn));
const auto [cond_pass, cond_fail] = [this, imm, nonzero] {
const auto skip = IR::Term::LinkBlock{ir.current_location.AdvancePC(2).AdvanceIT()};
const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(imm + 4).AdvanceIT()};
const auto skip = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).AdvanceIT()};
const auto branch = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm + 4).AdvanceIT()};
if (nonzero) {
return std::make_pair(skip, branch);
} else {
@@ -975,10 +974,10 @@ bool TranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) {
}
const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4);
const auto then_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
const auto else_location = ir.current_location.AdvancePC(2).AdvanceIT();
const auto then_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm32).AdvanceIT()};
const auto else_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).AdvanceIT()};
ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}});
ir.SetTerm(IR::Term::If{cond, then_, else_});
return false;
}
@@ -990,8 +989,12 @@ bool TranslatorVisitor::thumb16_B_t2(Imm<11> imm11) {
const s32 imm32 = static_cast<s32>((imm11.SignExtend<u32>() << 1) + 4);
const auto next_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
ir.SetTerm(IR::Term::LinkBlock{next_location});
// pattern (b +#0)
if (imm32 == 0) {
ir.SetTerm(IR::Term::LinkBlock{next_location});
} else {
ir.SetTerm(IR::Term::LinkBlockFast{next_location});
}
return false;
}
@@ -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.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -20,10 +23,8 @@ bool TranslatorVisitor::thumb32_BL_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
const s32 imm32 = static_cast<s32>((concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1) + 4);
const auto new_location = ir.current_location
.AdvancePC(imm32)
.AdvanceIT();
ir.SetTerm(IR::Term::LinkBlock{new_location});
auto const new_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
return false;
}
@@ -44,11 +45,8 @@ bool TranslatorVisitor::thumb32_BLX_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1>
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
const s32 imm32 = static_cast<s32>(concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1);
const auto new_location = ir.current_location
.SetPC(ir.AlignPC(4) + imm32)
.SetTFlag(false)
.AdvanceIT();
ir.SetTerm(IR::Term::LinkBlock{new_location});
const auto new_location = ir.current_location.SetPC(ir.AlignPC(4) + imm32).SetTFlag(false).AdvanceIT();
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
return false;
}
@@ -61,10 +59,13 @@ bool TranslatorVisitor::thumb32_B(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Im
}
const s32 imm32 = static_cast<s32>((concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1) + 4);
const auto new_location = ir.current_location
.AdvancePC(imm32)
.AdvanceIT();
ir.SetTerm(IR::Term::LinkBlock{new_location});
auto const new_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
// Pattern to halt execution (b +#0)
if (imm32 == 0) {
ir.SetTerm(IR::Term::LinkBlock{new_location});
} else {
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
}
return false;
}
@@ -75,13 +76,9 @@ bool TranslatorVisitor::thumb32_B_cond(Imm<1> S, Cond cond, Imm<6> hi, Imm<1> i1
// Note: i1 and i2 were not inverted from encoding and are opposite compared to the other B instructions.
const s32 imm32 = static_cast<s32>((concatenate(S, i2, i1, hi, lo).SignExtend<u32>() << 1) + 4);
const auto then_location = ir.current_location
.AdvancePC(imm32)
.AdvanceIT();
const auto else_location = ir.current_location
.AdvancePC(4)
.AdvanceIT();
ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}});
const auto then_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm32).AdvanceIT()};
const auto else_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).AdvanceIT()};
ir.SetTerm(IR::Term::If{cond, then_, else_});
return false;
}
@@ -14,8 +14,8 @@ bool TranslatorVisitor::B_cond(Imm<19> imm19, Cond cond) {
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::If{cond, cond_pass, cond_fail});
return false;
}
@@ -26,9 +26,9 @@ bool TranslatorVisitor::B_uncond(Imm<26> imm26) {
// Pattern to halt execution (B .)
if (target == ir.PC()) {
ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)});
return false;
} else {
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location->SetPC(target)});
}
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location->SetPC(target)});
return false;
}
@@ -79,8 +79,8 @@ bool TranslatorVisitor::CBZ(bool sf, Imm<19> imm19, Reg Rt) {
ir.SetCheckBit(ir.IsZero(operand1));
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
return false;
}
@@ -94,8 +94,8 @@ bool TranslatorVisitor::CBNZ(bool sf, Imm<19> imm19, Reg Rt) {
ir.SetCheckBit(ir.IsZero(operand1));
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
return false;
}
@@ -110,8 +110,8 @@ bool TranslatorVisitor::TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
const u64 target = ir.PC() + offset;
const auto cond_1 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_0 = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_1 = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
const auto cond_0 = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
return false;
}
@@ -126,8 +126,8 @@ bool TranslatorVisitor::TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
const u64 target = ir.PC() + offset;
const auto cond_1 = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_0 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_1 = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
const auto cond_0 = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
return false;
}
+9 -7
View File
@@ -1,7 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "common/assert.h"
#include "hid_core/frontend/emulated_console.h"
#include "hid_core/frontend/input_converter.h"
@@ -308,17 +312,15 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
std::scoped_lock lock{callback_mutex};
++last_callback_key;
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
return last_callback_key;
}
void EmulatedConsole::DeleteCallback(int key) {
std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
auto const it = callback_list.find(key);
ASSERT_MSG(it != callback_list.end(), "Tried to delete non-existent callback {}", key);
callback_list.erase(it);
}
} // namespace Core::HID
+9 -13
View File
@@ -10,6 +10,7 @@
#include <ranges>
#include "common/thread.h"
#include "common/assert.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/frontend/input_converter.h"
#include "hid_core/hid_util.h"
@@ -1876,12 +1877,9 @@ NpadColor EmulatedController::GetNpadColor(u32 color) {
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
std::unique_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ControllerUpdateCallback& poller = poller_pair.second;
if (!is_npad_service_update && poller.is_npad_service) {
continue;
}
if (poller.on_change) {
for (auto const& p : callback_list) {
auto const& poller = p.second;
if (is_npad_service_update || !poller.is_npad_service) {
poller.on_change(type);
}
}
@@ -1889,18 +1887,16 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
std::unique_lock lock{callback_mutex};
++last_callback_key;
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
return last_callback_key;
}
void EmulatedController::DeleteCallback(int key) {
std::unique_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
auto const it = callback_list.find(key);
ASSERT_MSG(it != callback_list.end(), "Tried to delete non-existent callback {}", key);
callback_list.erase(it);
}
void EmulatedController::StatusUpdate() {
+9 -7
View File
@@ -1,9 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <fmt/ranges.h>
#include "common/assert.h"
#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/frontend/input_converter.h"
@@ -467,17 +471,15 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
std::scoped_lock lock{callback_mutex};
++last_callback_key;
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
return last_callback_key;
}
void EmulatedDevices::DeleteCallback(int key) {
std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
auto const it = callback_list.find(key);
ASSERT_MSG(it != callback_list.end(), "Tried to delete non-existent callback {}", key);
callback_list.erase(it);
}
} // namespace Core::HID
+6 -2
View File
@@ -1,7 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cmath>
#include <numbers>
#include "common/math_util.h"
#include "hid_core/frontend/motion_input.h"
@@ -145,7 +149,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
}
const auto normal_accel = accel.Normalized();
auto rad_gyro = gyro * Common::PI * 2;
auto rad_gyro = gyro * std::numbers::pi_v<float> * 2.f;
const f32 swap = rad_gyro.x;
rad_gyro.x = rad_gyro.y;
rad_gyro.y = -swap;
@@ -272,7 +276,7 @@ Common::Vec3f MotionInput::GetEulerAngles() const {
return {
std::atan2(sinr_cosp, cosr_cosp),
2 * std::atan2(sinp, cosp) - Common::PI / 2,
2 * std::atan2(sinp, cosp) - float(std::numbers::pi_v<float>) / 2,
std::atan2(siny_cosp, cosy_cosp),
};
}
+2 -1
View File
@@ -24,7 +24,8 @@ HIDCore::HIDCore(Kernel::KernelCore& kernel_)
, player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)}
, other{std::make_unique<EmulatedController>(NpadIdType::Other)}
, handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)}
, console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()}
, console{std::make_unique<EmulatedConsole>()}
, devices{std::make_unique<EmulatedDevices>()}
, kernel{kernel_}
{}
+15 -17
View File
@@ -45,29 +45,17 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
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) {
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
}
}
NPad::~NPad() {
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->DeleteCallback(controller.callback_key);
for (std::size_t aruid = 0; aruid < AruidIndexMax; ++aruid) {
for (std::size_t i = 0; i < controller_data[aruid].size(); ++i) {
if (auto& controller = controller_data[aruid][i]; controller.device && controller.callback_key) {
controller.device->DeleteCallback(controller.callback_key);
}
}
}
}
@@ -101,6 +89,16 @@ Result NPad::Activate(u64 aruid) {
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
auto& controller = controller_data[aruid_index][i];
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
@@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <numbers>
#include "common/math_util.h"
#include "hid_core/resources/touch_screen/gesture_handler.h"
@@ -214,7 +215,7 @@ void GestureHandler::UpdatePanEvent(GestureState& next_state, GestureType& type)
if (std::abs(angle_between_two_lines) > AngleThreshold) {
type = GestureType::Rotate;
next_state.scale = 0;
next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
next_state.rotation_angle = angle_between_two_lines * 180.0f / float(std::numbers::pi_v<float>);
}
}
+29 -22
View File
@@ -3,6 +3,8 @@
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <numbers>
#include "common/logging.h"
#include "common/math_util.h"
#include "common/param_package.h"
@@ -146,9 +148,9 @@ public:
break;
}
case SDL_SENSOR_GYRO: {
motion.gyro_x = event.data[0] / (Common::PI * 2);
motion.gyro_y = -event.data[2] / (Common::PI * 2);
motion.gyro_z = event.data[1] / (Common::PI * 2);
motion.gyro_x = event.data[0] / (f32(std::numbers::pi_v<float>) * 2);
motion.gyro_y = -event.data[2] / (f32(std::numbers::pi_v<float>) * 2);
motion.gyro_z = event.data[1] / (f32(std::numbers::pi_v<float>) * 2);
break;
}
}
@@ -948,34 +950,39 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
}
const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
auto* controller = joystick->GetSDLGameController();
if (controller == nullptr) {
return {};
}
// This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
// We will add those afterwards
ButtonBindings switch_to_sdl_button;
switch_to_sdl_button = GetDefaultButtonBinding(joystick);
ButtonBindings switch_to_sdl_button = GetDefaultButtonBinding(joystick);
// Add the missing bindings for ZL/ZR
static constexpr ZButtonBindings switch_to_sdl_axis{{
{Settings::NativeButton::ZL, SDL_GAMEPAD_AXIS_LEFT_TRIGGER},
{Settings::NativeButton::ZR, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER},
}};
// Parameters contain two joysticks return dual
if (params.Has("guid2")) {
const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
if (joystick2->GetSDLGameController() != nullptr) {
return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button,
switch_to_sdl_axis);
if (auto* controller = joystick->GetSDLGameController(); controller) {
// Parameters contain two joysticks return dual
if (params.Has("guid2")) {
const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
if (joystick2->GetSDLGameController())
return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button, switch_to_sdl_axis);
}
return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
}
return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
// Default mappings for when the controller doesn't have an associated gamepad
// This fixes issues where SDL uses a backend which doesn't support gamepad remappings
ButtonMapping mapping;
for (const auto& pair : switch_to_sdl_button) {
SDL_GamepadBinding binding{};
binding.input_type = SDL_GAMEPAD_BINDTYPE_BUTTON;
binding.input.button = pair.second;
mapping.insert_or_assign(pair.first, BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
}
for (const auto& pair : switch_to_sdl_axis) {
SDL_GamepadBinding binding{};
binding.input_type = SDL_GAMEPAD_BINDTYPE_AXIS;
binding.input.axis.axis = pair.second;
mapping.insert_or_assign(pair.first, BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
}
return mapping;
}
ButtonBindings SDLDriver::GetDefaultButtonBinding(
+19 -47
View File
@@ -1,8 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <cmath>
#include <numbers>
#include "common/math_util.h"
#include "common/settings.h"
#include "input_common/helpers/stick_from_buttons.h"
@@ -15,9 +19,8 @@ public:
// do not play nicely with the theoretical maximum range.
// Using a value one lower from the maximum emulates real stick behavior.
static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
static constexpr float TAU = Common::PI * 2.0f;
// Use wider angle to ease the transition.
static constexpr float APERTURE = TAU * 0.15f;
static constexpr float APERTURE = float((std::numbers::pi_v<float> / 2.f)) * 0.15f;
using Button = std::unique_ptr<Common::Input::InputDevice>;
@@ -66,13 +69,13 @@ public:
bool IsAngleGreater(float old_angle, float new_angle) const {
const float top_limit = new_angle + APERTURE;
return (old_angle > new_angle && old_angle <= top_limit) ||
(old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
(old_angle + f32((std::numbers::pi_v<float> / 2.f)) > new_angle && old_angle + f32((std::numbers::pi_v<float> / 2.f)) <= top_limit);
}
bool IsAngleSmaller(float old_angle, float new_angle) const {
const float bottom_limit = new_angle - APERTURE;
return (old_angle >= bottom_limit && old_angle < new_angle) ||
(old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
(old_angle - f32((std::numbers::pi_v<float> / 2.f)) >= bottom_limit && old_angle - f32((std::numbers::pi_v<float> / 2.f)) < new_angle);
}
float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
@@ -87,16 +90,16 @@ public:
if (IsAngleGreater(new_angle, goal_angle)) {
new_angle -= modifier_angle * time_difference;
if (new_angle < 0) {
new_angle += TAU;
if (new_angle < 0.f) {
new_angle += f32((std::numbers::pi_v<float> / 2.f));
}
if (!IsAngleGreater(new_angle, goal_angle)) {
return goal_angle;
}
} else if (IsAngleSmaller(new_angle, goal_angle)) {
new_angle += modifier_angle * time_difference;
if (new_angle >= TAU) {
new_angle -= TAU;
if (new_angle >= f32((std::numbers::pi_v<float> / 2.f))) {
new_angle -= f32((std::numbers::pi_v<float> / 2.f));
}
if (!IsAngleSmaller(new_angle, goal_angle)) {
return goal_angle;
@@ -108,45 +111,14 @@ public:
}
void SetGoalAngle(bool r, bool l, bool u, bool d) {
// Move to the right
if (r && !u && !d) {
goal_angle = 0.0f;
}
// Move to the upper right
if (r && u && !d) {
goal_angle = Common::PI * 0.25f;
}
// Move up
if (u && !l && !r) {
goal_angle = Common::PI * 0.5f;
}
// Move to the upper left
if (l && u && !d) {
goal_angle = Common::PI * 0.75f;
}
// Move to the left
if (l && !u && !d) {
goal_angle = Common::PI;
}
// Move to the bottom left
if (l && !u && d) {
goal_angle = Common::PI * 1.25f;
}
// Move down
if (d && !l && !r) {
goal_angle = Common::PI * 1.5f;
}
// Move to the bottom right
if (r && !u && d) {
goal_angle = Common::PI * 1.75f;
}
if (r && !u && !d) goal_angle = f32(std::numbers::pi_v<float>) * 0.00f; //right
if (r && u && !d) goal_angle = f32(std::numbers::pi_v<float>) * 0.25f; //upper right
if (u && !l && !r) goal_angle = f32(std::numbers::pi_v<float>) * 0.50f; //up
if (l && u && !d) goal_angle = f32(std::numbers::pi_v<float>) * 0.75f; //upper left
if (l && !u && !d) goal_angle = f32(std::numbers::pi_v<float>) * 1.00f; //left
if (l && !u && d) goal_angle = f32(std::numbers::pi_v<float>) * 1.25f; //bottom left
if (d && !l && !r) goal_angle = f32(std::numbers::pi_v<float>) * 1.50f; //down
if (r && !u && d) goal_angle = f32(std::numbers::pi_v<float>) * 1.75f; //bottom right
}
void UpdateUpButtonStatus(const Common::Input::CallbackStatus& button_callback) {
+6 -7
View File
@@ -4,6 +4,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging.h"
#include "common/assert.h"
#include "input_common/input_engine.h"
namespace InputCommon {
@@ -463,8 +464,9 @@ const std::string& InputEngine::GetEngineName() const {
int InputEngine::SetCallback(InputIdentifier input_identifier) {
std::scoped_lock lock{mutex_callback};
++last_callback_key;
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
return last_callback_key++;
return last_callback_key;
}
void InputEngine::SetMappingCallback(MappingCallback callback) {
@@ -474,12 +476,9 @@ void InputEngine::SetMappingCallback(MappingCallback callback) {
void InputEngine::DeleteCallback(int key) {
std::scoped_lock lock{mutex_callback};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
auto const it = callback_list.find(key);
ASSERT_MSG(it != callback_list.end(), "Tried to delete non-existent callback {}", key);
callback_list.erase(it);
}
} // namespace InputCommon
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -665,6 +665,8 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU3
const IR::Value& clamp, const IR::Value& segmentation_mask);
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
const IR::Value& clamp, const IR::Value& segmentation_mask);
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 lane);
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 direction);
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
ScalarU32 swizzle);
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -97,6 +100,26 @@ void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, Sca
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
}
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 lane) {
// QuadBroadcast: src = (thread_id & ~3) | (lane & 3) — SHFIDX with quad mask 0x1C03
const Register ret{ctx.reg_alloc.Define(inst)};
ctx.Add("AND.U RC.x,{}.threadid,~3;"
"AND.U RC.y,{},3;"
"OR.U RC.x,RC.x,RC.y;"
"SHFIDX.U {},{},RC.x,0x1C03;"
"MOV.U {}.x,{}.y;",
ctx.stage_name, lane, ret, value, ret, ret);
}
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 direction) {
// QuadSwap: XOR thread_id with (direction+1) within quad — SHFXOR with quad mask 0x1C03
const Register ret{ctx.reg_alloc.Define(inst)};
ctx.Add("ADD.U RC.x,{},1;"
"SHFXOR.U {},{},RC.x,0x1C03;"
"MOV.U {}.x,{}.y;",
direction, ret, value, ret, ret);
}
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
ScalarU32 swizzle) {
const auto ret{ctx.reg_alloc.Define(inst)};
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -743,6 +743,10 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view index, std::string_view clamp,
std::string_view segmentation_mask);
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view lane);
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view direction);
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
std::string_view swizzle);
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -200,6 +203,20 @@ void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view val
ctx.AddU32("{}=shfl_in_bounds?shfl_result:{};", inst, value);
}
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view lane) {
// QuadBroadcast: read from (thread_id & ~3) | (lane & 3) within the subgroup
const auto src_thread_id{fmt::format("(({}&~3)|({}& 3))", THREAD_ID, lane)};
ctx.AddU32("{}=readInvocationARB({},{});", inst, value, src_thread_id);
}
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, std::string_view value,
std::string_view direction) {
// QuadSwap: XOR thread_id with (direction+1) — maps directions 0/1/2 to XOR 1/2/3
const auto src_thread_id{fmt::format("({}^({}+1))", THREAD_ID, direction)};
ctx.AddU32("{}=readInvocationARB({},{});", inst, value, src_thread_id);
}
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
std::string_view swizzle) {
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
@@ -322,6 +322,12 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
if (ctx.runtime_info.force_early_z) {
ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
}
if (ctx.profile.support_shader_quad_control &&
(program.info.uses_derivatives || program.info.uses_quad_shuffles)) {
ctx.AddExtension("SPV_KHR_quad_control");
ctx.AddCapability(spv::Capability::QuadControlKHR);
ctx.AddExecutionMode(main, spv::ExecutionMode::RequireFullQuadsKHR);
}
break;
default:
throw NotImplementedException("Stage {}", program.stage);
@@ -440,6 +446,12 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
ctx.AddCapability(spv::Capability::GroupNonUniformVote);
}
}
if (info.uses_quad_shuffles) {
if (profile.support_quad_shuffles) {
ctx.AddCapability(spv::Capability::GroupNonUniformQuad);
}
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
}
if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
ctx.AddCapability(spv::Capability::Int64Atomics);
}
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -622,6 +622,8 @@ Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clam
Id segmentation_mask);
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
Id segmentation_mask);
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane);
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction);
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle);
Id EmitDPdxFine(EmitContext& ctx, Id op_a);
Id EmitDPdyFine(EmitContext& ctx, Id op_a);
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -220,6 +223,21 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
return SelectValue(ctx, in_range, value, src_thread_id);
}
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane) {
if (ctx.profile.support_quad_shuffles) {
return ctx.OpGroupNonUniformQuadBroadcast(ctx.U32[1], SubgroupScope(ctx), value, lane);
}
const Id base{ctx.OpBitwiseAnd(ctx.U32[1], GetThreadId(ctx), ctx.Const(~3u))};
const Id local_lane{ctx.OpBitwiseAnd(ctx.U32[1], lane, ctx.Const(3u))};
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], base, local_lane)};
return ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id);
}
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction) {
const Id xor_mask{ctx.OpIAdd(ctx.U32[1], direction, ctx.Const(1u))};
return ctx.OpGroupNonUniformShuffleXor(ctx.U32[1], SubgroupScope(ctx), value, xor_mask);
}
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
const Id three{ctx.Const(3U)};
Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
+5 -4
View File
@@ -38,10 +38,11 @@ struct CbufWordKeyHash {
struct HandleKey {
u32 index, offset, shift_left;
u32 sec_index, sec_offset, sec_shift_left;
u32 count;
bool has_secondary;
constexpr bool operator==(const HandleKey& o) const noexcept {
return std::tie(index, offset, shift_left, sec_index, sec_offset, sec_shift_left, has_secondary)
== std::tie(o.index, o.offset, o.shift_left, o.sec_index, o.sec_offset, o.sec_shift_left, o.has_secondary);
return std::tie(index, offset, shift_left, sec_index, sec_offset, sec_shift_left, count, has_secondary)
== std::tie(o.index, o.offset, o.shift_left, o.sec_index, o.sec_offset, o.sec_shift_left, o.count, o.has_secondary);
}
};
struct HandleKeyHash {
@@ -50,8 +51,8 @@ struct HandleKeyHash {
h ^= (size_t(k.shift_left) << 1);
h ^= (size_t(k.sec_index) << 33) ^ (size_t(k.sec_offset) << 2);
h ^= (size_t(k.sec_shift_left) << 3);
h ^= k.has_secondary ? 0x9e3779b97f4a7c15ULL : 0ULL;
return h;
h ^= (size_t(k.count) << 7);
return h ^ (k.has_secondary ? 0x9e3779b97f4a7c15ULL : 0ULL);
}
};
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -2100,6 +2100,14 @@ U32 IREmitter::ShuffleButterfly(const IR::U32& value, const IR::U32& index, cons
return Inst<U32>(Opcode::ShuffleButterfly, value, index, clamp, seg_mask);
}
U32 IREmitter::QuadBroadcast(const IR::U32& value, const IR::U32& lane) {
return Inst<U32>(Opcode::QuadBroadcast, value, lane);
}
U32 IREmitter::QuadSwap(const IR::U32& value, const IR::U32& direction) {
return Inst<U32>(Opcode::QuadSwap, value, direction);
}
F32 IREmitter::FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle, FpControl control) {
return Inst<F32>(Opcode::FSwizzleAdd, Flags{control}, a, b, swizzle);
}
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -394,6 +394,8 @@ public:
const IR::U32& seg_mask);
[[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index,
const IR::U32& clamp, const IR::U32& seg_mask);
[[nodiscard]] U32 QuadBroadcast(const IR::U32& value, const IR::U32& lane);
[[nodiscard]] U32 QuadSwap(const IR::U32& value, const IR::U32& direction);
[[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle,
FpControl control = {});
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -5,6 +8,27 @@
namespace Shader::IR {
namespace Detail {
OpcodeMeta META_TABLE[532] = {
#define OPCODE(name_token, type_token, ...) \
{ \
.name{#name_token}, \
.type = type_token, \
.arg_types{__VA_ARGS__}, \
},
#include "opcodes.inc"
#undef OPCODE
};
u8 NUM_ARGS[532] = {
#define OPCODE(name_token, type_token, ...) u8(CalculateNumArgsOf(Opcode::name_token)),
#include "opcodes.inc"
#undef OPCODE
};
}
std::string_view NameOf(Opcode op) {
return Detail::META_TABLE[static_cast<size_t>(op)].name;
}
+35 -50
View File
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -30,69 +30,54 @@ struct OpcodeMeta {
};
// using enum Type;
constexpr Type Void{Type::Void};
constexpr Type Opaque{Type::Opaque};
constexpr Type Reg{Type::Reg};
constexpr Type Pred{Type::Pred};
constexpr Type Attribute{Type::Attribute};
constexpr Type Patch{Type::Patch};
constexpr Type U1{Type::U1};
constexpr Type U8{Type::U8};
constexpr Type U16{Type::U16};
constexpr Type U32{Type::U32};
constexpr Type U64{Type::U64};
constexpr Type F16{Type::F16};
constexpr Type F32{Type::F32};
constexpr Type F64{Type::F64};
constexpr Type U32x2{Type::U32x2};
constexpr Type U32x3{Type::U32x3};
constexpr Type U32x4{Type::U32x4};
constexpr Type F16x2{Type::F16x2};
constexpr Type F16x3{Type::F16x3};
constexpr Type F16x4{Type::F16x4};
constexpr Type F32x2{Type::F32x2};
constexpr Type F32x3{Type::F32x3};
constexpr Type F32x4{Type::F32x4};
constexpr Type F64x2{Type::F64x2};
constexpr Type F64x3{Type::F64x3};
constexpr Type F64x4{Type::F64x4};
static constexpr Type Void{Type::Void};
static constexpr Type Opaque{Type::Opaque};
static constexpr Type Reg{Type::Reg};
static constexpr Type Pred{Type::Pred};
static constexpr Type Attribute{Type::Attribute};
static constexpr Type Patch{Type::Patch};
static constexpr Type U1{Type::U1};
static constexpr Type U8{Type::U8};
static constexpr Type U16{Type::U16};
static constexpr Type U32{Type::U32};
static constexpr Type U64{Type::U64};
static constexpr Type F16{Type::F16};
static constexpr Type F32{Type::F32};
static constexpr Type F64{Type::F64};
static constexpr Type U32x2{Type::U32x2};
static constexpr Type U32x3{Type::U32x3};
static constexpr Type U32x4{Type::U32x4};
static constexpr Type F16x2{Type::F16x2};
static constexpr Type F16x3{Type::F16x3};
static constexpr Type F16x4{Type::F16x4};
static constexpr Type F32x2{Type::F32x2};
static constexpr Type F32x3{Type::F32x3};
static constexpr Type F32x4{Type::F32x4};
static constexpr Type F64x2{Type::F64x2};
static constexpr Type F64x3{Type::F64x3};
static constexpr Type F64x4{Type::F64x4};
constexpr OpcodeMeta META_TABLE[]{
#define OPCODE(name_token, type_token, ...) \
{ \
.name{#name_token}, \
.type = type_token, \
.arg_types{__VA_ARGS__}, \
},
#include "opcodes.inc"
#undef OPCODE
};
constexpr size_t CalculateNumArgsOf(Opcode op) {
const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types};
return static_cast<size_t>(
std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void)));
extern OpcodeMeta META_TABLE[532];
constexpr size_t CalculateNumArgsOf(Opcode op) noexcept {
const auto& arg_types = META_TABLE[size_t(op)].arg_types;
return size_t(std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void)));
}
constexpr u8 NUM_ARGS[]{
#define OPCODE(name_token, type_token, ...) static_cast<u8>(CalculateNumArgsOf(Opcode::name_token)),
#include "opcodes.inc"
#undef OPCODE
};
extern u8 NUM_ARGS[532];
} // namespace Detail
/// Get return type of an opcode
[[nodiscard]] inline Type TypeOf(Opcode op) noexcept {
return Detail::META_TABLE[static_cast<size_t>(op)].type;
return Detail::META_TABLE[size_t(op)].type;
}
/// Get the number of arguments an opcode accepts
[[nodiscard]] inline size_t NumArgsOf(Opcode op) noexcept {
return static_cast<size_t>(Detail::NUM_ARGS[static_cast<size_t>(op)]);
return size_t(Detail::NUM_ARGS[size_t(op)]);
}
/// Get the required type of an argument of an opcode
[[nodiscard]] inline Type ArgTypeOf(Opcode op, size_t arg_index) noexcept {
return Detail::META_TABLE[static_cast<size_t>(op)].arg_types[arg_index];
return Detail::META_TABLE[size_t(op)].arg_types[arg_index];
}
/// Get the name of an opcode
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -579,6 +582,8 @@ OPCODE(ShuffleIndex, U32, U32,
OPCODE(ShuffleUp, U32, U32, U32, U32, U32, )
OPCODE(ShuffleDown, U32, U32, U32, U32, U32, )
OPCODE(ShuffleButterfly, U32, U32, U32, U32, U32, )
OPCODE(QuadBroadcast, U32, U32, U32, )
OPCODE(QuadSwap, U32, U32, U32, )
OPCODE(FSwizzleAdd, F32, F32, F32, U32, )
OPCODE(DPdxFine, F32, F32, )
OPCODE(DPdyFine, F32, F32, )
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -36,7 +36,10 @@ enum class ShuffleMode : u64 {
}
}
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask) {
constexpr u32 QUAD_MASK = (28u << 8) | 3u;
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask,
bool index_is_imm, u32 index_imm, bool mask_is_imm, u32 mask_imm) {
union {
u64 insn;
BitField<0, 8, IR::Reg> dest_reg;
@@ -45,6 +48,21 @@ void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32
BitField<48, 3, IR::Pred> pred;
} const shfl{insn};
const bool is_quad_candidate{mask_is_imm && mask_imm == QUAD_MASK && index_is_imm &&
v.env.ShaderStage() == Stage::Fragment};
if (is_quad_candidate) {
if (shfl.mode == ShuffleMode::IDX && index_imm <= 3) {
v.X(shfl.dest_reg, v.ir.QuadBroadcast(v.X(shfl.src_reg), v.ir.Imm32(index_imm)));
v.ir.SetPred(shfl.pred, v.ir.Imm1(true));
return;
}
if (shfl.mode == ShuffleMode::BFLY && index_imm >= 1 && index_imm <= 3) {
v.X(shfl.dest_reg, v.ir.QuadSwap(v.X(shfl.src_reg), v.ir.Imm32(index_imm - 1)));
v.ir.SetPred(shfl.pred, v.ir.Imm1(true));
return;
}
}
const IR::U32 result{ShuffleOperation(v.ir, v.X(shfl.src_reg), index, mask, shfl.mode)};
v.ir.SetPred(shfl.pred, v.ir.GetInBoundsFromOp(result));
v.X(shfl.dest_reg, result);
@@ -59,11 +77,14 @@ void TranslatorVisitor::SHFL(u64 insn) {
BitField<29, 1, u64> src_b_flag;
BitField<34, 13, u64> src_b_imm;
} const flags{insn};
const IR::U32 src_a{flags.src_a_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
: GetReg20(insn)};
const IR::U32 src_b{flags.src_b_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
: GetReg39(insn)};
Shuffle(*this, insn, src_a, src_b);
const bool index_is_imm{flags.src_a_flag != 0};
const bool mask_is_imm{flags.src_b_flag != 0};
const IR::U32 src_a{index_is_imm ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
: GetReg20(insn)};
const IR::U32 src_b{mask_is_imm ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
: GetReg39(insn)};
Shuffle(*this, insn, src_a, src_b, index_is_imm, static_cast<u32>(flags.src_a_imm),
mask_is_imm, static_cast<u32>(flags.src_b_imm));
}
} // namespace Shader::Maxwell
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -498,6 +498,10 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::ShuffleButterfly:
info.uses_subgroup_shuffles = true;
break;
case IR::Opcode::QuadBroadcast:
case IR::Opcode::QuadSwap:
info.uses_quad_shuffles = true;
break;
case IR::Opcode::GetCbufU8:
case IR::Opcode::GetCbufS8:
case IR::Opcode::GetCbufU16:
+12 -8
View File
@@ -261,21 +261,25 @@ bool IsTextureInstruction(const IR::Inst& inst) {
static inline u32 ReadCbufCached(Environment& env, u32 index, u32 offset) {
const CbufWordKey k{index, offset};
if (auto it = env.cbuf_word_cache.find(k); it != env.cbuf_word_cache.end()) return it->second;
if (auto it = env.cbuf_word_cache.find(k); it != env.cbuf_word_cache.end())
return it->second;
const u32 v = env.ReadCbufValue(index, offset);
env.cbuf_word_cache.emplace(k, v);
return v;
}
static inline u32 GetTextureHandleCached(Environment& env, const ConstBufferAddr& cbuf) {
const u32 sec_idx = cbuf.has_secondary ? cbuf.secondary_index : cbuf.index;
const u32 sec_off = cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset;
const HandleKey hk{cbuf.index, cbuf.offset, cbuf.shift_left,
sec_idx, sec_off, cbuf.secondary_shift_left, cbuf.has_secondary};
if (auto it = env.handle_cache.find(hk); it != env.handle_cache.end()) return it->second;
// Must all be uniquely different variables
// If has secondary, then it will be cbuf.secondary_{index|offset}, else its 0.
// So we can just hand it out the raw variable without using sec_idx or sec_off
// because comparing 0 against 0 will yield true.
const HandleKey hk{cbuf.index, cbuf.offset, cbuf.shift_left, cbuf.secondary_index, cbuf.secondary_offset, cbuf.secondary_shift_left, cbuf.count, cbuf.has_secondary};
if (auto it = env.handle_cache.find(hk); it != env.handle_cache.end())
return it->second;
const u32 sec_idx = cbuf.has_secondary ? cbuf.secondary_index : cbuf.index;
const u32 sec_off = cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset;
const u32 lhs = ReadCbufCached(env, cbuf.index, cbuf.offset) << cbuf.shift_left;
const u32 rhs = ReadCbufCached(env, sec_idx, sec_off) << cbuf.secondary_shift_left;
const u32 rhs = ReadCbufCached(env, sec_idx, sec_off) << cbuf.secondary_shift_left;
const u32 handle = lhs | rhs;
env.handle_cache.emplace(hk, handle);
return handle;
+2
View File
@@ -29,6 +29,8 @@ struct Profile {
bool support_fp32_signed_zero_nan_preserve{};
bool support_fp64_signed_zero_nan_preserve{};
bool support_explicit_workgroup_layout{};
bool support_shader_quad_control{};
bool support_quad_shuffles{};
bool support_vote{};
bool support_viewport_index_layer_non_geometry{};
bool support_viewport_mask{};
+1
View File
@@ -251,6 +251,7 @@ struct Info {
bool uses_is_helper_invocation{};
bool uses_subgroup_invocation_id{};
bool uses_subgroup_shuffles{};
bool uses_quad_shuffles{};
std::array<bool, 30> uses_patches{};
std::array<Interpolation, 32> interpolation{};
+40 -35
View File
@@ -20,53 +20,58 @@ Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::Nv
Decoder::~Decoder() = default;
void Decoder::SetFrameDimensions(s32 width, s32 height) {
if (width <= 0 || height <= 0) {
frame_dimensions.reset();
return;
}
frame_dimensions = FFmpeg::FrameDimensions{width, height};
}
void Decoder::Decode() {
if (!initialized) {
return;
}
const auto packet_data = ComposeFrame();
FFmpeg::FrameOffsets offsets{};
offsets.hidden = vp9_hidden_frame;
offsets.interlaced = IsInterlaced();
if (offsets.interlaced) {
std::tie(offsets.luma, offsets.luma_bottom, std::ignore, std::ignore) =
GetInterlacedOffsets();
} else {
std::tie(offsets.luma, std::ignore) = GetProgressiveOffsets();
}
if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) {
// Send assembled bitstream to decoder.
if (!decode_api.SendPacket(packet_data)) {
return;
}
auto push = [&](u64 luma, std::shared_ptr<FFmpeg::Frame> frame) {
if (UsingDecodeOrder()) {
host1x.frame_queue.PushDecodeOrder(id, luma, std::move(frame));
} else {
host1x.frame_queue.PushPresentOrder(id, luma, std::move(frame));
}
};
// Only receive/store visible frames.
if (vp9_hidden_frame) {
return;
}
while (auto result = decode_api.ReceiveFrame()) {
auto& [frame, o] = *result;
if (o.hidden || !frame) {
continue;
// Receive output frames from decoder.
auto frame = decode_api.ReceiveFrame();
if (!frame) {
return;
}
if (IsInterlaced()) {
auto [luma_top, luma_bottom, chroma_top, chroma_bottom] = GetInterlacedOffsets();
auto frame_copy = frame;
if (!frame.get()) {
LOG_ERROR(HW_GPU,
"Nvdec {} failed to decode interlaced frame for top {:#X} bottom 0x{:X}", id,
luma_top, luma_bottom);
}
if (o.interlaced) {
auto frame_copy = frame;
push(o.luma, std::move(frame));
push(o.luma_bottom, std::move(frame_copy));
if (UsingDecodeOrder()) {
host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
host1x.frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
} else {
push(o.luma, std::move(frame));
host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
}
} else {
auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
if (!frame.get()) {
LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma {:#X}", id,
luma_offset);
}
if (UsingDecodeOrder()) {
host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
} else {
host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
}
}
}
-8
View File
@@ -10,7 +10,6 @@
#include <mutex>
#include <optional>
#include <string_view>
#include <tuple>
#include <ankerl/unordered_dense.h>
#include <queue>
@@ -47,19 +46,12 @@ protected:
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
virtual bool IsInterlaced() = 0;
void SetFrameDimensions(s32 width, s32 height);
std::optional<FFmpeg::FrameDimensions> GetFrameDimensions() const {
return frame_dimensions;
}
FFmpeg::DecodeApi decode_api;
Host1x::Host1x& host1x;
const Host1x::NvdecCommon::NvdecRegisters& regs;
s32 id;
bool initialized : 1 = false;
bool vp9_hidden_frame : 1 = false;
std::optional<FFmpeg::FrameDimensions> frame_dimensions;
};
} // namespace Tegra
-4
View File
@@ -52,10 +52,6 @@ bool H264::IsInterlaced() {
std::span<const u8> H264::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(H264DecoderContext));
const auto& params = current_context.h264_parameter_set;
SetFrameDimensions(static_cast<s32>(params.pic_width_in_mbs) * 16,
static_cast<s32>(params.frame_height_in_mbs) * 16);
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) {
frame_scratch.resize_destructive(current_context.stream_len);
-1
View File
@@ -35,7 +35,6 @@ std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
std::span<const u8> VP8::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(VP8PictureInfo));
SetFrameDimensions(current_context.frame_width, current_context.frame_height);
const bool is_key_frame = current_context.key_frame == 1u;
const auto bitstream_size = size_t(current_context.vld_buffer_size);
-1
View File
@@ -841,7 +841,6 @@ std::span<const u8> VP9::ComposeFrame() {
{
Vp9FrameContainer curr_frame = GetCurrentFrame();
current_frame_info = curr_frame.info;
SetFrameDimensions(current_frame_info.frame_size.width, current_frame_info.frame_size.height);
bitstream = std::move(curr_frame.bit_stream);
}
// The uncompressed header routine sets PrevProb parameters needed for the compressed header
+37 -191
View File
@@ -4,11 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <string>
#include <string_view>
#include <vector>
#include "common/assert.h"
#include "common/logging.h"
#include "common/scope_exit.h"
@@ -24,16 +19,15 @@ extern "C" {
#endif
#include <libavutil/hwcontext.h>
#include <libavutil/log.h>
}
namespace FFmpeg {
namespace {
constexpr AVPixelFormat PREFERRED_GPU_FORMAT = AV_PIX_FMT_NV12;
constexpr AVPixelFormat PREFERRED_CPU_FORMAT = AV_PIX_FMT_YUV420P;
constexpr std::array PREFERRED_GPU_DECODERS = {
constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
constexpr std::array PreferredGpuDecoders = {
#if defined(_WIN32)
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_D3D11VA,
@@ -60,14 +54,15 @@ AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* p
if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i);
if (config) {
for (const auto type : PREFERRED_GPU_DECODERS)
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
codec_context->pix_fmt = config->pix_fmt;
}
} else {
if (!config) {
break;
}
for (const auto type : PreferredGpuDecoders) {
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
codec_context->pix_fmt = config->pix_fmt;
}
}
}
}
@@ -79,7 +74,7 @@ AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* p
LOG_INFO(HW_GPU, "Could not find supported GPU pixel format, falling back to CPU decoder");
av_buffer_unref(&codec_context->hw_device_ctx);
codec_context->pix_fmt = PREFERRED_CPU_FORMAT;
codec_context->pix_fmt = PreferredCpuFormat;
return codec_context->pix_fmt;
}
@@ -89,57 +84,6 @@ std::string AVError(int errnum) {
return errbuf;
}
#ifdef __ANDROID__
// Match a 3- or 4-byte annex-B NAL start code at `i`. Returns its length, or 0.
size_t FindNalStartCode(std::span<const u8> data, size_t i) {
const size_t n = data.size();
if (i + 3 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 0 && data[i + 3] == 1) {
return 4;
}
if (i + 2 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
return 3;
}
return 0;
}
// Pull SPS (NAL type 7) + PPS (NAL type 8) out of an annex-B frame into an
// extradata buffer, each prefixed with a 4-byte start code. Eden synthesizes
// these inline into the very first frame; h264_mediacodec wants them at open.
std::vector<u8> ExtractH264ParameterSetExtradata(std::span<const u8> packet) {
std::vector<u8> extradata;
const size_t size = packet.size();
size_t i = 0;
while (i < size) {
const size_t sc = FindNalStartCode(packet, i);
if (sc == 0) {
++i;
continue;
}
const size_t nal_start = i + sc;
if (nal_start >= size) {
break;
}
const u8 nal_type = packet[nal_start] & 0x1F;
size_t j = nal_start + 1;
while (j < size && FindNalStartCode(packet, j) == 0) {
++j;
}
if (nal_type == 7 || nal_type == 8) {
constexpr u8 start[4] = {0, 0, 0, 1};
extradata.insert(extradata.end(), start, start + sizeof(start));
extradata.insert(extradata.end(), packet.begin() + nal_start, packet.begin() + j);
} else if (nal_type == 1 || nal_type == 5) {
break;
}
i = j;
}
return extradata;
}
#endif
}
Packet::Packet(std::span<const u8> data) {
@@ -174,26 +118,7 @@ Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
return AV_CODEC_ID_NONE;
}
}();
#ifdef __ANDROID__
// FFmpeg exposes MediaCodec via dedicated decoders rather than as a
// hw_config on the regular ones.
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
const char* mc_name = nullptr;
switch (av_codec) {
case AV_CODEC_ID_H264: mc_name = "h264_mediacodec"; break;
case AV_CODEC_ID_VP8: mc_name = "vp8_mediacodec"; break;
case AV_CODEC_ID_VP9: mc_name = "vp9_mediacodec"; break;
default: break;
}
if (mc_name) {
m_codec = avcodec_find_decoder_by_name(mc_name);
}
}
#endif
if (!m_codec) {
m_codec = avcodec_find_decoder(av_codec);
}
m_codec = avcodec_find_decoder(av_codec);
}
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
@@ -217,10 +142,13 @@ bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceTyp
std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() {
std::vector<AVHWDeviceType> types;
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
while (true) {
current_device_type = av_hwdevice_iterate_types(current_device_type);
if (current_device_type == AV_HWDEVICE_TYPE_NONE)
if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
return types;
}
types.push_back(current_device_type);
}
}
@@ -230,20 +158,25 @@ HardwareContext::~HardwareContext() {
}
bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder) {
auto const supported_types = GetSupportedDeviceTypes();
for (auto const type : PREFERRED_GPU_DECODERS) {
const auto supported_types = GetSupportedDeviceTypes();
for (const auto type : PreferredGpuDecoders) {
AVPixelFormat hw_pix_fmt;
if (std::ranges::find(supported_types, type) == supported_types.end()) {
LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
continue;
}
if (InitializeWithType(type)) {
AVPixelFormat hw_pix_fmt{};
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
return true;
}
if (!this->InitializeWithType(type)) {
continue;
}
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
return true;
}
}
return false;
}
@@ -251,7 +184,7 @@ bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
av_buffer_unref(&m_gpu_decoder);
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); ret < 0) {
LOG_INFO(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret));
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret));
return false;
}
@@ -281,9 +214,6 @@ DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
m_codec_context->thread_count = 0;
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
// Forwarded into MediaCodec as KEY_LOW_LATENCY on Android.
m_codec_context->flags |= AV_CODEC_FLAG_LOW_DELAY;
m_codec_context->flags2 |= AV_CODEC_FLAG2_FAST;
}
DecoderContext::~DecoderContext() {
@@ -297,19 +227,7 @@ void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, A
m_codec_context->pix_fmt = hw_pix_fmt;
}
bool DecoderContext::OpenContext(const Decoder& decoder, std::span<const u8> extradata) {
if (!extradata.empty()) {
av_freep(&m_codec_context->extradata);
m_codec_context->extradata = static_cast<u8*>(
av_mallocz(extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE));
if (!m_codec_context->extradata) {
LOG_ERROR(HW_GPU, "Failed to allocate extradata");
return false;
}
std::memcpy(m_codec_context->extradata, extradata.data(), extradata.size());
m_codec_context->extradata_size = static_cast<int>(extradata.size());
}
bool DecoderContext::OpenContext(const Decoder& decoder) {
if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
return false;
@@ -347,17 +265,11 @@ std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
m_final_frame = std::make_shared<Frame>();
if (m_codec_context->hw_device_ctx) {
#ifdef __ANDROID__
// c2.mtk.vp9.decoder, c2.mtk.vp89.decoder will be fine if we don't
// re-encode stuff twice :>
m_final_frame = std::move(intermediate_frame);
#else
m_final_frame->SetFormat(PREFERRED_GPU_FORMAT);
m_final_frame->SetFormat(PreferredGpuFormat);
if (const int ret = av_hwframe_transfer_data(m_final_frame->GetFrame(), intermediate_frame->GetFrame(), 0); ret < 0) {
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
return {};
}
#endif
} else {
m_final_frame = std::move(intermediate_frame);
}
@@ -369,18 +281,9 @@ void DecodeApi::Reset() {
m_hardware_context.reset();
m_decoder_context.reset();
m_decoder.reset();
m_opened = false;
m_defer_android_mediacodec_open = false;
m_needs_h264_extradata = false;
m_next_pts = 0;
while (!m_pending_offsets.empty()) {
m_pending_offsets.pop();
}
}
bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
av_log_set_level(AV_LOG_DEBUG);
this->Reset();
m_decoder.emplace(codec);
m_decoder_context.emplace(*m_decoder);
@@ -391,80 +294,23 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
}
#ifdef __ANDROID__
const std::string_view decoder_name = m_decoder->GetCodec() ? m_decoder->GetCodec()->name : "";
// MediaCodec decoders need the frame dimensions before avcodec_open2().
m_defer_android_mediacodec_open = decoder_name == "h264_mediacodec" ||
decoder_name == "vp8_mediacodec" ||
decoder_name == "vp9_mediacodec";
// h264_mediacodec also needs SPS/PPS in extradata at open. We pull them
// from the first frame's bitstream in SendPacket.
m_needs_h264_extradata = decoder_name == "h264_mediacodec";
if (m_defer_android_mediacodec_open) {
return true;
}
#endif
// Open the decoder context.
if (!m_decoder_context->OpenContext(*m_decoder)) {
this->Reset();
return false;
}
m_opened = true;
return true;
}
bool DecodeApi::SendPacket(std::span<const u8> packet_data, const FrameOffsets& offsets,
std::optional<FrameDimensions> dimensions) {
if (!m_opened) {
std::vector<u8> extradata;
#ifdef __ANDROID__
if (m_defer_android_mediacodec_open) {
if (!dimensions) {
return true;
}
auto* ctx = m_decoder_context->GetCodecContext();
ctx->width = dimensions->width;
ctx->height = dimensions->height;
ctx->coded_width = dimensions->width;
ctx->coded_height = dimensions->height;
}
if (m_needs_h264_extradata) {
extradata = ExtractH264ParameterSetExtradata(packet_data);
if (extradata.empty()) {
return true;
}
}
#endif
if (!m_decoder_context->OpenContext(*m_decoder, extradata)) {
this->Reset();
return false;
}
m_opened = true;
}
m_pending_offsets.push(offsets);
bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
FFmpeg::Packet packet(packet_data);
packet.GetPacket()->pts = m_next_pts;
packet.GetPacket()->dts = m_next_pts;
++m_next_pts;
return m_decoder_context->SendPacket(packet);
}
std::optional<DecodeApi::DecodedFrame> DecodeApi::ReceiveFrame() {
auto frame = m_decoder_context->ReceiveFrame();
if (!frame) {
return std::nullopt;
}
FrameOffsets offsets{};
if (!m_pending_offsets.empty()) {
offsets = m_pending_offsets.front();
m_pending_offsets.pop();
}
return DecodedFrame{std::move(frame), offsets};
std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
// Receive raw frame from decoder.
return m_decoder_context->ReceiveFrame();
}
}
+3 -26
View File
@@ -179,7 +179,7 @@ public:
~DecoderContext();
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
bool OpenContext(const Decoder& decoder, std::span<const u8> extradata = {});
bool OpenContext(const Decoder& decoder);
bool SendPacket(const Packet& packet);
std::shared_ptr<Frame> ReceiveFrame();
@@ -198,18 +198,6 @@ private:
bool m_decode_order{};
};
struct FrameOffsets {
bool interlaced{};
bool hidden{};
u64 luma{};
u64 luma_bottom{};
};
struct FrameDimensions {
s32 width{};
s32 height{};
};
class DecodeApi {
public:
YUZU_NON_COPYABLE(DecodeApi);
@@ -225,24 +213,13 @@ public:
return m_decoder_context->UsingDecodeOrder();
}
bool SendPacket(std::span<const u8> packet_data, const FrameOffsets& offsets,
std::optional<FrameDimensions> dimensions = std::nullopt);
struct DecodedFrame {
std::shared_ptr<Frame> frame;
FrameOffsets offsets;
};
std::optional<DecodedFrame> ReceiveFrame();
bool SendPacket(std::span<const u8> packet_data);
std::shared_ptr<Frame> ReceiveFrame();
private:
std::optional<FFmpeg::Decoder> m_decoder;
std::optional<FFmpeg::DecoderContext> m_decoder_context;
std::optional<FFmpeg::HardwareContext> m_hardware_context;
bool m_opened{};
bool m_defer_android_mediacodec_open{};
bool m_needs_h264_extradata{};
s64 m_next_pts{};
std::queue<FrameOffsets> m_pending_offsets;
};
} // namespace FFmpeg
-1
View File
@@ -31,7 +31,6 @@ Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt)
Nvdec::~Nvdec() {
LOG_INFO(HW_GPU, "Destroying nvdec {}", id);
host1x.frame_queue.Close(id);
}
void Nvdec::ProcessMethod(u32 method, u32 argument) {
+1 -8
View File
@@ -118,7 +118,6 @@ void Vic::Execute() noexcept {
output_surface.resize(output_width * output_height);
if (Settings::values.nvdec_emulation.GetValue() != Settings::NvdecEmulation::Off) {
bool decoded_frame = false;
for (size_t i = 0; i < config.slot_structs.size(); i++) {
if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) {
auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address();
@@ -137,17 +136,11 @@ void Vic::Execute() noexcept {
break;
}
Blend(config, slot_config, config.output_surface_config.out_pixel_format);
decoded_frame = true;
} else {
LOG_TRACE(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
LOG_ERROR(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
}
}
}
if (decoded_frame) {
has_decoded_frame = true;
} else if (!has_decoded_frame) {
return;
}
} else {
// Fill the frame with black, as otherwise they can have random data and be very glitchy.
std::fill(output_surface.begin(), output_surface.end(), Pixel{});
-1
View File
@@ -628,7 +628,6 @@ private:
s32 id;
s32 nvdec_id{-1};
bool has_decoded_frame{};
u32 syncpoint;
};
@@ -56,6 +56,9 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
const Maxwell& regs = maxwell3d.regs;
const auto topology_ = maxwell3d.draw_manager.draw_state.topology;
driver_id = features.driver_id;
driver_version = features.driver_version;
raw1 = 0;
extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
@@ -21,6 +21,8 @@ namespace Vulkan {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
struct DynamicFeatures {
u32 driver_id;
u32 driver_version;
bool has_extended_dynamic_state;
bool has_extended_dynamic_state_2;
bool has_extended_dynamic_state_2_logic_op;
@@ -228,6 +230,8 @@ struct FixedPipelineState {
};
std::array<u8, Maxwell::NumRenderTargets> color_formats;
u32 driver_id;
u32 driver_version;
u32 alpha_test_ref;
u32 point_size;
@@ -37,6 +37,8 @@
namespace Vulkan {
namespace {
using boost::container::small_vector;
using boost::container::static_vector;
using Shader::ImageBufferDescriptor;
using Shader::Backend::SPIRV::RENDERAREA_LAYOUT_OFFSET;
using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET;
@@ -583,9 +585,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
} else {
dynamic.raw1 = key.state.dynamic_state.raw1;
}
boost::container::static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
boost::container::static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
boost::container::static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
if (!key.state.dynamic_vertex_input) {
const size_t num_vertex_arrays = (std::min)(
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
@@ -810,7 +812,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (dynamic.depth_bounds_enable && !device.IsDepthBoundsSupported()) {
LOG_WARNING(Render_Vulkan, "Depth bounds is enabled but not supported");
}
boost::container::static_vector<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
static_vector<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
const size_t num_attachments{NumAttachments(key.state)};
for (size_t index = 0; index < num_attachments; ++index) {
static constexpr std::array mask_table{
@@ -846,7 +848,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pAttachments = cb_attachments.data(),
.blendConstants = {}
};
boost::container::static_vector<VkDynamicState, 34> dynamic_states{
static_vector<VkDynamicState, 34> dynamic_states{
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
@@ -941,9 +943,12 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pNext = nullptr,
.requiredSubgroupSize = GuestWarpSize,
};
boost::container::static_vector<VkPipelineShaderStageCreateInfo, 5> shader_stages;
static_vector<VkPipelineShaderStageCreateInfo, 5> shader_stages;
for (size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) {
if (spv_modules[stage]) {
if (!spv_modules[stage]) {
continue;
}
[[maybe_unused]] auto& stage_ci =
shader_stages.emplace_back(VkPipelineShaderStageCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = nullptr,
@@ -953,7 +958,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pName = "main",
.pSpecializationInfo = nullptr,
});
}
}
VkPipelineCreateFlags flags{};
if (device.IsKhrPipelineExecutablePropertiesEnabled() && Settings::values.renderer_debug.GetValue()) {
@@ -62,7 +62,7 @@ using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
constexpr u32 CACHE_VERSION = 17;
constexpr u32 CACHE_VERSION = 18;
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
template <typename Container>
@@ -402,6 +402,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_fp64_signed_zero_nan_preserve =
float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
.support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
.support_shader_quad_control = device.IsKhrShaderQuadControlSupported(),
.support_quad_shuffles = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_QUAD_BIT),
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
.support_viewport_index_layer_non_geometry =
device.IsExtShaderViewportIndexLayerSupported(),
@@ -479,6 +481,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
LOG_INFO(Render_Vulkan, "DynamicState setting value: {}", u32(Settings::values.dyna_state.GetValue()));
dynamic_features = {};
dynamic_features.driver_id = device.GetDriverID();
dynamic_features.driver_version = device.GetDriverVersion();
// User granularity enforced in vulkan_device.cpp switch statement:
// Level 0: Core Dynamic States only
@@ -1330,6 +1330,11 @@ void Device::RemoveUnsuitableExtensions() {
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
}
// VK_KHR_shader_quad_control
extensions.shader_quad_control = features.shader_quad_control.shaderQuadControl;
RemoveExtensionFeatureIfUnsuitable(extensions.shader_quad_control, features.shader_quad_control,
VK_KHR_SHADER_QUAD_CONTROL_EXTENSION_NAME);
// VK_KHR_workgroup_memory_explicit_layout
extensions.workgroup_memory_explicit_layout =
features.features.shaderInt16 &&
@@ -1518,7 +1523,7 @@ std::vector<VkDeviceQueueCreateInfo> Device::GetDeviceQueueCreateInfos() const {
}
void Device::InitializeGPULogging() {
// Get log level from settings Off is the disable.
// Get log level from settings - Off is the disable.
const auto log_level = static_cast<GPU::Logging::LogLevel>(
static_cast<u32>(Settings::values.gpu_log_level.GetValue()));
if (log_level == GPU::Logging::LogLevel::Off) {
@@ -68,6 +68,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
pipeline_executable_properties) \
FEATURE(KHR, ShaderQuadControl, SHADER_QUAD_CONTROL, shader_quad_control) \
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
workgroup_memory_explicit_layout)
@@ -483,6 +484,11 @@ FN_MAX_LIMIT_LIST
return extensions.workgroup_memory_explicit_layout;
}
/// Returns true if the device supports VK_KHR_shader_quad_control.
bool IsKhrShaderQuadControlSupported() const {
return extensions.shader_quad_control && features.shader_quad_control.shaderQuadControl;
}
/// Returns true if the device supports VK_KHR_image_format_list.
bool IsKhrImageFormatListSupported() const {
return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2;
@@ -1612,7 +1612,7 @@ void PlayerControlPreview::DrawGCBody(QPainter& p, const QPointF center) {
std::array<QPointF, gc_body.size()> qbody;
std::array<QPointF, 8> left_hex;
std::array<QPointF, 8> right_hex;
constexpr float angle = 2 * 3.1415f / 8;
constexpr float angle = 2.f * float(M_PI) / 8.f;
for (std::size_t point = 0; point < gc_left_body.size() / 2; ++point) {
const float body_x = gc_left_body[point * 2 + 0];