Compare commits

..

164 Commits

Author SHA1 Message Date
a3596ba858 Reset in-memory JIT cache on game quit + fix Purge PPTC (#709)
Jit cache now fully resets when booting a game multiple times.
This should fix random jit cache crashes.
Also removed some redundant code related to region allocation and fixed
PPTC Purge not fully purging all PPTC files in the backup folder.
2025-02-25 15:34:21 -06:00
3ffcc72117 UI: Fix compatibility list crashing (missing font) 2025-02-22 23:48:47 -06:00
fe1617ffea Revert the Metal Experiment (#701)
Metal sounded like a good idea to get in the emulator but frankly I
underestimated just how experimental and not ready it was.
From my write up in the Discord:
```
As is, Metal supports only a few games.
The games it does support freeze on first use of not playing them via Vulkan, because shader translation is broken.
So you need to use a dirty hack to not delete all your shaders.
Not to mention it breaks many games via MoltenVK because of changes to the shared GPU code.

Merging Metal seemed like a great idea, because of the few games it does support.
But I don't think it's worth it. Many of the games it breaks via MoltenVK *don't work via Metal*. 
Which effectively makes current Ryubing worse for Mac users than Ryujinx 1.1.1403.

I think what I'm gonna do is revert Metal, and reopen it as a PR. That way, you can still take advantage of the Metal backend as is, but without making other games worse with no solution.
```

For what it's worth, the shader translation part could at least be
"fixed" by always applying a 30ms delay for shader translation to Metal.
That being said, that solution sucks ass.
The MoltenVK regressions are even worse.



I hope this is not a let down to the Mac users. I hope you realize I'm
reverting this because you're actively getting a worse experience with
it in the emulator.
2025-02-22 21:26:46 -06:00
eb6b0e9adc Updated Zh-CN Simplified Chinese. (#703) 2025-02-22 02:16:28 -06:00
9631bdfe16 docs: compat: Hogwarts Legacy is an Unreal Engine 4 game 2025-02-20 20:56:52 -06:00
2a84656ffc misc: chore: use new array in LdnGameDataReceivedEventArgs instead of collecting the ldn datas into a list 2025-02-20 19:49:17 -06:00
6c6580ddcc misc: chore: Move the LDN constants into a SharedConstants class 2025-02-20 19:30:00 -06:00
c47448628c UI: Print LED setting failed on Debug 2025-02-20 19:29:18 -06:00
d0ac83b493 misc: chore: Prevent firmware installation prompt from showing up multiple times during runtime when using --install-firmware 2025-02-20 18:52:45 -06:00
e0ddbe55c0 Ukrainian localization changes (1.2.82) (#678)
Some changes and new lines for Ukrainian (uk_UA) localization.
2025-02-20 18:11:56 -06:00
4a4078865f Add Melatonin to compatibility list (#667)
It's playable, no bugs, and can run pretty easily way above intended
FPS, which shows there's not really any performance issues lol.


![image](https://github.com/user-attachments/assets/63402c9f-2412-4b43-9e5d-42a19436ac55)
2025-02-20 16:09:14 -06:00
3f59bade94 Update Russian Translation (#695) 2025-02-20 14:32:35 -06:00
c2ed0fd5fd UI: --install-firmware startup flag.
Has the normal UI flow, this is just for systems where the file picker doesn't show up.
2025-02-19 23:07:50 -06:00
de16d8fa3e UI: Remove the ability to select Swedish & Norwegian for System language
Switch doesn't natively support these languages, they should only be UI language.
2025-02-19 18:24:24 -06:00
9b1fb3a27b misc: chore: missed a collection expression usage in avalonia project 2025-02-18 21:35:30 -06:00
b9150a0092 misc: chore: use target-typed new in collection initializers where type is evident 2025-02-18 21:35:11 -06:00
c1002d4826 misc: chore: collection expressions & target typed new in ARMeilleure & Ryujinx.Cpu 2025-02-18 21:34:09 -06:00
b1de7696ee misc: chore: VP9 project cleanup
Target-typed new, remove var usage, use collection expressions, rename many fields & properties to match C# standard
2025-02-18 21:33:07 -06:00
f91cd05260 Implement VP9 loop filtering (#550)
Unmerged PR from OG Ryujinx (#4367). From @gdkchan:

> The main goal of this change is porting the loop filtering from
libvpx, which should fix the block artifacts on some VP9 videos on games
using NVDEC to decode them. In addition to that, there are two other
changes:
> 
> - The remaining decoder code required to decode a VP9 video (with
headers included) has been added. That was done because it's much better
to test the decoder standalone with a video file. I decided to keep that
code on the emulator, even if some of it is unused, since it makes
standalone testing easier in the future too, and we can include unit
tests with video files.
> - Large refactoring of both new and existing code to conform with our
conding [sic] styles, done by @TSRBerry (thanks!) Some of it has been
automated.
> 
> Since we had no loop filtering before, this change will make video
decoding slower. That may cause frame drop etc if the decoder is not
fast enough in some games. I plan to optimize the decoder more in the
future to make up for that, but if possible I'd prefer to not do it as
part of this PR, but if the perf loss is too severe I might consider.
> 
> This will need to be tested on games that had the block artifacts, it
would be nice to confirm if they match hardware now, and get some
before/after screenshots etc.

Comment from @Bjorn29512:

> Significantly improves the block artifacts in FE: Engage.
> 
> Before:
>
![](https://user-images.githubusercontent.com/110204265/216882414-ec88dbda-7544-4490-8a47-37f074056ae3.png)
> 
> After:
>
![](https://user-images.githubusercontent.com/110204265/216882478-4e81fead-1033-4877-b282-f9cac6d6aa3b.png)

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2025-02-18 20:59:36 -06:00
920933bc9f Small PT-BR update (#688) 2025-02-18 17:29:58 -06:00
52b0b45d34 misc: chore: null-coalesce led rainbow checking in headless 2025-02-17 16:29:57 -06:00
12f0dbcc70 metal: Commented Bayonetta & New Pokemon Snap for Metal for more testing, removed Astral Chain 2025-02-17 13:43:28 -06:00
719be560ec UI: RPC: Hogwarts Legacy asset image 2025-02-17 02:04:18 -06:00
18238736be Early exit when outdated Windows detected 2025-02-16 23:55:36 -06:00
5d9e4ad7a4 Add Super Mario Party Jamboree to compatibility list (#673) 2025-02-16 23:22:36 -06:00
adba775f0c docs: compat: FINAL FANTASY is now Playable 2025-02-16 21:27:47 -06:00
2ffaeb2803 HLE: LDN: Reduce NAT timeout from 5 seconds to 2.5 2025-02-16 17:48:19 -06:00
b16b844760 Update PT-BR Big Translation & Add DKC Returns HD to compatibility list (#666)
Today I had a free day to look at and work on the entire Brazilian
Portuguese language.

Translations that had not been done before have been added
Fixed several translations that didn't make sense
Translated description of functions that were not translated correctly
Words were written as close to the original English
Add Donkey Kong Country Returns HD to compatibility list
2025-02-16 15:39:42 -06:00
bc07bc482d Gracefully handle errors in socket creation (#662)
In all other calls, exceptions are handled within the ManagedSocket
class, this can't easily be done here as the exception is thrown from
the constructor, so the exception is handled in ISocket and the correct
error is passed to the application.

This should fix #660
2025-02-16 02:26:37 -06:00
61975ca44d Add logging in socket implementation (#661)
Adds logging to most Socket calls to help with debugging network
issues.

Shouldn't affect any functionality. There's a small chance it could spam
the log in some games.
2025-02-16 01:03:44 -06:00
66054dd225 UI: RPC: Remove git hash from RPC ryujinx logo hover information 2025-02-16 00:58:25 -06:00
b1f61e5143 misc: chore: [ci skip] Reformat PlayReports.cs 2025-02-15 20:52:03 -06:00
0d7d0e8092 UI: Add descriptions of what each dynamic RPC formatter actually shows when hovering whether it has support in the game info popup 2025-02-15 20:45:27 -06:00
aa2178dbe5 UI: Button to open screenshots folder in File menu 2025-02-15 20:25:17 -06:00
f92d09711b misc: chore: rewrite PlayReports.Analyzer creation to use Lazy and create the value alongside DiscordIntegrationModule init 2025-02-15 19:02:53 -06:00
45ee8cd0e8 misc: Play Report Analyzer: Skyward Sword HD rupee count 2025-02-15 19:02:04 -06:00
395bbd144a misc: chore: Change Analyzer AddSpec logic to log the non-hexadecimal value and ignore the added entry instead of throwing an exception 2025-02-15 19:01:24 -06:00
744d813b87 misc: i18n: change localization of Ignore Applet to Ignore Controller Applet & redo tooltip 2025-02-15 00:40:18 -06:00
7d59ada798 misc: chore: rename IgnoreApplet to IgnoreControllerApplet 2025-02-15 00:25:28 -06:00
a4b5304935 UI: Refresh game list when emulated Switch language is changed (to show different logos/names) 2025-02-15 00:20:01 -06:00
0965ee905d misc: Fix Match System Time not persisting between emulator launches 2025-02-14 22:18:15 -06:00
855161b23b Prevent log from showing negative JIT cache sizes (32bit-int overflow) (#664)
![image](https://github.com/user-attachments/assets/5820ce7b-cbfe-4908-8f5e-7ee82040ee1a)
2025-02-14 21:37:19 -06:00
6b55d158b7 Norwegian locale update & update Jackbox 5 & 6 compat (#658) 2025-02-13 22:38:55 -06:00
91f73a4891 Updated Swedish translation (#594) 2025-02-13 19:28:39 -06:00
883d4d863a i18n: chore: [ci skip] missing closing single quote in translation (not JSON breaking) 2025-02-13 19:16:05 -06:00
ca5de909a1 Updated German translation (#626) 2025-02-13 19:14:02 -06:00
5172567b08 Update Brazilian Translation PT-BR (#633)
I updated the Brazilian Portuguese translation, I added the texts that
were not translated.
2025-02-13 19:08:48 -06:00
6fe4cee7c0 Update zh_CN translation (#650) 2025-02-13 18:16:12 -06:00
8623452abc docs: new discord invite 2025-02-13 03:23:02 -06:00
17e8ae1d9a UI: More advanced customization for what happens when Ryujinx loses focus 2025-02-13 01:57:08 -06:00
7591b07fce infra: Update workflows to ubuntu 24.04 (20.04 will be deprecated soon) 2025-02-12 16:21:03 -06:00
89b4389ed2 docs: compat: [ci skip] forgot to update time for FUSER 2025-02-12 16:03:26 -06:00
d9ee729199 docs: compat: FUSER is now Ingame 2025-02-12 15:21:19 -06:00
ba0cd13cff misc: chore: fix warning [ci skip] 2025-02-11 22:54:24 -06:00
501b199e24 UI: setting: Disable Input when Out of Focus 2025-02-11 22:12:05 -06:00
8aecccadb8 misc: chore: Format AvaHostUIHandler 2025-02-11 21:23:04 -06:00
e23d610f49 UI: Change Update Available button text color to white 2025-02-11 21:18:05 -06:00
f6822f7358 misc: chore: add direct error code tuple to DisplayErrorAppletDialog
for use when i find the list of error codes -> causes
2025-02-11 21:09:55 -06:00
d3f84a1305 misc: chore: Rename UserSelectorDialog to ProfileSelectorDialog 2025-02-11 20:50:06 -06:00
06d34a5992 misc: chore: optimize UserSelectorDialog closed handler 2025-02-11 20:36:11 -06:00
e8e1dc6619 misc: chore: this isn't even bound to a XAML element, why was this an async void 2025-02-11 20:30:46 -06:00
c5603d4c36 UI: Make about window text bigger and add a separator in the right side 2025-02-11 20:22:29 -06:00
05b56730d6 docs: compat: remove title ID column from compat entries for homebrew 2025-02-11 20:22:09 -06:00
43f7b000ca misc: chore: Optimize AboutWindowViewModel resource disposal 2025-02-11 20:08:38 -06:00
ad89cf39b6 UI: Change the Discord invite to link to the rules channel
When next stable comes out the old one will be invalidated
2025-02-11 18:21:12 -06:00
96c33a0b92 misc: chore: [ci skip] AppletMetadata: Use previously retrieved content path 2025-02-11 12:20:52 -06:00
e0db55df46 UI: When starting games in fullscreen, change ExtendsContentIntoTitleBar to true. 2025-02-11 12:04:34 -06:00
30fef8e96e misc: chore: Use UpdateCommand instance for the normal Check for Updates button 2025-02-11 00:47:39 -06:00
9cb5f5689b UI: Show Update Available button in the system accent color 2025-02-11 00:21:58 -06:00
a205ec374b UI: More advanced IsVisible binding for update available button (idk why it's always showing) 2025-02-10 22:42:57 -06:00
aab9b58542 compat: "Rustler": Playable (#649) 2025-02-10 22:32:39 -06:00
daa648dc40 UI: Correct visibility for new background update button 2025-02-10 22:25:04 -06:00
1024aa8757 UI: Change the background updater notification to a persistent button on the status bar when not in a game 2025-02-10 22:13:58 -06:00
13388e972a UI: RPC: add image asset for Bluey: The Video Game 2025-02-10 20:00:59 -06:00
1eb78872d8 misc: chore: annoyed I missed this, it's very obvious 2025-02-10 19:51:28 -06:00
fe9fe2a10f UI: Added option to check for updates in the background 2025-02-10 19:28:46 -06:00
6ab899f621 misc: chore: [ci skip] Use explicit types & target-typed new 2025-02-10 16:44:26 -06:00
faacec9801 JIT Cache Regions + HLE SoNoSigpipe BSD socket mapping (#615)
Instead of one big 2048MB JIT Cache that'd crash the emulator when maxed
out, we now have it where we add 256MB JIT Cache regions when needed,
helping reduce allocated memory where games don't use the JIT cache for
it, and helping bigger games that DO need JIT cache bigger than 2048MB!

![image](https://github.com/user-attachments/assets/ff17dc48-6028-4377-8c73-746ab21ab83b)
(SSBU goes past the 2048MB JIT Cache limit that would normally crash
Ryujinx ^)

Also I added a BSD socket that Baba is You's networking for downloading
custom levels uses.
2025-02-10 15:53:44 -06:00
55fdb3f6b2 headless: Default to Vulkan 2025-02-10 15:45:09 -06:00
1129ab0e8c misc: chore: Remove unused property in ApplicationData 2025-02-10 15:44:58 -06:00
b6b391b2cf misc: chore: [ci skip] Remove unused 'using' directives from solution 2025-02-10 14:34:12 -06:00
f3cf03495d misc: add the ability to ignore UI logs when using trace & debug log levels 2025-02-10 14:25:47 -06:00
7bce8206d5 misc: chore: small cleanups 2025-02-10 13:59:25 -06:00
efa0cc7554 UI: Show issue labels of games in the tooltip on playability status 2025-02-09 21:15:38 -06:00
1c0813d09d misc: chore: [ci skip] shorten lines in FormatterSpecBase.Format & consistently format them 2025-02-09 00:50:11 -06:00
8bec09d7ff Merge remote-tracking branch 'origin/master' 2025-02-09 00:45:54 -06:00
e4b4e94b56 misc: chore: cleanup Specs.cs 2025-02-09 00:45:46 -06:00
764c9e9d4e misc: chore: unify GameSpec creation 2025-02-09 00:37:54 -06:00
05e991db87 misc: chore: collapse all NSO emulator formatters into one method 2025-02-09 00:37:35 -06:00
2cd876b1cb Update Zh-CN Simplified Chinese (#642) 2025-02-08 21:33:20 -06:00
93a298523f Dynamic Presence support for every NSO emulator 2025-02-08 16:52:07 -06:00
253cbb2810 Initial Implementation of SSBU PlayReport usage! (#638)
Currently, this has as many game modes as I could find to implement,
along with a list of all the characters in the game and their code ID.
2025-02-08 16:23:24 -06:00
9c226dcc7a misc: chore: [ci skip] rename ValueFormatter to SingleValueFormatter and some minor cleanups 2025-02-08 01:34:44 -06:00
30a534edcd misc: chore: [ci skip] generify Formatter Specs to be able to run formatters of different types at interleaving priorities 2025-02-08 01:26:23 -06:00
1d88771d1b Play Report Analyzer v4
You can now access the *entire* play report data in any given value formatter.
The input types have been restructured and, notably, not every instance of Value has an ApplicationMetadata on it. It's now on the container type that also contains the matched values and the entire play report.
2025-02-08 00:22:34 -06:00
4e8157688e UI: See what games do/don't have an image & dynamic RPC support in the Game Info popup 2025-02-07 18:34:11 -06:00
5085af0050 UI: Changed the color of "Ingame" from yellow to orange to stand out better in light mode 2025-02-07 18:28:32 -06:00
2c8edaf89e PlayReport: Add Sparse Multi Value formatters 2025-02-07 15:43:50 -06:00
aa8ba8b503 Merge remote-tracking branch 'origin/master' 2025-02-06 22:56:33 -06:00
a4211fec33 UI: Properly space the play time & last play date in the game info popup 2025-02-06 22:56:25 -06:00
54b233dd78 Updated the compat list. (#618) 2025-02-06 04:46:23 -06:00
d1da937fce misc: chore: [ci skip] XMLdocs on new Play Report Analyzer members 2025-02-05 19:51:43 -06:00
4a8f98126f [ci skip] remove test 2025-02-05 19:45:29 -06:00
e55629a908 misc: chore: [ci skip] Play Report Analyzer: Added Multi Value formatters 2025-02-05 19:42:36 -06:00
c638a7daf8 misc: chore: Move Play Report analyzer into a dedicated namespace and remove the PlayReport name prefix on types 2025-02-05 19:27:44 -06:00
5e5e180fea PlayReportAnalyzer: Added Pokemon Scarlet and Violet (#630)
Every base game location excluding buildings are done, DLC locations
will be added at a later point
2025-02-05 18:32:27 -06:00
131fe71205 Update Korean translation (#624) 2025-02-05 02:40:37 -06:00
6af388c623 misc: chore: [ci skip] oops forgot to localize the reset button & confirmation 2025-02-05 02:01:33 -06:00
45cec4e7cf UI: In-app Configuration resetting 2025-02-05 01:42:27 -06:00
479b38f035 Add tooltips to game status (#625) 2025-02-05 00:42:20 -06:00
3ecc7819cc UI: Fix the app list sort types using the newly changed localization keys 2025-02-04 23:47:24 -06:00
4b1d94ccd8 misc: chore: [ci skip] use MultiplayerInfoConverter instance instead of constructing for every use 2025-02-04 23:36:36 -06:00
4ae9f1c0d2 UI: Use Hosted Games & Player Count localization keys in list view too 2025-02-04 23:31:31 -06:00
717851985e UI: Reorganize Game Info dialog popup + localization 2025-02-04 23:28:37 -06:00
bd08a111a8 UI: Show what each value is in the Game Info dialog, add game icon 2025-02-04 22:47:12 -06:00
1972a47f39 UI: Game stats button on right click for Grid view users 2025-02-04 19:32:17 -06:00
222ceb818b misc: chore: Use ApplicationLibrary helpers for getting DLCs & Updates for a game 2025-02-04 18:21:49 -06:00
b0fcc5bee1 misc: chore: Simplify HasCompatibilityEntry
(Totally didn't realize that SelectedApplication is already an ApplicationData)
2025-02-04 18:21:24 -06:00
820e8f7375 [ci skip] UI: Strip dumped file information out of the DLC name 2025-02-04 18:10:28 -06:00
e8a7d5b0b7 UI: Only show DLC RomFS button under Extract Data when DLCs are available.
Also convert the constructor of DlcSelectViewModel to expect a normal title id and not one already converted to the base ID.
2025-02-04 17:21:54 -06:00
fafb99c702 misc: chore: [ci skip] don't even bother looking up the application; the tag present on the control *is* a valid title ID and can't reasonably change in between the tag being set and playability information being requested.
Even if it does, worst case scenario the compat list that pops up has no results.
2025-02-04 15:57:32 -06:00
df9e6e4812 UI: Added the ability to view Compat information on right click, and on clicking the status itself like the title ID button. 2025-02-04 15:51:27 -06:00
566f3d079a misc: chore: Play Report analyzer code simplification 2025-02-04 00:56:59 -06:00
d7707d4176 UI: RPC: Only update presence if a value is actually different from the current presence 2025-02-03 23:12:50 -06:00
7a9b62884a misc: chore: type-specific value accessors on PlayReportValue 2025-02-03 19:56:02 -06:00
de9faf183a misc: chore: [ci skip] wrong element order 2025-02-03 19:45:05 -06:00
0bf7c5dfa2 misc: chore: [ci skip] AlwaysReturn factory function to go with the AlwaysResets singleton one. 2025-02-03 19:23:47 -06:00
11bc32d98e UI: RPC: Reset Details when switching between Master Mode and Normal Mode on the title screen. 2025-02-03 19:19:17 -06:00
063430ea16 misc: chore: Use .Match 2025-02-03 19:18:31 -06:00
65f08caaa3 misc: chore: .Match helper method on PlayReportAnalyzer.FormattedValue. 2025-02-03 19:18:17 -06:00
f225b18c05 misc: chore: XMLDocs on PlayReportAnalyzer system.
- Change PlayReportValue to a basic class passed normally instead of a struct passed by reference
2025-02-03 18:54:38 -06:00
d8549f687b misc: chore: convert split ThreadStatic fields & property getter/setters into a single property with [field: ThreadStatic] 2025-02-03 17:04:11 -06:00
5ab50680b4 HLE: Run Play Report event handlers in a dedicated .NET thread 2025-02-03 17:01:44 -06:00
a0edc5c2b0 UI: RPC: Small change to how values are passed to hopefully detect the player pos better 2025-02-03 15:46:39 -06:00
158ea7b4d6 misc: chore: logged coordinate is a decimal not a whole number 2025-02-03 15:33:12 -06:00
8bc3de8303 UI: RPC: Add TOTK current world layer to RPC based on logged player y pos 2025-02-03 14:54:34 -06:00
c812106611 UI: Show playability information under game version in List view 2025-02-03 13:49:56 -06:00
11e4d8f970 Add the JP version of P5R to the list of Metal games (#612) 2025-02-03 01:36:58 -06:00
774edb7b29 UI: Match System Time is now an active setting which you can toggle on/off. 2025-02-02 23:46:55 -06:00
55536f5d78 misc: chore: Early exit HandlePlayReport if RPC is not enabled 2025-02-02 22:14:43 -06:00
b2eecd28ce UI: RPC: Value Formatter V3
- Allows the ability to bind a single PlayReportGameSpec to multiple title IDs, like for MK8D
- Allows the ability for the value formatters to tell the caller of the analyzer that they should reset the value, and also added the ability to explicitly not handle a value format.
2025-02-02 22:10:49 -06:00
fe43c32e60 UI: The argument to Play Report value formatters is now a struct containing the current ApplicationMetadata & the BoxedValue that was the only argument previously.
This allows for the title of Mario Kart to be localized when one of the value checkers doesn't match.
2025-02-02 20:47:42 -06:00
8117e160c2 misc: chore: [ci skip] Move the play report analyzer definition into a PlayReport static class to avoid polluting the Discord integration module 2025-02-02 20:32:01 -06:00
bf713a80d6 PlayReportAnalyzer: Added Games (#614)
Added Super Mario Odyssey, Super Mario Odyssey (China), Super Mario 3D
World + Bowser's Fury, Mario Kart 8 Deluxe and Mario Kart 8 Deluxe
(China)
2025-02-02 20:29:00 -06:00
b38b5a1e70 docs: compat: Saints Row IV: Playable -> Ingame
Deadlock label added.

Game sometimes just stops loading in loading screens. Game continues like its doing something but you'll be sitting there for minutes wondering why nothing is happening.

Considering the game isn't crashing, this might be an emulator-side mutex issue. I've seen that before.
2025-02-02 16:59:06 -06:00
2d7700949c UI: Play Report Analysis V2
Support for multiple keys per game, and provide an order of resolution via Priority.

(Currently) functionally identical to before, as only BOTW Master Mode is supported.
2025-02-02 16:07:30 -06:00
ea2287af03 misc: chore: Rewrite play report checker to use a simple loop instead of Gommon Optionals
(I love how a class that's supposed to guard against null values entering your code still allows them thats so cool)
2025-02-02 13:17:31 -06:00
37af8c70aa UI: RPC: Add the ability for the DiscordIntegrationModule to inspect values in Play Reports and dynamically show different gameplay values, depending on a predefined map of values and formatters.
Currently only BOTW Master Mode is supported.
Open to PRs!
2025-02-02 02:21:33 -06:00
50cee3fd19 feature: HorizonStatic PlayReportPrinted event 2025-02-02 02:20:14 -06:00
a46aacf2e2 gpu: Switch the 500ms timeout back to 1s
It seemed like it was waiting for 1 second no matter what; might as well have the log & syncpoint map match reality.
2025-02-01 19:21:19 -06:00
ad9d6588e8 misc: chore: Collapse HLE swkbd character validation utils into a single class 2025-02-01 14:11:35 -06:00
38ef65aae0 misc: chore: Move all GeneratedRegex methods into one static class with static instance accessors. 2025-02-01 14:07:32 -06:00
9f94aa1c79 misc: chore: gpu: Lower default Syncpoint wait timeout from 1 second to 500ms 2025-02-01 03:30:13 -06:00
2c9a26c11c misc: chore: Regular Architecture bool properties in RunningPlatform without OS constraint 2025-02-01 03:29:24 -06:00
a4a15a4c80 misc: chore: simplify graphics backend selection logic in RendererHost constructor 2025-02-01 03:28:49 -06:00
cc3b95eee1 misc: chore: More descriptive error for trying to create a Metal EmbeddedWindow on non-ARM Mac 2025-02-01 03:28:26 -06:00
2ab806f759 UI: [ci skip] Fix ContentDialog symbols being backwards for right-to-left languages 2025-02-01 01:42:12 -06:00
6d75410bd2 UI: Use the dynamic Ryujinx/Ryujinx Canary for dialog titles 2025-01-30 21:57:03 -06:00
196b2eaf66 misc: chore: [ci skip] Remove needless fs integrity checks get in aoc extractor 2025-01-30 20:54:08 -06:00
82fe519766 misc: chore: [ci skip] fix log on AOC extraction failure 2025-01-30 20:52:12 -06:00
ff05e03cc8 misc: chore: Remove unused using directives across entire solution. 2025-01-30 20:27:17 -06:00
e18c6d90c4 misc: chore: Strip extension in GetNameForApplicationId instead of requiring the caller to do it 2025-01-30 20:22:11 -06:00
9075a3960b misc: chore: Prefix OS, CPU, and RAM lines with the same method name as other system info prints. 2025-01-30 20:18:26 -06:00
3cf54987d2 misc: chore: More ObservableProperty usage 2025-01-30 20:16:30 -06:00
433 changed files with 17634 additions and 21540 deletions

View File

@ -29,7 +29,7 @@ env:
jobs:
tag:
name: Create tag
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- name: Get version info
id: version_info
@ -202,7 +202,7 @@ jobs:
macos_release:
name: Release MacOS universal
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

View File

@ -18,7 +18,7 @@ env:
jobs:
tag:
name: Create tag
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- name: Get version info
id: version_info
@ -183,7 +183,7 @@ jobs:
macos_release:
name: Release MacOS universal
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

View File

@ -42,11 +42,10 @@
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Gommon" Version="2.7.1" />
<PackageVersion Include="Gommon" Version="2.7.1.1" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.6.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpMetal" Version="1.0.0-preview21" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />

View File

@ -39,12 +39,12 @@
<p align="center">
Click below to join the Discord:
<br>
<a href="https://discord.gg/dHPrkBkkyA">
<a href="https://discord.gg/PEuzjrFXUA">
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
</a>
<br>
<br>
<img src="https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/docs/shell.png">
<img src="https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/docs/shell.png">
</p>
## Usage

View File

@ -77,15 +77,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}"
ProjectSection(ProjectDependencies) = postProject
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal.SharpMetalExtensions", "src/Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj", "{81EA598C-DBA1-40B0-8DA4-4796B78F2037}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@ -95,6 +86,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\release.yml = .github\workflows\release.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -217,6 +210,10 @@ Global
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.Build.0 = Release|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -249,16 +246,9 @@ Global
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -332,6 +332,7 @@
0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21
01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26
01004DA012976000,"Area 86",,playable,2020-12-16 16:45:52
01008d8006a6a000,"Arena of Valor",crash,boots,2025-02-03 22:19:34
0100691013C46000,"ARIA CHRONICLE",,playable,2022-11-16 13:50:55
0100D4A00B284000,"ARK: Survival Evolved",gpu;nvdec;online-broken;UE4;ldn-untested,ingame,2024-04-16 00:53:56
0100C56012C96000,"Arkanoid vs. Space Invaders",services,ingame,2021-01-21 12:50:30
@ -426,6 +427,7 @@
0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07
0100CD801CE5E000,"Balatro",,ingame,2024-04-21 02:01:53
010010A00DA48000,"Baldur's Gate and Baldur's Gate II: Enhanced Editions",32-bit,playable,2022-09-12 23:52:15
0100fd1014726000,"Baldur's Gate: Dark Alliance",ldn-untested,ingame,2025-02-03 22:21:00
0100BC400FB64000,"Balthazar's Dream",,playable,2022-09-13 00:13:22
01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39
010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37
@ -528,6 +530,7 @@
01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43
0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26
0100CFA00CC74000,"Blades of Time",deadlock;online,boots,2022-07-17 19:19:58
01003d700dd8a000,"Blades",,boots,2025-02-03 22:22:00
01006CC01182C000,"Blair Witch",nvdec;UE4,playable,2022-10-01 14:06:16
010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13
0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31
@ -955,7 +958,7 @@
010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28
01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12
010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,menus,2023-08-13 17:20:03
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,ingame,2025-02-03 22:12:30
01001770115C8000,"Dodo Peak",nvdec;UE4,playable,2022-10-04 16:13:05
010077B0100DA000,"Dogurai",,playable,2020-10-04 02:40:16
010048100D51A000,"Dokapon Up! Mugen no Roulette",gpu;Needs Update,menus,2022-12-08 19:39:10
@ -966,6 +969,7 @@
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34
010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10
01009D901BC56000,"Donkey Kong Country™: Returns HD",gpu,ingame,2025-02-16 13:44:12
0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
@ -1155,7 +1159,7 @@
010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22
0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47
0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39
01000EA014150000,"FINAL FANTASY",crash,nothing,2024-09-05 20:55:30
01000EA014150000,"FINAL FANTASY",,playable,2025-02-16 21:27:30
01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04
01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00
0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55
@ -1246,7 +1250,7 @@
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
0100E1F013674000,"FUSER™",nvdec;UE4,playable,2022-10-17 20:58:32
0100E1F013674000,"FUSER™",nvdec;UE4;slow;gpu,ingame,2025-02-12 16:03:00
0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02
01003C300B274000,"Futari de! Nyanko Daisensou",,playable,2024-01-05 22:26:52
010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53
@ -1432,7 +1436,7 @@
010083A018262000,"Hitman: Blood Money — Reprisal",deadlock,ingame,2024-09-28 16:28:50
01004B100A5CC000,"Hob: The Definitive Edition",,playable,2021-01-13 09:39:19
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
0100F7E00C70E000,"Hogwarts Legacy",slow,ingame,2024-09-03 19:53:58
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
@ -1654,7 +1658,7 @@
0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03
0100A7700B46C000,"Legendary Fishing",online,playable,2021-04-14 15:08:46
0100739018020000,"LEGO® 2K Drive",gpu;ldn-works,ingame,2024-04-09 02:05:12
01003A30012C0000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
010085500130a000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
010070D009FEC000,"LEGO® DC Super-Villains",,playable,2021-05-27 18:10:37
010052A00B5D2000,"LEGO® Harry Potter™ Collection",crash,ingame,2024-01-31 10:28:07
010073C01AF34000,"LEGO® Horizon Adventures™",vulkan-backend-bug;opengl-backend-bug;UE4,ingame,2025-01-07 04:24:56
@ -1796,6 +1800,7 @@
010005A00B312000,"Megaton Rainfall",gpu;opengl,boots,2022-08-04 18:29:43
0100EA100DF92000,"Meiji Katsugeki Haikara Ryuuseigumi - Seibai Shimaseu, Yonaoshi Kagyou",32-bit;nvdec,playable,2022-12-05 13:19:12
0100B360068B2000,"Mekorama",gpu,boots,2021-06-17 16:37:21
010012301932A000,"Melatonin",,playable,2025-02-16 04:08:17
01000FA010340000,"Melbits World",nvdec;online,menus,2021-11-26 13:51:22
0100F68019636000,"Melon Journey",,playable,2023-04-23 21:20:01
010079C012896000,"Memories Off -Innocent Fille- for Dearest",,playable,2020-08-04 07:31:22
@ -1913,6 +1918,7 @@
010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17
0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04
0100211005E94000,"Mulaka",,playable,2021-01-28 18:07:20
01008e2013fb4000,"Multi Quiz",ldn-untested,ingame,2025-02-03 22:26:00
010038B00B9AE000,"Mummy Pinball",,playable,2022-08-05 16:08:11
01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29
010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08
@ -2028,6 +2034,7 @@
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
0100C9A00ECE6000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
0100e0601c632000,"Nintendo 64™ Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
@ -2058,7 +2065,7 @@
010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07
010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23
010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22
0000000000000000,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
,"nxquake2",services;crash;homebrew,nothing,2022-08-04 23:14:04
010049F00EC30000,"Nyan Cat: Lost in Space",online,playable,2021-06-12 13:22:03
01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14
@ -2466,7 +2473,7 @@
0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38
0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01
01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18
0000000000000000,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
010009B00D33C000,"Rugby Challenge 4",slow;online-broken;UE4,playable,2022-10-06 12:45:53
01006EC00F2CC000,"RUINER",UE4,playable,2022-10-03 14:11:33
010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28
@ -2475,6 +2482,7 @@
010081C0191D8000,"Rune Factory 3 Special",,playable,2023-10-15 08:32:49
010051D00E3A4000,"Rune Factory 4 Special",32-bit;crash;nvdec,ingame,2023-05-06 08:49:17
010014D01216E000,"Rune Factory 5 (JP)",gpu,ingame,2021-06-01 12:00:36
010071E0145F8000,"Rustler",,playable,2025-02-10 20:17:12
0100E21013908000,"RWBY: Grimm Eclipse - Definitive Edition",online-broken,playable,2022-11-03 10:44:01
010012C0060F0000,"RXN -Raijin-",nvdec,playable,2021-01-10 16:05:43
0100B8B012ECA000,"S.N.I.P.E.R. - Hunter Scope",,playable,2021-04-19 15:58:09
@ -2483,7 +2491,7 @@
0100A5200C2E0000,"Safety First!",,playable,2021-01-06 09:05:23
0100A51013530000,"SaGa Frontier Remastered",nvdec,playable,2022-11-03 13:54:56
010003A00D0B4000,"SaGa SCARLET GRACE: AMBITIONS™",,playable,2022-10-06 13:20:31
01008D100D43E000,"Saints Row IV®: Re-Elected™",ldn-untested;LAN,playable,2023-12-04 18:33:37
01008D100D43E000,"Saints Row IV®: Re-Elected™",ldn-untested;LAN;deadlock,ingame,2025-02-02 16:57:53
0100DE600BEEE000,"SAINTS ROW®: THE THIRD™ - THE FULL PACKAGE",slow;LAN,playable,2023-08-24 02:40:58
01007F000EB36000,"Sakai and...",nvdec,playable,2022-12-15 13:53:19
0100B1400E8FE000,"Sakuna: Of Rice and Ruin",,playable,2023-07-24 13:47:13
@ -2532,7 +2540,7 @@
0100C3E00B700000,"SEGA AGES Space Harrier",,playable,2021-01-11 12:57:40
010054400D2E6000,"SEGA AGES Virtua Racing",online-broken,playable,2023-01-29 17:08:39
01001E700AC60000,"SEGA AGES Wonder Boy: Monster Land",online,playable,2021-05-05 16:28:25
0100B3C014BDA000,"SEGA Genesis™ Nintendo Switch Online",crash;regression,nothing,2022-04-11 07:27:21
0100B3C014BDA000,"SEGA Genesis™ Nintendo Switch Online",crash;regression,ingame,2025-02-03 22:13:30
0100F7300B24E000,"SEGA Mega Drive Classics",online,playable,2021-01-05 11:08:00
01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31
0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10
@ -2668,10 +2676,10 @@
01004F401BEBE000,"Song of Nunu: A League of Legends Story",,ingame,2024-07-12 18:53:44
0100E5400BF94000,"Songbird Symphony",,playable,2021-02-27 02:44:04
010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02
0000000000000000,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
0000000000000000,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
0000000000000000,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
0000000000000000,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26
01001270012B6000,"SONIC FORCES™",,playable,2024-07-28 13:11:21
01004AD014BF0000,"Sonic Frontiers",gpu;deadlock;amd-vendor-bug;intel-vendor-bug,ingame,2024-09-05 09:18:53
@ -2688,7 +2696,7 @@
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37
010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10
0000000000000000,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
0100D9B0041CE000,"Spacecats with Lasers",,playable,2022-08-15 17:22:44
010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56
010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39
@ -2832,8 +2840,9 @@
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
0000000000000000,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16
010028600EBDA000,"Super Mario™ 3D World + Bowsers Fury",ldn-works,playable,2024-07-31 10:45:37
01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
@ -2964,6 +2973,7 @@
0100C38004DCC000,"The Flame In The Flood: Complete Edition",gpu;nvdec;UE4,ingame,2022-08-22 16:23:49
010007700D4AC000,"The Forbidden Arts",,playable,2021-01-26 16:26:24
010030700CBBC000,"The friends of Ringo Ishikawa",,playable,2022-08-22 16:33:17
0100b620139d8000,"The Game of Life 2",ldn-untested,ingame,2025-02-03 22:30:00
01006350148DA000,"The Gardener and the Wild Vines",gpu,ingame,2024-04-29 16:32:10
0100B13007A6A000,"The Gardens Between",,playable,2021-01-29 16:16:53
010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29
@ -2981,6 +2991,8 @@
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
01006fe0096ac000,"The Jackbox Party Pack 5",slow;online-working,ingame,2025-02-14 05:32:00
01005a400db52000,"The Jackbox Party Pack 6",slow;online-working,ingame,2025-02-14 05:26:00
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
@ -3159,6 +3171,7 @@
010055E00CA68000,"Trine 4: The Nightmare Prince",gpu,nothing,2025-01-07 05:47:46
0100D9000A930000,"Trine Enchanted Edition",ldn-untested;nvdec,playable,2021-06-03 11:28:15
01002D7010A54000,"Trinity Trigger",crash,ingame,2023-03-03 03:09:09
010020700a5e0000,"TRIVIAL PURSUIT Live!",ldn-untested,ingame,2025-02-03 22:35:00
0100868013FFC000,"TRIVIAL PURSUIT Live! 2",,boots,2022-12-19 00:04:33
0100F78002040000,"Troll and I™",gpu;nvdec,ingame,2021-06-04 16:58:50
0100145011008000,"Trollhunters: Defenders of Arcadia",gpu;nvdec,ingame,2020-11-30 13:27:09
@ -3208,6 +3221,7 @@
0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27
0100A3E011CB0000,"Unlock the King 2",,playable,2021-06-15 20:43:55
01005AA00372A000,"UNO® for Nintendo Switch",nvdec;ldn-untested,playable,2022-07-28 14:49:47
0100b6e012ebe000,"UNO",ldn-untested,ingame,2025-02-03 22:40:00
0100E5D00CC0C000,"Unravel Two",nvdec,playable,2024-05-23 15:45:05
010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31
0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49
@ -3372,6 +3386,7 @@
0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51
010012F00B6F2000,"Yomawari: The Long Night Collection",,playable,2022-09-03 14:36:59
0100CC600ABB2000,"Yonder: The Cloud Catcher Chronicles (Retail Only)",,playable,2021-01-28 14:06:25
0100534009ff2000,"Yonder: The Cloud Catcher Chronicles",,playable,2025-02-03 22:19:13
0100BE50042F6000,"Yono and the Celestial Elephants",,playable,2021-01-28 18:23:58
0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45
010022F00DA66000,"Yooka-Laylee and the Impossible Lair",,playable,2021-03-05 17:32:21

1 title_id game_name labels status last_updated
332 0100E680149DC000 Arcaea playable 2023-03-16 19:31:21
333 01003C2010C78000 Archaica: The Path Of Light crash nothing 2020-10-16 13:22:26
334 01004DA012976000 Area 86 playable 2020-12-16 16:45:52
335 01008d8006a6a000 Arena of Valor crash boots 2025-02-03 22:19:34
336 0100691013C46000 ARIA CHRONICLE playable 2022-11-16 13:50:55
337 0100D4A00B284000 ARK: Survival Evolved gpu;nvdec;online-broken;UE4;ldn-untested ingame 2024-04-16 00:53:56
338 0100C56012C96000 Arkanoid vs. Space Invaders services ingame 2021-01-21 12:50:30
427 0100E48013A34000 Balan Wonderworld Demo gpu;services;UE4;demo ingame 2023-02-16 20:05:07
428 0100CD801CE5E000 Balatro ingame 2024-04-21 02:01:53
429 010010A00DA48000 Baldur's Gate and Baldur's Gate II: Enhanced Editions 32-bit playable 2022-09-12 23:52:15
430 0100fd1014726000 Baldur's Gate: Dark Alliance ldn-untested ingame 2025-02-03 22:21:00
431 0100BC400FB64000 Balthazar's Dream playable 2022-09-13 00:13:22
432 01008D30128E0000 Bamerang playable 2022-10-26 00:29:39
433 010013C010C5C000 Banner of the Maid playable 2021-06-14 15:23:37
530 01005950022EC000 Blade Strangers nvdec playable 2022-07-17 19:02:43
531 0100DF0011A6A000 Bladed Fury playable 2022-10-26 11:36:26
532 0100CFA00CC74000 Blades of Time deadlock;online boots 2022-07-17 19:19:58
533 01003d700dd8a000 Blades boots 2025-02-03 22:22:00
534 01006CC01182C000 Blair Witch nvdec;UE4 playable 2022-10-01 14:06:16
535 010039501405E000 Blanc gpu;slow ingame 2023-02-22 14:00:13
536 0100698009C6E000 Blasphemous nvdec playable 2021-03-01 12:15:31
958 010012800EBAE000 Disney TSUM TSUM FESTIVAL crash menus 2020-07-14 14:05:28
959 01009740120FE000 DISTRAINT 2 playable 2020-09-03 16:08:12
960 010075B004DD2000 DISTRAINT: Deluxe Edition playable 2020-06-15 23:42:24
961 010027400CDC6000 Divinity: Original Sin 2 - Definitive Edition services;crash;online-broken;regression menus ingame 2023-08-13 17:20:03 2025-02-03 22:12:30
962 01001770115C8000 Dodo Peak nvdec;UE4 playable 2022-10-04 16:13:05
963 010077B0100DA000 Dogurai playable 2020-10-04 02:40:16
964 010048100D51A000 Dokapon Up! Mugen no Roulette gpu;Needs Update menus 2022-12-08 19:39:10
969 0100751007ADA000 Don't Starve: Nintendo Switch Edition nvdec playable 2022-02-05 20:43:34
970 010088B010DD2000 Dongo Adventure playable 2022-10-04 16:22:26
971 0100C1F0051B6000 Donkey Kong Country™: Tropical Freeze playable 2024-08-05 16:46:10
972 01009D901BC56000 Donkey Kong Country™: Returns HD gpu ingame 2025-02-16 13:44:12
973 0100F2C00F060000 Doodle Derby boots 2020-12-04 22:51:48
974 0100416004C00000 DOOM gpu;slow;nvdec;online-broken ingame 2024-09-23 15:40:07
975 010018900DD00000 DOOM (1993) nvdec;online-broken menus 2022-09-06 13:32:19
1159 010095600AA36000 Fill-a-Pix: Phil's Epic Adventure playable 2020-12-22 13:48:22
1160 0100C3A00BB76000 Fimbul nvdec playable 2022-07-26 13:31:47
1161 0100C8200E942000 Fin and the Ancient Mystery nvdec playable 2020-12-17 16:40:39
1162 01000EA014150000 FINAL FANTASY crash nothing playable 2024-09-05 20:55:30 2025-02-16 21:27:30
1163 01006B7014156000 FINAL FANTASY II crash nothing 2024-04-13 19:18:04
1164 01006F000B056000 FINAL FANTASY IX audout;nvdec playable 2021-06-05 11:35:00
1165 0100AA201415C000 FINAL FANTASY V playable 2023-04-26 01:11:55
1250 0100A6B00D4EC000 Furwind playable 2021-02-19 19:44:08
1251 0100ECE00C0C4000 Fury Unleashed crash;services ingame 2020-10-18 11:52:40
1252 010070000ED9E000 Fury Unleashed Demo playable 2020-10-08 20:09:21
1253 0100E1F013674000 FUSER™ nvdec;UE4 nvdec;UE4;slow;gpu playable ingame 2022-10-17 20:58:32 2025-02-12 16:03:00
1254 0100A7A015E4C000 Fushigi no Gensokyo Lotus Labyrinth Needs Update;audio;gpu;nvdec ingame 2021-01-20 15:30:02
1255 01003C300B274000 Futari de! Nyanko Daisensou playable 2024-01-05 22:26:52
1256 010055801134E000 FUZE Player online-broken;vulkan-backend-bug ingame 2022-10-18 12:23:53
1436 010083A018262000 Hitman: Blood Money — Reprisal deadlock ingame 2024-09-28 16:28:50
1437 01004B100A5CC000 Hob: The Definitive Edition playable 2021-01-13 09:39:19
1438 0100F7300ED2C000 Hoggy2 playable 2022-10-10 13:53:35
1439 0100F7E00C70E000 Hogwarts Legacy slow UE4;slow ingame 2024-09-03 19:53:58
1440 0100633007D48000 Hollow Knight nvdec playable 2023-01-16 15:44:56
1441 0100F2100061E800 Hollow0 UE4;gpu ingame 2021-03-03 23:42:56
1442 0100342009E16000 Holy Potatoes! What The Hell?! playable 2020-07-03 10:48:56
1658 0100A73006E74000 Legendary Eleven playable 2021-06-08 12:09:03
1659 0100A7700B46C000 Legendary Fishing online playable 2021-04-14 15:08:46
1660 0100739018020000 LEGO® 2K Drive gpu;ldn-works ingame 2024-04-09 02:05:12
1661 01003A30012C0000 010085500130a000 LEGO® CITY Undercover nvdec playable 2024-09-30 08:44:27
1662 010070D009FEC000 LEGO® DC Super-Villains playable 2021-05-27 18:10:37
1663 010052A00B5D2000 LEGO® Harry Potter™ Collection crash ingame 2024-01-31 10:28:07
1664 010073C01AF34000 LEGO® Horizon Adventures™ vulkan-backend-bug;opengl-backend-bug;UE4 ingame 2025-01-07 04:24:56
1800 010005A00B312000 Megaton Rainfall gpu;opengl boots 2022-08-04 18:29:43
1801 0100EA100DF92000 Meiji Katsugeki Haikara Ryuuseigumi - Seibai Shimaseu, Yonaoshi Kagyou 32-bit;nvdec playable 2022-12-05 13:19:12
1802 0100B360068B2000 Mekorama gpu boots 2021-06-17 16:37:21
1803 010012301932A000 Melatonin playable 2025-02-16 04:08:17
1804 01000FA010340000 Melbits World nvdec;online menus 2021-11-26 13:51:22
1805 0100F68019636000 Melon Journey playable 2023-04-23 21:20:01
1806 010079C012896000 Memories Off -Innocent Fille- for Dearest playable 2020-08-04 07:31:22
1918 010073E008E6E000 Mugsters playable 2021-01-28 17:57:17
1919 0100A8400471A000 MUJO playable 2020-05-08 16:31:04
1920 0100211005E94000 Mulaka playable 2021-01-28 18:07:20
1921 01008e2013fb4000 Multi Quiz ldn-untested ingame 2025-02-03 22:26:00
1922 010038B00B9AE000 Mummy Pinball playable 2022-08-05 16:08:11
1923 01008E200C5C2000 Muse Dash playable 2020-06-06 14:41:29
1924 010035901046C000 Mushroom Quest playable 2020-05-17 13:07:08
2034 010003C00B868000 Ninjin: Clash of Carrots online-broken playable 2024-07-10 05:12:26
2035 0100746010E4C000 NinNinDays playable 2022-11-20 15:17:29
2036 0100C9A00ECE6000 Nintendo 64™ – Nintendo Switch Online gpu;vulkan ingame 2024-04-23 20:21:07
2037 0100e0601c632000 Nintendo 64™ – Nintendo Switch Online: MATURE 17+ ingame 2025-02-03 22:27:00
2038 0100D870045B6000 Nintendo Entertainment System™ - Nintendo Switch Online online playable 2022-07-01 15:45:06
2039 0100C4B0034B2000 Nintendo Labo Toy-Con 01 Variety Kit gpu ingame 2022-08-07 12:56:07
2040 01001E9003502000 Nintendo Labo Toy-Con 03 Vehicle Kit services;crash menus 2022-08-03 17:20:11
2065 010002700C34C000 Numbala playable 2020-05-11 12:01:07
2066 010020500C8C8000 Number Place 10000 gpu menus 2021-11-24 09:14:23
2067 010003701002C000 Nurse Love Syndrome playable 2022-10-13 10:05:22
2068 0000000000000000 nx-hbmenu Needs Update;homebrew boots 2024-04-06 22:05:32
2069 nxquake2 services;crash;homebrew nothing 2022-08-04 23:14:04
2070 010049F00EC30000 Nyan Cat: Lost in Space online playable 2021-06-12 13:22:03
2071 01002E6014FC4000 O---O playable 2022-10-29 12:12:14
2473 0100AFE00DDAC000 Royal Roads playable 2020-11-17 12:54:38
2474 0100E2C00B414000 RPG Maker MV nvdec playable 2021-01-05 20:12:01
2475 01005CD015986000 rRootage Reloaded playable 2022-08-05 23:20:18
2476 0000000000000000 RSDKv5u homebrew ingame 2024-04-01 16:25:34
2477 010009B00D33C000 Rugby Challenge 4 slow;online-broken;UE4 playable 2022-10-06 12:45:53
2478 01006EC00F2CC000 RUINER UE4 playable 2022-10-03 14:11:33
2479 010074F00DE4A000 Run the Fan playable 2021-02-27 13:36:28
2482 010081C0191D8000 Rune Factory 3 Special playable 2023-10-15 08:32:49
2483 010051D00E3A4000 Rune Factory 4 Special 32-bit;crash;nvdec ingame 2023-05-06 08:49:17
2484 010014D01216E000 Rune Factory 5 (JP) gpu ingame 2021-06-01 12:00:36
2485 010071E0145F8000 Rustler playable 2025-02-10 20:17:12
2486 0100E21013908000 RWBY: Grimm Eclipse - Definitive Edition online-broken playable 2022-11-03 10:44:01
2487 010012C0060F0000 RXN -Raijin- nvdec playable 2021-01-10 16:05:43
2488 0100B8B012ECA000 S.N.I.P.E.R. - Hunter Scope playable 2021-04-19 15:58:09
2491 0100A5200C2E0000 Safety First! playable 2021-01-06 09:05:23
2492 0100A51013530000 SaGa Frontier Remastered nvdec playable 2022-11-03 13:54:56
2493 010003A00D0B4000 SaGa SCARLET GRACE: AMBITIONS™ playable 2022-10-06 13:20:31
2494 01008D100D43E000 Saints Row IV®: Re-Elected™ ldn-untested;LAN ldn-untested;LAN;deadlock playable ingame 2023-12-04 18:33:37 2025-02-02 16:57:53
2495 0100DE600BEEE000 SAINTS ROW®: THE THIRD™ - THE FULL PACKAGE slow;LAN playable 2023-08-24 02:40:58
2496 01007F000EB36000 Sakai and... nvdec playable 2022-12-15 13:53:19
2497 0100B1400E8FE000 Sakuna: Of Rice and Ruin playable 2023-07-24 13:47:13
2540 0100C3E00B700000 SEGA AGES Space Harrier playable 2021-01-11 12:57:40
2541 010054400D2E6000 SEGA AGES Virtua Racing online-broken playable 2023-01-29 17:08:39
2542 01001E700AC60000 SEGA AGES Wonder Boy: Monster Land online playable 2021-05-05 16:28:25
2543 0100B3C014BDA000 SEGA Genesis™ – Nintendo Switch Online crash;regression nothing ingame 2022-04-11 07:27:21 2025-02-03 22:13:30
2544 0100F7300B24E000 SEGA Mega Drive Classics online playable 2021-01-05 11:08:00
2545 01009840046BC000 Semispheres playable 2021-01-06 23:08:31
2546 0100D1800D902000 SENRAN KAGURA Peach Ball playable 2021-06-03 15:12:10
2676 01004F401BEBE000 Song of Nunu: A League of Legends Story ingame 2024-07-12 18:53:44
2677 0100E5400BF94000 Songbird Symphony playable 2021-02-27 02:44:04
2678 010031D00A604000 Songbringer playable 2020-06-22 10:42:02
2679 0000000000000000 Sonic 1 (2013) crash;homebrew ingame 2024-04-06 18:31:20
2680 0000000000000000 Sonic 2 (2013) crash;homebrew ingame 2024-04-01 16:25:30
2681 0000000000000000 Sonic A.I.R homebrew ingame 2024-04-01 16:25:32
2682 0000000000000000 Sonic CD crash;homebrew ingame 2024-04-01 16:25:31
2683 010040E0116B8000 Sonic Colors: Ultimate playable 2022-11-12 21:24:26
2684 01001270012B6000 SONIC FORCES™ playable 2024-07-28 13:11:21
2685 01004AD014BF0000 Sonic Frontiers gpu;deadlock;amd-vendor-bug;intel-vendor-bug ingame 2024-09-05 09:18:53
2696 0100707011722000 Space Elite Force playable 2020-11-27 15:21:05
2697 010047B010260000 Space Pioneer playable 2022-10-20 12:24:37
2698 010010A009830000 Space Ribbon playable 2022-08-15 17:17:10
2699 0000000000000000 SpaceCadetPinball homebrew ingame 2024-04-18 19:30:04
2700 0100D9B0041CE000 Spacecats with Lasers playable 2022-08-15 17:22:44
2701 010034800FB60000 Spaceland playable 2020-11-01 14:31:56
2702 010028D0045CE000 Sparkle 2 playable 2020-10-19 11:51:39
2840 01009B90006DC000 Super Mario Maker™ 2 online-broken;ldn-broken playable 2024-08-25 11:05:19
2841 0100000000010000 Super Mario Odyssey™ nvdec;intel-vendor-bug;mac-bug playable 2024-08-25 01:32:34
2842 010036B0034E4000 Super Mario Party™ gpu;Needs Update;ldn-works ingame 2024-06-21 05:10:16
2843 0100965017338000 Super Mario Party Jamboree mac-bug;gpu ingame 2025-02-17 02:09:20
2844 0100BC0018138000 Super Mario RPG™ gpu;audio;nvdec ingame 2024-06-19 17:43:42
2845 0000000000000000 Super Mario World homebrew boots 2024-06-13 01:40:31
2846 010049900F546000 Super Mario™ 3D All-Stars services-horizon;slow;vulkan;amd-vendor-bug ingame 2024-05-07 02:38:16
2847 010028600EBDA000 Super Mario™ 3D World + Bowser’s Fury ldn-works playable 2024-07-31 10:45:37
2848 01004F8006A78000 Super Meat Boy services playable 2020-04-02 23:10:07
2973 0100C38004DCC000 The Flame In The Flood: Complete Edition gpu;nvdec;UE4 ingame 2022-08-22 16:23:49
2974 010007700D4AC000 The Forbidden Arts playable 2021-01-26 16:26:24
2975 010030700CBBC000 The friends of Ringo Ishikawa playable 2022-08-22 16:33:17
2976 0100b620139d8000 The Game of Life 2 ldn-untested ingame 2025-02-03 22:30:00
2977 01006350148DA000 The Gardener and the Wild Vines gpu ingame 2024-04-29 16:32:10
2978 0100B13007A6A000 The Gardens Between playable 2021-01-29 16:16:53
2979 010036E00FB20000 The Great Ace Attorney Chronicles playable 2023-06-22 21:26:29
2991 010015D003EE4000 The Jackbox Party Pack 2 online-working playable 2022-08-22 18:23:40
2992 0100CC80013D6000 The Jackbox Party Pack 3 slow;online-working playable 2022-08-22 18:41:06
2993 0100E1F003EE8000 The Jackbox Party Pack 4 online-working playable 2022-08-22 18:56:34
2994 01006fe0096ac000 The Jackbox Party Pack 5 slow;online-working ingame 2025-02-14 05:32:00
2995 01005a400db52000 The Jackbox Party Pack 6 slow;online-working ingame 2025-02-14 05:26:00
2996 010052C00B184000 The Journey Down: Chapter One nvdec playable 2021-02-24 13:32:41
2997 01006BC00B188000 The Journey Down: Chapter Three nvdec playable 2021-02-24 13:45:27
2998 01009AB00B186000 The Journey Down: Chapter Two nvdec playable 2021-02-24 13:32:13
3171 010055E00CA68000 Trine 4: The Nightmare Prince gpu nothing 2025-01-07 05:47:46
3172 0100D9000A930000 Trine Enchanted Edition ldn-untested;nvdec playable 2021-06-03 11:28:15
3173 01002D7010A54000 Trinity Trigger crash ingame 2023-03-03 03:09:09
3174 010020700a5e0000 TRIVIAL PURSUIT Live! ldn-untested ingame 2025-02-03 22:35:00
3175 0100868013FFC000 TRIVIAL PURSUIT Live! 2 boots 2022-12-19 00:04:33
3176 0100F78002040000 Troll and I™ gpu;nvdec ingame 2021-06-04 16:58:50
3177 0100145011008000 Trollhunters: Defenders of Arcadia gpu;nvdec ingame 2020-11-30 13:27:09
3221 0100AB2010B4C000 Unlock The King playable 2020-09-01 13:58:27
3222 0100A3E011CB0000 Unlock the King 2 playable 2021-06-15 20:43:55
3223 01005AA00372A000 UNO® for Nintendo Switch nvdec;ldn-untested playable 2022-07-28 14:49:47
3224 0100b6e012ebe000 UNO ldn-untested ingame 2025-02-03 22:40:00
3225 0100E5D00CC0C000 Unravel Two nvdec playable 2024-05-23 15:45:05
3226 010001300CC4A000 Unruly Heroes playable 2021-01-07 18:09:31
3227 0100B410138C0000 Unspottable playable 2022-10-25 19:28:49
3386 0100F47016F26000 Yomawari 3 playable 2022-05-10 08:26:51
3387 010012F00B6F2000 Yomawari: The Long Night Collection playable 2022-09-03 14:36:59
3388 0100CC600ABB2000 Yonder: The Cloud Catcher Chronicles (Retail Only) playable 2021-01-28 14:06:25
3389 0100534009ff2000 Yonder: The Cloud Catcher Chronicles playable 2025-02-03 22:19:13
3390 0100BE50042F6000 Yono and the Celestial Elephants playable 2021-01-28 18:23:58
3391 0100F110029C8000 Yooka-Laylee playable 2021-01-28 14:21:45
3392 010022F00DA66000 Yooka-Laylee and the Impossible Lair playable 2021-03-05 17:32:21

View File

@ -1,7 +1,6 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Translation.Cache;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.CodeGen

View File

@ -1,4 +1,3 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

View File

@ -7,6 +7,7 @@ namespace ARMeilleure.Memory
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
public IJitMemoryBlock Block { get; }
public IJitMemoryAllocator Allocator { get; }
public nint Pointer => Block.Pointer;
@ -21,6 +22,7 @@ namespace ARMeilleure.Memory
granularity = DefaultGranularity;
}
Allocator = allocator;
Block = allocator.Reserve(maxSize);
_maxSize = maxSize;
_sizeGranularity = granularity;

View File

@ -1,4 +1,3 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

View File

@ -1,6 +1,5 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;

View File

@ -1,7 +1,6 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using Ryujinx.Common.Memory.PartialUnmaps;
using System;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;

View File

@ -1,5 +1,4 @@
using ARMeilleure.Memory;
using System;
namespace ARMeilleure.State
{

View File

@ -6,7 +6,6 @@ using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory;
using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Reflection;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;

View File

@ -2,6 +2,8 @@ using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Memory;
using ARMeilleure.Native;
using Humanizer;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Generic;
@ -18,51 +20,68 @@ namespace ARMeilleure.Translation.Cache
private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 2047 * 1024 * 1024;
private const int CacheSize = 256 * 1024 * 1024;
private static ReservedRegion _jitRegion;
private static JitCacheInvalidation _jitCacheInvalidator;
private static CacheMemoryAllocator _cacheAllocator;
private static List<CacheMemoryAllocator> _cacheAllocators = [];
private static readonly List<CacheEntry> _cacheEntries = [];
private static readonly Lock _lock = new();
private static bool _initialized;
private static readonly List<ReservedRegion> _jitRegions = [];
private static int _activeRegionIndex = 0;
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
public static void Initialize(IJitMemoryAllocator allocator)
{
if (_initialized)
{
return;
}
lock (_lock)
{
if (_initialized)
{
return;
if (OperatingSystem.IsWindows())
{
JitUnwindWindows.RemoveFunctionTableHandler(
_jitRegions[0].Pointer);
}
for (int i = 0; i < _jitRegions.Count; i++)
{
_jitRegions[i].Dispose();
}
_jitRegions.Clear();
_cacheAllocators.Clear();
}
else
{
_initialized = true;
}
_jitRegion = new ReservedRegion(allocator, CacheSize);
_activeRegionIndex = 0;
ReservedRegion firstRegion = new(allocator, CacheSize);
_jitRegions.Add(firstRegion);
CacheMemoryAllocator firstCacheAllocator = new(CacheSize);
_cacheAllocators.Add(firstCacheAllocator);
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
{
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
}
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
if (OperatingSystem.IsWindows())
{
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize));
JitUnwindWindows.InstallFunctionTableHandler(
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
);
}
_initialized = true;
}
}
@ -75,8 +94,8 @@ namespace ARMeilleure.Translation.Cache
Debug.Assert(_initialized);
int funcOffset = Allocate(code.Length);
nint funcPtr = _jitRegion.Pointer + funcOffset;
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
nint funcPtr = targetRegion.Pointer + funcOffset;
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
@ -90,9 +109,9 @@ namespace ARMeilleure.Translation.Cache
}
else
{
ReprotectAsWritable(funcOffset, code.Length);
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
Marshal.Copy(code, 0, funcPtr, code.Length);
ReprotectAsExecutable(funcOffset, code.Length);
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
@ -116,52 +135,77 @@ namespace ARMeilleure.Translation.Cache
{
Debug.Assert(_initialized);
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
foreach (ReservedRegion region in _jitRegions)
{
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
{
continue;
}
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
{
_cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
}
return;
}
}
}
private static void ReprotectAsWritable(int offset, int size)
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static void ReprotectAsExecutable(int offset, int size)
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static int Allocate(int codeSize)
{
codeSize = AlignCodeSize(codeSize);
int allocOffset = _cacheAllocator.Allocate(codeSize);
int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
if (allocOffset < 0)
if (allocOffset >= 0)
{
throw new OutOfMemoryException("JIT Cache exhausted.");
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
return allocOffset;
}
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
int exhaustedRegion = _activeRegionIndex;
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
_jitRegions.Add(newRegion);
_activeRegionIndex = _jitRegions.Count - 1;
return allocOffset;
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation).");
_cacheAllocators.Add(new CacheMemoryAllocator(CacheSize));
int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
if (allocOffsetNew < 0)
{
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
}
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
return allocOffsetNew;
}
private static int AlignCodeSize(int codeSize)
{
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
@ -185,18 +229,21 @@ namespace ARMeilleure.Translation.Cache
{
lock (_lock)
{
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
if (index < 0)
foreach (ReservedRegion _ in _jitRegions)
{
index = ~index - 1;
}
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
if (index >= 0)
{
entry = _cacheEntries[index];
entryIndex = index;
return true;
if (index < 0)
{
index = ~index - 1;
}
if (index >= 0)
{
entry = _cacheEntries[index];
entryIndex = index;
return true;
}
}
}

View File

@ -1,5 +1,4 @@
using ARMeilleure.Memory;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation.Cache

View File

@ -52,6 +52,11 @@ namespace ARMeilleure.Translation.Cache
nint context,
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
[LibraryImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static unsafe partial bool RtlDeleteFunctionTable(
ulong tableIdentifier);
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
private static int _sizeOfRuntimeFunction;
@ -91,6 +96,23 @@ namespace ARMeilleure.Translation.Cache
}
}
public static void RemoveFunctionTableHandler(nint codeCachePointer)
{
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
bool result;
unsafe
{
result = RtlDeleteFunctionTable(codeCachePtr | 3);
}
if (!result)
{
throw new InvalidOperationException("Failure removing function table callback.");
}
}
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
{
int offset = (int)((long)controlPc - context.ToInt64());

View File

@ -1,5 +1,3 @@
using System;
namespace ARMeilleure.Translation
{
class DelegateInfo

View File

@ -1,5 +1,3 @@
using System;
namespace ARMeilleure.Translation
{
delegate void DispatcherFunction(nint nativeContext, ulong startAddress);

View File

@ -1,5 +1,3 @@
using System;
namespace ARMeilleure.Translation
{
delegate ulong GuestFunction(nint nativeContextPtr);

View File

@ -144,17 +144,15 @@ namespace ARMeilleure.Translation.PTC
public List<ulong> GetBlacklistedFunctions()
{
List<ulong> funcs = new List<ulong>();
List<ulong> funcs = [];
foreach (var profiledFunc in ProfiledFuncs)
foreach ((ulong ptr, FuncProfile funcProfile) in ProfiledFuncs)
{
if (profiledFunc.Value.Blacklist)
{
if (!funcs.Contains(profiledFunc.Key))
{
funcs.Add(profiledFunc.Key);
}
}
if (!funcProfile.Blacklist)
continue;
if (!funcs.Contains(ptr))
funcs.Add(ptr);
}
return funcs;

View File

@ -1,5 +1,4 @@
using ARMeilleure.Common;
using System;
namespace ARMeilleure.Translation
{

View File

@ -5,7 +5,6 @@ using ARMeilleure.Diagnostics;
using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory;
using ARMeilleure.Signal;
using ARMeilleure.State;
using ARMeilleure.Translation.Cache;
using ARMeilleure.Translation.PTC;

View File

@ -4,7 +4,6 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation.Cache;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;

View File

@ -4,7 +4,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Threading;

View File

@ -1,5 +1,4 @@
using Ryujinx.Common.Memory;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

View File

@ -1,4 +1,3 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;

View File

@ -4,7 +4,6 @@ using Ryujinx.Audio.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Threading;
namespace Ryujinx.Audio.Backends.Common

View File

@ -1,4 +1,3 @@
using System;
using System.Runtime.InteropServices;
using CpuAddress = System.UInt64;
using DspAddress = System.UInt64;

View File

@ -6,9 +6,7 @@ namespace Ryujinx.Common.Configuration
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
public enum GraphicsBackend
{
Auto,
Vulkan,
OpenGl,
Metal
}
}

View File

@ -1,5 +1,4 @@
using Microsoft.Win32;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
using System.Diagnostics;

View File

@ -1,4 +1,3 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

View File

@ -0,0 +1,118 @@
using System.Text.RegularExpressions;
namespace Ryujinx.Common.Helper
{
public static partial class Patterns
{
#region Accessors
public static readonly Regex Numeric = NumericRegex();
public static readonly Regex AmdGcn = AmdGcnRegex();
public static readonly Regex NvidiaConsumerClass = NvidiaConsumerClassRegex();
public static readonly Regex DomainLp1Ns = DomainLp1NsRegex();
public static readonly Regex DomainLp1Lp1Npln = DomainLp1Lp1NplnRegex();
public static readonly Regex DomainLp1Znc = DomainLp1ZncRegex();
public static readonly Regex DomainSbApi = DomainSbApiRegex();
public static readonly Regex DomainSbAccounts = DomainSbAccountsRegex();
public static readonly Regex DomainAccounts = DomainAccountsRegex();
public static readonly Regex Module = ModuleRegex();
public static readonly Regex FsSdk = FsSdkRegex();
public static readonly Regex SdkMw = SdkMwRegex();
// ReSharper disable once InconsistentNaming
public static readonly Regex CJK = CJKRegex();
public static readonly Regex LdnPassphrase = LdnPassphraseRegex();
public static readonly Regex CleanText = CleanTextRegex();
#endregion
#region Generated pattern stubs
#region Numeric validation
[GeneratedRegex("[0-9]|.")]
internal static partial Regex NumericRegex();
#endregion
#region GPU names
[GeneratedRegex(
"Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
internal static partial Regex AmdGcnRegex();
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
internal static partial Regex NvidiaConsumerClassRegex();
#endregion
#region DNS blocking
public static readonly Regex[] BlockedHosts =
[
DomainLp1Ns,
DomainLp1Lp1Npln,
DomainLp1Znc,
DomainSbApi,
DomainSbAccounts,
DomainAccounts
];
const RegexOptions DnsRegexOpts =
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
[GeneratedRegex(@"^(.*)\-lp1\.(n|s)\.n\.srv\.nintendo\.net$", DnsRegexOpts)]
internal static partial Regex DomainLp1NsRegex();
[GeneratedRegex(@"^(.*)\-lp1\.lp1\.t\.npln\.srv\.nintendo\.net$", DnsRegexOpts)]
internal static partial Regex DomainLp1Lp1NplnRegex();
[GeneratedRegex(@"^(.*)\-lp1\.(znc|p)\.srv\.nintendo\.net$", DnsRegexOpts)]
internal static partial Regex DomainLp1ZncRegex();
[GeneratedRegex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$", DnsRegexOpts)]
internal static partial Regex DomainSbApiRegex();
[GeneratedRegex(@"^(.*)\-sb\.accounts\.nintendo\.com$", DnsRegexOpts)]
internal static partial Regex DomainSbAccountsRegex();
[GeneratedRegex(@"^accounts\.nintendo\.com$", DnsRegexOpts)]
internal static partial Regex DomainAccountsRegex();
#endregion
#region Executable information
[GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
internal static partial Regex ModuleRegex();
[GeneratedRegex(@"sdk_version: ([0-9.]*)")]
internal static partial Regex FsSdkRegex();
[GeneratedRegex(@"SDK MW[ -~]*")]
internal static partial Regex SdkMwRegex();
#endregion
#region CJK
[GeneratedRegex(
"\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")]
private static partial Regex CJKRegex();
#endregion
[GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
private static partial Regex LdnPassphraseRegex();
[GeneratedRegex(@"[^\u0000\u0009\u000A\u000D\u0020-\uFFFF]..")]
private static partial Regex CleanTextRegex();
#endregion
}
}

View File

@ -10,14 +10,18 @@ namespace Ryujinx.Common.Helper
public static bool IsMacOS => OperatingSystem.IsMacOS();
public static bool IsWindows => OperatingSystem.IsWindows();
public static bool IsLinux => OperatingSystem.IsLinux();
public static bool IsArm => RuntimeInformation.OSArchitecture is Architecture.Arm64;
public static bool IsX64 => RuntimeInformation.OSArchitecture is Architecture.X64;
public static bool IsIntelMac => IsMacOS && RuntimeInformation.OSArchitecture is Architecture.X64;
public static bool IsArmMac => IsMacOS && RuntimeInformation.OSArchitecture is Architecture.Arm64;
public static bool IsIntelMac => IsMacOS && IsX64;
public static bool IsArmMac => IsMacOS && IsArm;
public static bool IsX64Windows => IsWindows && (RuntimeInformation.OSArchitecture is Architecture.X64);
public static bool IsArmWindows => IsWindows && (RuntimeInformation.OSArchitecture is Architecture.Arm64);
public static bool IsX64Windows => IsWindows && IsX64;
public static bool IsArmWindows => IsWindows && IsArm;
public static bool IsX64Linux => IsLinux && (RuntimeInformation.OSArchitecture is Architecture.X64);
public static bool IsArmLinux => IsLinux && (RuntimeInformation.OSArchitecture is Architecture.Arm64);
public static bool IsX64Linux => IsLinux && IsX64;
public static bool IsArmLinux => IsLinux && IsArmMac;
}
}

View File

@ -0,0 +1,9 @@
namespace Ryujinx.Common
{
public static class SharedConstants
{
public const string DefaultLanPlayHost = "ryuldn.vudjun.com";
public const short LanPlayPort = 30456;
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
}
}

View File

@ -3,7 +3,6 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper;
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Common
{
@ -11,54 +10,6 @@ namespace Ryujinx.Common
{
public static ReactiveObject<Optional<string>> CurrentApplication { get; } = new();
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
{
switch (currentBackend)
{
case GraphicsBackend.Metal when !OperatingSystem.IsMacOS():
case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS():
return GraphicsBackend.Vulkan;
case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal:
return currentBackend;
}
if (!RunningPlatform.IsArmMac)
return GraphicsBackend.Vulkan;
return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan;
}
public static readonly string[] GreatMetalTitles =
[
"01009b500007c000", // ARMS
"0100a5c00d162000", // Cuphead
"010023800d64a000", // Deltarune
"01003a30012c0000", // LEGO City Undercover
"010048701995e000", // Luigi's Manion 2 HD
"010028600EBDA000", // Mario 3D World
"0100152000022000", // Mario Kart 8 Deluxe
"010075a016a3a000", // Persona 4 Arena Ultimax
"0100187003A36000", // Pokémon: Let's Go, Eevee!
"010003f003a34000", // Pokémon: Let's Go, Pikachu!
"01008C0016544000", // Sea of Stars
"01006A800016E000", // Smash Ultimate
"01006bb00c6f0000", // The Legend of Zelda: Link's Awakening
// These ones have small issues, but those happen on Vulkan as well:
"01006f8002326000", // Animal Crossings: New Horizons
"01009bf0072d4000", // Captain Toad: Treasure Tracker
"01009510001ca000", // Fast RMX
"01005CA01580E000", // Persona 5 Royale
"010015100b514000", // Super Mario Bros. Wonder
"0100000000010000", // Super Mario Odyssey
// Further testing is appreciated, I did not test the entire game:
"01007300020fa000", // Astral Chain
"010076f0049a2000", // Bayonetta
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
"0100f4300bf2c000", // New Pokemon Snap
];
public static string GetDiscordGameAsset(string titleId)
=> DiscordGameAssetKeys.Contains(titleId) ? titleId : "game";
@ -164,15 +115,16 @@ namespace Ryujinx.Common
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
//NSO Membership games
"0100ccf019c8c000", // F-ZERO 99
"0100c62011050000", // GB - Nintendo Switch Online
"010012f017576000", // GBA - Nintendo Switch Online
"0100c9a00ece6000", // N64 - Nintendo Switch Online
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
"0100d870045b6000", // NES - Nintendo Switch Online
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100ccf019c8c000", // F-ZERO 99
"0100ad9012510000", // PAC-MAN 99
"010040600c5ce000", // Tetris 99
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100277011f1a000", // Super Mario Bros. 35
//Misc Nintendo 1st party games
@ -218,6 +170,7 @@ namespace Ryujinx.Common
//Misc Games
"010056e00853a000", // A Hat in Time
"0100fd1014726000", // Baldurs Gate: Dark Alliance
"01008c2019598000", // Bluey: The Video Game
"0100c6800b934000", // Brawlhalla
"0100dbf01000a000", // Burnout Paradise Remastered
"0100744001588000", // Cars 3: Driven to Win
@ -228,6 +181,7 @@ namespace Ryujinx.Common
"01008c8012920000", // Dying Light Platinum Edition
"01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2
"0100f7e00c70e000", // Hogwarts Legacy
"010085500130a000", // Lego City: Undercover
"010073c01af34000", // LEGO Horizon Adventures
"0100d71004694000", // Minecraft

View File

@ -1,6 +1,5 @@
using ARMeilleure.State;
using Ryujinx.Memory;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

View File

@ -5,7 +5,6 @@ using Ryujinx.Memory.Tracking;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

View File

@ -1,5 +1,4 @@
using Ryujinx.Memory;
using System;
using System.Runtime.Versioning;
using System.Threading;

View File

@ -2,7 +2,6 @@ using ARMeilleure.Common;
using ARMeilleure.Memory;
using ARMeilleure.Translation;
using Ryujinx.Cpu.Signal;
using Ryujinx.Memory;
namespace Ryujinx.Cpu.Jit
{

View File

@ -5,7 +5,6 @@ using Ryujinx.Memory.Tracking;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

View File

@ -8,7 +8,6 @@ using Ryujinx.Memory.Tracking;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Ryujinx.Cpu.Jit

View File

@ -3,7 +3,6 @@ using ARMeilleure.Memory;
using Ryujinx.Cpu.LightningJit.Arm32;
using Ryujinx.Cpu.LightningJit.Arm64;
using Ryujinx.Cpu.LightningJit.State;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.LightningJit

View File

@ -2,7 +2,6 @@ using ARMeilleure.Common;
using ARMeilleure.Memory;
using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;

View File

@ -1,5 +1,4 @@
using Ryujinx.Cpu.LightningJit.CodeGen;
using System;
using System.Diagnostics;
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64

View File

@ -1,5 +1,3 @@
using Ryujinx.Cpu.LightningJit.CodeGen;
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
{
static class InstEmitVfpMove

View File

@ -1,5 +1,3 @@
using System.Diagnostics;
namespace Ryujinx.Cpu.LightningJit.Arm64
{
static class SysUtils

View File

@ -3,7 +3,6 @@ using ARMeilleure.Memory;
using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using Ryujinx.Cpu.LightningJit.Graph;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;

View File

@ -1,4 +1,6 @@
using ARMeilleure.Memory;
using Humanizer;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Generic;
@ -15,9 +17,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 2047 * 1024 * 1024;
private const int CacheSize = 256 * 1024 * 1024;
private static ReservedRegion _jitRegion;
private static JitCacheInvalidation _jitCacheInvalidator;
private static CacheMemoryAllocator _cacheAllocator;
@ -26,6 +27,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly Lock _lock = new();
private static bool _initialized;
private static readonly List<ReservedRegion> _jitRegions = [];
private static int _activeRegionIndex = 0;
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)]
@ -45,7 +48,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
return;
}
_jitRegion = new ReservedRegion(allocator, CacheSize);
ReservedRegion firstRegion = new(allocator, CacheSize);
_jitRegions.Add(firstRegion);
_activeRegionIndex = 0;
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
{
@ -65,8 +70,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
Debug.Assert(_initialized);
int funcOffset = Allocate(code.Length);
nint funcPtr = _jitRegion.Pointer + funcOffset;
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
nint funcPtr = targetRegion.Pointer + funcOffset;
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
@ -80,18 +85,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache
}
else
{
ReprotectAsWritable(funcOffset, code.Length);
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
ReprotectAsExecutable(funcOffset, code.Length);
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
Marshal.Copy(code.ToArray(), 0, funcPtr, code.Length);
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
}
else
{
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
}
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
}
Add(funcOffset, code.Length);
@ -106,50 +104,80 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{
Debug.Assert(_initialized);
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
foreach (ReservedRegion region in _jitRegions)
{
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
{
continue;
}
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
{
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
}
return;
}
}
}
private static void ReprotectAsWritable(int offset, int size)
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static void ReprotectAsExecutable(int offset, int size)
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static int Allocate(int codeSize)
{
codeSize = AlignCodeSize(codeSize);
int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset < 0)
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
{
throw new OutOfMemoryException("JIT Cache exhausted.");
int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset >= 0)
{
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
_activeRegionIndex = i;
return allocOffset;
}
}
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
int exhaustedRegion = _activeRegionIndex;
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
_jitRegions.Add(newRegion);
_activeRegionIndex = _jitRegions.Count - 1;
int newRegionNumber = _activeRegionIndex;
return allocOffset;
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
if (allocOffsetNew < 0)
{
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
}
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
return allocOffsetNew;
}
private static int AlignCodeSize(int codeSize)

View File

@ -1,5 +1,4 @@
using ARMeilleure.Memory;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.LightningJit.Cache

View File

@ -1,4 +1,3 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

View File

@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{
private const int CodeAlignment = 4; // Bytes.
private const int SharedCacheSize = 2047 * 1024 * 1024;
private const int LocalCacheSize = 128 * 1024 * 1024;
private const int LocalCacheSize = 256 * 1024 * 1024;
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
// and allow the guest to take the fast path.

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Cpu.LightningJit

View File

@ -1,5 +1,3 @@
using System;
namespace Ryujinx.Cpu.LightningJit
{
class TranslatedFunction

View File

@ -5,7 +5,6 @@ using Ryujinx.Cpu.LightningJit.Cache;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using Ryujinx.Cpu.LightningJit.State;
using Ryujinx.Cpu.Signal;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;

View File

@ -1,4 +1,3 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.Signal

View File

@ -1,6 +1,4 @@
using Ryujinx.Common.Logging;
using System;
using System.Threading;
namespace Ryujinx.Graphics.Device
{

View File

@ -1,18 +0,0 @@
namespace Ryujinx.Graphics.GAL
{
public readonly struct ComputeSize
{
public readonly static ComputeSize VtgAsCompute = new(32, 32, 1);
public readonly int X;
public readonly int Y;
public readonly int Z;
public ComputeSize(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
}
}

View File

@ -339,84 +339,6 @@ namespace Ryujinx.Graphics.GAL
return 1;
}
/// <summary>
/// Get bytes per element for this format.
/// </summary>
/// <param name="format">Texture format</param>
/// <returns>Byte size for an element of this format (pixel, vertex attribute, etc)</returns>
public static int GetBytesPerElement(this Format format)
{
int scalarSize = format.GetScalarSize();
switch (format)
{
case Format.R8G8Unorm:
case Format.R8G8Snorm:
case Format.R8G8Uint:
case Format.R8G8Sint:
case Format.R8G8Uscaled:
case Format.R8G8Sscaled:
case Format.R16G16Float:
case Format.R16G16Unorm:
case Format.R16G16Snorm:
case Format.R16G16Uint:
case Format.R16G16Sint:
case Format.R16G16Uscaled:
case Format.R16G16Sscaled:
case Format.R32G32Float:
case Format.R32G32Uint:
case Format.R32G32Sint:
case Format.R32G32Uscaled:
case Format.R32G32Sscaled:
return 2 * scalarSize;
case Format.R8G8B8Unorm:
case Format.R8G8B8Snorm:
case Format.R8G8B8Uint:
case Format.R8G8B8Sint:
case Format.R8G8B8Uscaled:
case Format.R8G8B8Sscaled:
case Format.R16G16B16Float:
case Format.R16G16B16Unorm:
case Format.R16G16B16Snorm:
case Format.R16G16B16Uint:
case Format.R16G16B16Sint:
case Format.R16G16B16Uscaled:
case Format.R16G16B16Sscaled:
case Format.R32G32B32Float:
case Format.R32G32B32Uint:
case Format.R32G32B32Sint:
case Format.R32G32B32Uscaled:
case Format.R32G32B32Sscaled:
return 3 * scalarSize;
case Format.R8G8B8A8Unorm:
case Format.R8G8B8A8Snorm:
case Format.R8G8B8A8Uint:
case Format.R8G8B8A8Sint:
case Format.R8G8B8A8Srgb:
case Format.R8G8B8A8Uscaled:
case Format.R8G8B8A8Sscaled:
case Format.B8G8R8A8Unorm:
case Format.B8G8R8A8Srgb:
case Format.R16G16B16A16Float:
case Format.R16G16B16A16Unorm:
case Format.R16G16B16A16Snorm:
case Format.R16G16B16A16Uint:
case Format.R16G16B16A16Sint:
case Format.R16G16B16A16Uscaled:
case Format.R16G16B16A16Sscaled:
case Format.R32G32B32A32Float:
case Format.R32G32B32A32Uint:
case Format.R32G32B32A32Sint:
case Format.R32G32B32A32Uscaled:
case Format.R32G32B32A32Sscaled:
return 4 * scalarSize;
}
return scalarSize;
}
/// <summary>
/// Checks if the texture format is a depth or depth-stencil format.
/// </summary>

View File

@ -4,22 +4,23 @@ namespace Ryujinx.Graphics.GAL
{
public int FragmentOutputMap { get; }
public ResourceLayout ResourceLayout { get; }
public ComputeSize ComputeLocalSize { get; }
public ProgramPipelineState? State { get; }
public bool FromCache { get; set; }
public ShaderInfo(
int fragmentOutputMap,
ResourceLayout resourceLayout,
ComputeSize computeLocalSize,
ProgramPipelineState? state,
bool fromCache = false)
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false)
{
FragmentOutputMap = fragmentOutputMap;
ResourceLayout = resourceLayout;
ComputeLocalSize = computeLocalSize;
State = state;
FromCache = fromCache;
}
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false)
{
FragmentOutputMap = fragmentOutputMap;
ResourceLayout = resourceLayout;
State = null;
FromCache = fromCache;
}
}
}

View File

@ -1,6 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender

View File

@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
/// </summary>
class VtgAsComputeContext : IDisposable
{
private const int DummyBufferSize = 16;
private readonly GpuContext _context;
/// <summary>
@ -46,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
1,
1,
1,
format.GetBytesPerElement(),
1,
format,
DepthStencilMode.Depth,
Target.TextureBuffer,
@ -519,6 +521,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
}
/// <summary>
/// Gets the range for a dummy 16 bytes buffer, filled with zeros.
/// </summary>
/// <returns>Dummy buffer range</returns>
public BufferRange GetDummyBufferRange()
{
if (_dummyBuffer == BufferHandle.Null)
{
_dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory);
_context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0);
}
return new BufferRange(_dummyBuffer, 0, DummyBufferSize);
}
/// <summary>
/// Gets the range for a sequential index buffer, with ever incrementing index values.
/// </summary>

View File

@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
continue;
}
@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
continue;
}
int vbStride = vertexBuffer.UnpackStride();
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
ulong oldVbSize = vbSize;
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
int componentSize = format.GetScalarSize();
@ -340,6 +344,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
return maxOutputVertices / verticesPerPrimitive;
}
/// <summary>
/// Binds a dummy buffer as vertex buffer into a buffer texture.
/// </summary>
/// <param name="reservations">Shader resource binding reservations</param>
/// <param name="index">Buffer texture index</param>
/// <param name="format">Buffer texture format</param>
private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format)
{
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
bufferTexture.SetStorage(_vacContext.GetDummyBufferRange());
_context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null);
}
/// <summary>
/// Binds a vertex buffer into a buffer texture.
/// </summary>

View File

@ -913,7 +913,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Span<Rectangle<int>> scissors =
[
new Rectangle<int>(scissorX, scissorY, scissorW, scissorH)
new(scissorX, scissorY, scissorW, scissorH)
];
_context.Renderer.Pipeline.SetScissors(scissors);

View File

@ -1,4 +1,3 @@
using Ryujinx.Graphics.Device;
using System;
using System.Collections.Generic;
using System.Diagnostics;

View File

@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Image

View File

@ -1,5 +1,4 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using System;

View File

@ -7,7 +7,6 @@ using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

View File

@ -1,6 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

View File

@ -2,7 +2,6 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader;
using System;
using System.Linq;
namespace Ryujinx.Graphics.Gpu.Shader

View File

@ -324,11 +324,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
bool loadHostCache = header.CodeGenVersion == CodeGenVersion;
if (context.Capabilities.Api == TargetApi.Metal)
{
loadHostCache = false;
}
int programIndex = 0;
DataEntry entry = new();
@ -397,8 +392,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
context,
shaders,
specState.PipelineState,
specState.TransformFeedbackDescriptors != null,
specState.ComputeState.GetLocalSize());
specState.TransformFeedbackDescriptors != null);
IProgram hostProgram;
@ -635,10 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return;
}
if (context.Capabilities.Api != TargetApi.Metal)
{
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
}
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
}
/// <summary>

View File

@ -1,4 +1,3 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
@ -367,9 +366,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
try
{
if (_context.Capabilities.Api == TargetApi.Metal && _context.DirtyHacks.IsEnabled(DirtyHack.ShaderTranslationDelay))
Thread.Sleep(_context.DirtyHacks[DirtyHack.ShaderTranslationDelay]);
AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute);
_asyncTranslationQueue.Add(asyncTranslation, _cancellationToken);
}
@ -494,12 +490,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
ref GpuChannelComputeState computeState = ref compilation.SpecializationState.ComputeState;
ShaderInfoBuilder shaderInfoBuilder = new(
_context,
compilation.SpecializationState.TransformFeedbackDescriptors != null,
computeLocalSize: computeState.GetLocalSize());
ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null);
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
{
@ -729,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
ShaderProgram program = translatorContext.Translate();
CachedShaderStage[] shaders = [new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data)];
CachedShaderStage[] shaders = [new(program.Info, shader.Code, shader.Cb1Data)];
_compilationQueue.Enqueue(new ProgramCompilation([program], shaders, newSpecState, programIndex, isCompute: true));
}

View File

@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly GpuAccessorState _state;
private readonly int _stageIndex;
private readonly bool _compute;
private readonly bool _isOpenGL;
private readonly bool _isVulkan;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;
@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
_channel = channel;
_state = state;
_stageIndex = stageIndex;
_isOpenGL = context.Capabilities.Api == TargetApi.OpenGL;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;
@ -117,10 +117,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
public GpuGraphicsState QueryGraphicsState()
{
return _state.GraphicsState.CreateShaderGraphicsState(
_isOpenGL,
!_isVulkan,
_supportsQuads,
_hasGeometryShader,
!_isOpenGL || _state.GraphicsState.YNegateEnabled);
_isVulkan || _state.GraphicsState.YNegateEnabled);
}
/// <inheritdoc/>

View File

@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int binding;
if (_context.Capabilities.Api != TargetApi.OpenGL)
if (_context.Capabilities.Api == TargetApi.Vulkan)
{
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
}
@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int binding;
if (_context.Capabilities.Api != TargetApi.OpenGL)
if (_context.Capabilities.Api == TargetApi.Vulkan)
{
if (count == 1)
{
@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int binding;
if (_context.Capabilities.Api != TargetApi.OpenGL)
if (_context.Capabilities.Api == TargetApi.Vulkan)
{
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
}
@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int binding;
if (_context.Capabilities.Api != TargetApi.OpenGL)
if (_context.Capabilities.Api == TargetApi.Vulkan)
{
if (count == 1)
{

View File

@ -1,5 +1,3 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
@ -63,14 +61,5 @@ namespace Ryujinx.Graphics.Gpu.Shader
SharedMemorySize = sharedMemorySize;
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
}
/// <summary>
/// Gets the local group size of the shader in a GAL compatible struct.
/// </summary>
/// <returns>Local group size</returns>
public ComputeSize GetLocalSize()
{
return new ComputeSize(LocalSizeX, LocalSizeY, LocalSizeZ);
}
}
}

View File

@ -1,4 +1,3 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
@ -118,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private static string GetDiskCachePath()
{
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId.ToLower(), "cache", "shader")
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
: null;
}
@ -205,7 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
GpuChannelComputeState computeState,
ulong gpuVa)
{
if (_cpPrograms.TryGetValue(gpuVa, out CachedShaderProgram cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
{
return cpShader;
}
@ -224,11 +223,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false);
ShaderSource[] shaderSourcesArray = [CreateShaderSource(translatedShader.Program)];
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(
_context,
translatedShader.Program.Info,
computeState.GetLocalSize());
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
@ -255,8 +251,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
channel.TextureManager.UpdateRenderTargets();
RtControl rtControl = state.RtControl;
TextureMsaaMode msaaMode = state.RtMsaaMode;
var rtControl = state.RtControl;
var msaaMode = state.RtMsaaMode;
pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
@ -266,7 +262,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int rtIndex = rtControl.UnpackPermutationIndex(index);
RtColorState colorState = state.RtColorState[rtIndex];
var colorState = state.RtColorState[rtIndex];
if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
{
@ -311,12 +307,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
ref GpuChannelGraphicsState graphicsState,
ShaderAddresses addresses)
{
if (_gpPrograms.TryGetValue(addresses, out CachedShaderProgram gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
{
return gpShaders;
}
if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out CachedGraphicsGuestCode cachedGuestCode))
if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode))
{
_gpPrograms[addresses] = gpShaders;
return gpShaders;
@ -369,7 +365,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
bool geometryToCompute = ShouldConvertGeometryToCompute(_context, geometryHasStore);
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
List<ShaderSource> shaderSources = [];
List<ShaderSource> shaderSources = new();
TranslatorContext previousStage = null;
ShaderInfoBuilder infoBuilder = new(_context, transformFeedbackDescriptors != null, vertexToCompute);
@ -429,8 +425,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage;
(program, ShaderProgramInfo vacInfo) = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
infoBuilder.AddStageInfoVac(vacInfo);
program = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
}
else
{
@ -535,9 +530,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled)
{
ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language);
ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, context.GetVertexAsComputeInfo(), tfEnabled);
ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, tfEnabled);
return new(_context.Renderer.CreateProgram([source], info), program.Info, context.GetResourceReservations());
return new(_context.Renderer.CreateProgram(new[] { source }, info), program.Info, context.GetResourceReservations());
}
/// <summary>
@ -587,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
{
TfState tf = state.TfState[i];
var tf = state.TfState[i];
descs[i] = new TransformFeedbackDescriptor(
tf.BufferIndex,
@ -693,7 +688,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>The generated translator context</returns>
public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
{
TranslationOptions options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
return Translator.CreateContext(gpuVa, gpuAccessor, options);
}
@ -710,7 +705,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>The generated translator context</returns>
public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
{
TranslationOptions options = CreateTranslationOptions(api, flags);
var options = CreateTranslationOptions(api, flags);
return Translator.CreateContext(gpuVa, gpuAccessor, options);
}
@ -736,7 +731,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
MemoryManager memoryManager = channel.MemoryManager;
var memoryManager = channel.MemoryManager;
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
@ -774,7 +769,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Compiled graphics shader code</returns>
private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code, bool asCompute)
{
MemoryManager memoryManager = channel.MemoryManager;
var memoryManager = channel.MemoryManager;
ulong cb1DataAddress = context.Stage == ShaderStage.Compute
? channel.BufferManager.GetComputeUniformBufferAddress(1)
@ -802,7 +797,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
if (address == MemoryManager.PteUnmapped || size == 0)
{
return [];
return Array.Empty<byte>();
}
return memoryManager.Physical.GetSpan(address, size).ToArray();
@ -827,20 +822,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Creates shader translation options with the requested graphics API and flags.
/// The shader language is chosen based on the current configuration and graphics API.
/// The shader language is choosen based on the current configuration and graphics API.
/// </summary>
/// <param name="api">Target graphics API</param>
/// <param name="flags">Translation flags</param>
/// <returns>Translation options</returns>
private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags)
{
TargetLanguage lang = api switch
{
TargetApi.OpenGL => TargetLanguage.Glsl,
TargetApi.Vulkan => GraphicsConfig.EnableSpirvCompilationOnVulkan ? TargetLanguage.Spirv : TargetLanguage.Glsl,
TargetApi.Metal => TargetLanguage.Msl,
_ => throw new NotImplementedException()
};
TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan
? TargetLanguage.Spirv
: TargetLanguage.Glsl;
return new TranslationOptions(lang, api, flags);
}

View File

@ -22,7 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
ResourceStages.Geometry;
private readonly GpuContext _context;
private readonly ComputeSize _computeLocalSize;
private int _fragmentOutputMap;
@ -40,11 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
/// <param name="vertexAsCompute">Indicates that the vertex shader will be emulated on a compute shader</param>
/// <param name="computeLocalSize">Indicates the local thread size for a compute shader</param>
public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false, ComputeSize computeLocalSize = default)
public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false)
{
_context = context;
_computeLocalSize = computeLocalSize;
_fragmentOutputMap = -1;
@ -98,7 +95,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
{
AddDescriptor(stages, type, setIndex, start, count);
// AddUsage(stages, type, setIndex, start, count, write);
AddUsage(stages, type, setIndex, start, count, write);
}
/// <summary>
@ -162,25 +159,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
AddUsage(info.Images, stages, isImage: true);
}
public void AddStageInfoVac(ShaderProgramInfo info)
{
ResourceStages stages = info.Stage switch
{
ShaderStage.Compute => ResourceStages.Compute,
ShaderStage.Vertex => ResourceStages.Vertex,
ShaderStage.TessellationControl => ResourceStages.TessellationControl,
ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
ShaderStage.Geometry => ResourceStages.Geometry,
ShaderStage.Fragment => ResourceStages.Fragment,
_ => ResourceStages.None,
};
AddUsage(info.CBuffers, stages, isStorage: false);
AddUsage(info.SBuffers, stages, isStorage: true);
AddUsage(info.Textures, stages, isImage: false);
AddUsage(info.Images, stages, isImage: true);
}
/// <summary>
/// Adds a resource descriptor to the list of descriptors.
/// </summary>
@ -383,7 +361,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly());
return new ShaderInfo(_fragmentOutputMap, resourceLayout, _computeLocalSize, pipeline, fromCache);
if (pipeline.HasValue)
{
return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
}
else
{
return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
}
}
/// <summary>
@ -393,16 +378,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="programs">Shaders from the disk cache</param>
/// <param name="pipeline">Optional pipeline for background compilation</param>
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
/// <param name="computeLocalSize">Compute local thread size</param>
/// <returns>Shader information</returns>
public static ShaderInfo BuildForCache(
GpuContext context,
IEnumerable<CachedShaderStage> programs,
ProgramPipelineState? pipeline,
bool tfEnabled,
ComputeSize computeLocalSize)
bool tfEnabled)
{
ShaderInfoBuilder builder = new(context, tfEnabled, computeLocalSize: computeLocalSize);
ShaderInfoBuilder builder = new(context, tfEnabled);
foreach (CachedShaderStage program in programs)
{
@ -420,12 +403,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
/// <param name="context">GPU context that owns the shader</param>
/// <param name="info">Compute shader information</param>
/// <param name="computeLocalSize">Compute local thread size</param>
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
/// <returns>Shader information</returns>
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, ComputeSize computeLocalSize, bool fromCache = false)
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
{
ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false, computeLocalSize: computeLocalSize);
ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false);
builder.AddStageInfo(info);
@ -440,11 +422,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
/// <returns>Shader information</returns>
public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, ShaderProgramInfo info2, bool tfEnabled, bool fromCache = false)
public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false)
{
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute);
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true);
builder.AddStageInfoVac(info2);
builder.AddStageInfo(info, vertexAsCompute: true);
return builder.Build(null, fromCache);

View File

@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
}
using ManualResetEvent waitEvent = new(false);
SyncpointWaiterHandle info = _syncpoints[id].RegisterCallback(threshold, (x) => waitEvent.Set());
SyncpointWaiterHandle info = _syncpoints[id].RegisterCallback(threshold, _ => waitEvent.Set());
if (info == null)
{
@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
bool signaled = waitEvent.WaitOne(timeout);
if (!signaled && info != null)
if (!signaled)
{
Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution...");

View File

@ -1,146 +0,0 @@
using System;
using System.Diagnostics;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
interface IAuto
{
bool HasCommandBufferDependency(CommandBufferScoped cbs);
void IncrementReferenceCount();
void DecrementReferenceCount(int cbIndex);
void DecrementReferenceCount();
}
interface IAutoPrivate : IAuto
{
void AddCommandBufferDependencies(CommandBufferScoped cbs);
}
[SupportedOSPlatform("macos")]
class Auto<T> : IAutoPrivate, IDisposable where T : IDisposable
{
private int _referenceCount;
private T _value;
private readonly BitMap _cbOwnership;
private readonly MultiFenceHolder _waitable;
private bool _disposed;
private bool _destroyed;
public Auto(T value)
{
_referenceCount = 1;
_value = value;
_cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers);
}
public Auto(T value, MultiFenceHolder waitable) : this(value)
{
_waitable = waitable;
}
public T Get(CommandBufferScoped cbs, int offset, int size, bool write = false)
{
_waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, write);
return Get(cbs);
}
public T GetUnsafe()
{
return _value;
}
public T Get(CommandBufferScoped cbs)
{
if (!_destroyed)
{
AddCommandBufferDependencies(cbs);
}
return _value;
}
public bool HasCommandBufferDependency(CommandBufferScoped cbs)
{
return _cbOwnership.IsSet(cbs.CommandBufferIndex);
}
public bool HasRentedCommandBufferDependency(CommandBufferPool cbp)
{
return _cbOwnership.AnySet();
}
public void AddCommandBufferDependencies(CommandBufferScoped cbs)
{
// We don't want to add a reference to this object to the command buffer
// more than once, so if we detect that the command buffer already has ownership
// of this object, then we can just return without doing anything else.
if (_cbOwnership.Set(cbs.CommandBufferIndex))
{
if (_waitable != null)
{
cbs.AddWaitable(_waitable);
}
cbs.AddDependant(this);
}
}
public bool TryIncrementReferenceCount()
{
int lastValue;
do
{
lastValue = _referenceCount;
if (lastValue == 0)
{
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
return true;
}
public void IncrementReferenceCount()
{
if (Interlocked.Increment(ref _referenceCount) == 1)
{
Interlocked.Decrement(ref _referenceCount);
throw new InvalidOperationException("Attempted to increment the reference count of an object that was already destroyed.");
}
}
public void DecrementReferenceCount(int cbIndex)
{
_cbOwnership.Clear(cbIndex);
DecrementReferenceCount();
}
public void DecrementReferenceCount()
{
if (Interlocked.Decrement(ref _referenceCount) == 0)
{
_value.Dispose();
_value = default;
_destroyed = true;
}
Debug.Assert(_referenceCount >= 0);
}
public void Dispose()
{
if (!_disposed)
{
DecrementReferenceCount();
_disposed = true;
}
}
}
}

View File

@ -1,107 +0,0 @@
using SharpMetal.Metal;
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class BackgroundResource : IDisposable
{
private readonly MetalRenderer _renderer;
private CommandBufferPool _pool;
private PersistentFlushBuffer _flushBuffer;
public BackgroundResource(MetalRenderer renderer)
{
_renderer = renderer;
}
public CommandBufferPool GetPool()
{
if (_pool == null)
{
MTLCommandQueue queue = _renderer.BackgroundQueue;
_pool = new CommandBufferPool(queue, true);
_pool.Initialize(null); // TODO: Proper encoder factory for background render/compute
}
return _pool;
}
public PersistentFlushBuffer GetFlushBuffer()
{
_flushBuffer ??= new PersistentFlushBuffer(_renderer);
return _flushBuffer;
}
public void Dispose()
{
_pool?.Dispose();
_flushBuffer?.Dispose();
}
}
[SupportedOSPlatform("macos")]
class BackgroundResources : IDisposable
{
private readonly MetalRenderer _renderer;
private readonly Dictionary<Thread, BackgroundResource> _resources;
public BackgroundResources(MetalRenderer renderer)
{
_renderer = renderer;
_resources = new Dictionary<Thread, BackgroundResource>();
}
private void Cleanup()
{
lock (_resources)
{
foreach (KeyValuePair<Thread, BackgroundResource> tuple in _resources)
{
if (!tuple.Key.IsAlive)
{
tuple.Value.Dispose();
_resources.Remove(tuple.Key);
}
}
}
}
public BackgroundResource Get()
{
Thread thread = Thread.CurrentThread;
lock (_resources)
{
if (!_resources.TryGetValue(thread, out BackgroundResource resource))
{
Cleanup();
resource = new BackgroundResource(_renderer);
_resources[thread] = resource;
}
return resource;
}
}
public void Dispose()
{
lock (_resources)
{
foreach (BackgroundResource resource in _resources.Values)
{
resource.Dispose();
}
}
}
}
}

View File

@ -1,157 +0,0 @@
namespace Ryujinx.Graphics.Metal
{
readonly struct BitMap
{
public const int IntSize = 64;
private const int IntShift = 6;
private const int IntMask = IntSize - 1;
private readonly long[] _masks;
public BitMap(int count)
{
_masks = new long[(count + IntMask) / IntSize];
}
public bool AnySet()
{
for (int i = 0; i < _masks.Length; i++)
{
if (_masks[i] != 0)
{
return true;
}
}
return false;
}
public bool IsSet(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
return (_masks[wordIndex] & wordMask) != 0;
}
public bool IsSet(int start, int end)
{
if (start == end)
{
return IsSet(start);
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
if (startIndex == endIndex)
{
return (_masks[startIndex] & startMask & endMask) != 0;
}
if ((_masks[startIndex] & startMask) != 0)
{
return true;
}
for (int i = startIndex + 1; i < endIndex; i++)
{
if (_masks[i] != 0)
{
return true;
}
}
if ((_masks[endIndex] & endMask) != 0)
{
return true;
}
return false;
}
public bool Set(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
if ((_masks[wordIndex] & wordMask) != 0)
{
return false;
}
_masks[wordIndex] |= wordMask;
return true;
}
public void SetRange(int start, int end)
{
if (start == end)
{
Set(start);
return;
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
if (startIndex == endIndex)
{
_masks[startIndex] |= startMask & endMask;
}
else
{
_masks[startIndex] |= startMask;
for (int i = startIndex + 1; i < endIndex; i++)
{
_masks[i] |= -1;
}
_masks[endIndex] |= endMask;
}
}
public void Clear(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
_masks[wordIndex] &= ~wordMask;
}
public void Clear()
{
for (int i = 0; i < _masks.Length; i++)
{
_masks[i] = 0;
}
}
public void ClearInt(int start, int end)
{
for (int i = start; i <= end; i++)
{
_masks[i] = 0;
}
}
}
}

View File

@ -1,385 +0,0 @@
using Ryujinx.Graphics.GAL;
using SharpMetal.Metal;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class BufferHolder : IDisposable
{
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
public int Size { get; }
private readonly IntPtr _map;
private readonly MetalRenderer _renderer;
private readonly Pipeline _pipeline;
private readonly MultiFenceHolder _waitable;
private readonly Auto<DisposableBuffer> _buffer;
private readonly ReaderWriterLockSlim _flushLock;
private FenceHolder _flushFence;
private int _flushWaiting;
public BufferHolder(MetalRenderer renderer, Pipeline pipeline, MTLBuffer buffer, int size)
{
_renderer = renderer;
_pipeline = pipeline;
_map = buffer.Contents;
_waitable = new MultiFenceHolder(size);
_buffer = new Auto<DisposableBuffer>(new(buffer), _waitable);
_flushLock = new ReaderWriterLockSlim();
Size = size;
}
public Auto<DisposableBuffer> GetBuffer()
{
return _buffer;
}
public Auto<DisposableBuffer> GetBuffer(bool isWrite)
{
if (isWrite)
{
SignalWrite(0, Size);
}
return _buffer;
}
public Auto<DisposableBuffer> GetBuffer(int offset, int size, bool isWrite)
{
if (isWrite)
{
SignalWrite(offset, size);
}
return _buffer;
}
public void SignalWrite(int offset, int size)
{
if (offset == 0 && size == Size)
{
_cachedConvertedBuffers.Clear();
}
else
{
_cachedConvertedBuffers.ClearRange(offset, size);
}
}
private void ClearFlushFence()
{
// Assumes _flushLock is held as writer.
if (_flushFence != null)
{
if (_flushWaiting == 0)
{
_flushFence.Put();
}
_flushFence = null;
}
}
private void WaitForFlushFence()
{
if (_flushFence == null)
{
return;
}
// If storage has changed, make sure the fence has been reached so that the data is in place.
_flushLock.ExitReadLock();
_flushLock.EnterWriteLock();
if (_flushFence != null)
{
FenceHolder fence = _flushFence;
Interlocked.Increment(ref _flushWaiting);
// Don't wait in the lock.
_flushLock.ExitWriteLock();
fence.Wait();
_flushLock.EnterWriteLock();
if (Interlocked.Decrement(ref _flushWaiting) == 0)
{
fence.Put();
}
_flushFence = null;
}
// Assumes the _flushLock is held as reader, returns in same state.
_flushLock.ExitWriteLock();
_flushLock.EnterReadLock();
}
public PinnedSpan<byte> GetData(int offset, int size)
{
_flushLock.EnterReadLock();
WaitForFlushFence();
Span<byte> result;
if (_map != IntPtr.Zero)
{
result = GetDataStorage(offset, size);
// Need to be careful here, the buffer can't be unmapped while the data is being used.
_buffer.IncrementReferenceCount();
_flushLock.ExitReadLock();
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
}
throw new InvalidOperationException("The buffer is not mapped");
}
public unsafe Span<byte> GetDataStorage(int offset, int size)
{
int mappingSize = Math.Min(size, Size - offset);
if (_map != IntPtr.Zero)
{
return new Span<byte>((void*)(_map + offset), mappingSize);
}
throw new InvalidOperationException("The buffer is not mapped.");
}
public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, bool allowCbsWait = true)
{
int dataSize = Math.Min(data.Length, Size - offset);
if (dataSize == 0)
{
return;
}
if (_map != IntPtr.Zero)
{
// If persistently mapped, set the data directly if the buffer is not currently in use.
bool isRented = _buffer.HasRentedCommandBufferDependency(_renderer.CommandBufferPool);
// If the buffer is rented, take a little more time and check if the use overlaps this handle.
bool needsFlush = isRented && _waitable.IsBufferRangeInUse(offset, dataSize, false);
if (!needsFlush)
{
WaitForFences(offset, dataSize);
data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
SignalWrite(offset, dataSize);
return;
}
}
if (cbs != null &&
cbs.Value.Encoders.CurrentEncoderType == EncoderType.Render &&
!(_buffer.HasCommandBufferDependency(cbs.Value) &&
_waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
{
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
// This avoids ending and beginning render passes on each buffer data upload.
cbs = _pipeline.GetPreloadCommandBuffer();
}
if (allowCbsWait)
{
_renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, this, offset, data);
}
else
{
bool rentCbs = cbs == null;
if (rentCbs)
{
cbs = _renderer.CommandBufferPool.Rent();
}
if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, this, offset, data))
{
// Need to do a slow upload.
BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize);
srcHolder.SetDataUnchecked(0, data);
Auto<DisposableBuffer> srcBuffer = srcHolder.GetBuffer();
Auto<DisposableBuffer> dstBuffer = this.GetBuffer(true);
Copy(cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize);
srcHolder.Dispose();
}
if (rentCbs)
{
cbs.Value.Dispose();
}
}
}
public unsafe void SetDataUnchecked(int offset, ReadOnlySpan<byte> data)
{
int dataSize = Math.Min(data.Length, Size - offset);
if (dataSize == 0)
{
return;
}
if (_map != IntPtr.Zero)
{
data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
}
}
public void SetDataUnchecked<T>(int offset, ReadOnlySpan<T> data) where T : unmanaged
{
SetDataUnchecked(offset, MemoryMarshal.AsBytes(data));
}
public static void Copy(
CommandBufferScoped cbs,
Auto<DisposableBuffer> src,
Auto<DisposableBuffer> dst,
int srcOffset,
int dstOffset,
int size,
bool registerSrcUsage = true)
{
MTLBuffer srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
MTLBuffer dstbuffer = dst.Get(cbs, dstOffset, size, true).Value;
cbs.Encoders.EnsureBlitEncoder().CopyFromBuffer(
srcBuffer,
(ulong)srcOffset,
dstbuffer,
(ulong)dstOffset,
(ulong)size);
}
public void WaitForFences()
{
_waitable.WaitForFences();
}
public void WaitForFences(int offset, int size)
{
_waitable.WaitForFences(offset, size);
}
private bool BoundToRange(int offset, ref int size)
{
if (offset >= Size)
{
return false;
}
size = Math.Min(Size - offset, size);
return true;
}
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
{
if (!BoundToRange(offset, ref size))
{
return null;
}
I8ToI16CacheKey key = new(_renderer);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder))
{
holder = _renderer.BufferManager.Create((size * 2 + 3) & ~3);
_renderer.HelperShader.ConvertI8ToI16(cbs, this, holder, offset, size);
key.SetBuffer(holder.GetBuffer());
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
}
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
if (!BoundToRange(offset, ref size))
{
return null;
}
TopologyConversionCacheKey key = new(_renderer, pattern, indexSize);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder))
{
// The destination index size is always I32.
int indexCount = size / indexSize;
int convertedCount = pattern.GetConvertedCount(indexCount);
holder = _renderer.BufferManager.Create(convertedCount * 4);
_renderer.HelperShader.ConvertIndexBuffer(cbs, this, holder, pattern, indexSize, offset, indexCount);
key.SetBuffer(holder.GetBuffer());
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
}
public bool TryGetCachedConvertedBuffer(int offset, int size, ICacheKey key, out BufferHolder holder)
{
return _cachedConvertedBuffers.TryGetValue(offset, size, key, out holder);
}
public void AddCachedConvertedBuffer(int offset, int size, ICacheKey key, BufferHolder holder)
{
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
public void AddCachedConvertedBufferDependency(int offset, int size, ICacheKey key, Dependency dependency)
{
_cachedConvertedBuffers.AddDependency(offset, size, key, dependency);
}
public void RemoveCachedConvertedBuffer(int offset, int size, ICacheKey key)
{
_cachedConvertedBuffers.Remove(offset, size, key);
}
public void Dispose()
{
_pipeline.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
_buffer.Dispose();
_cachedConvertedBuffers.Dispose();
_flushLock.EnterWriteLock();
ClearFlushFence();
_flushLock.ExitWriteLock();
}
}
}

View File

@ -1,237 +0,0 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using SharpMetal.Metal;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
readonly struct ScopedTemporaryBuffer : IDisposable
{
private readonly BufferManager _bufferManager;
private readonly bool _isReserved;
public readonly BufferRange Range;
public readonly BufferHolder Holder;
public BufferHandle Handle => Range.Handle;
public int Offset => Range.Offset;
public ScopedTemporaryBuffer(BufferManager bufferManager, BufferHolder holder, BufferHandle handle, int offset, int size, bool isReserved)
{
_bufferManager = bufferManager;
Range = new BufferRange(handle, offset, size);
Holder = holder;
_isReserved = isReserved;
}
public void Dispose()
{
if (!_isReserved)
{
_bufferManager.Delete(Range.Handle);
}
}
}
[SupportedOSPlatform("macos")]
class BufferManager : IDisposable
{
private readonly IdList<BufferHolder> _buffers;
private readonly MTLDevice _device;
private readonly MetalRenderer _renderer;
private readonly Pipeline _pipeline;
public int BufferCount { get; private set; }
public StagingBuffer StagingBuffer { get; }
public BufferManager(MTLDevice device, MetalRenderer renderer, Pipeline pipeline)
{
_device = device;
_renderer = renderer;
_pipeline = pipeline;
_buffers = new IdList<BufferHolder>();
StagingBuffer = new StagingBuffer(_renderer, this);
}
public BufferHandle Create(nint pointer, int size)
{
// TODO: This is the wrong Metal method, we need no-copy which SharpMetal isn't giving us.
MTLBuffer buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared);
if (buffer == IntPtr.Zero)
{
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}, and pointer 0x{pointer:X}.");
return BufferHandle.Null;
}
BufferHolder holder = new(_renderer, _pipeline, buffer, size);
BufferCount++;
ulong handle64 = (uint)_buffers.Add(holder);
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
public BufferHandle CreateWithHandle(int size)
{
return CreateWithHandle(size, out _);
}
public BufferHandle CreateWithHandle(int size, out BufferHolder holder)
{
holder = Create(size);
if (holder == null)
{
return BufferHandle.Null;
}
BufferCount++;
ulong handle64 = (uint)_buffers.Add(holder);
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
public ScopedTemporaryBuffer ReserveOrCreate(CommandBufferScoped cbs, int size)
{
StagingBufferReserved? result = StagingBuffer.TryReserveData(cbs, size);
if (result.HasValue)
{
return new ScopedTemporaryBuffer(this, result.Value.Buffer, StagingBuffer.Handle, result.Value.Offset, result.Value.Size, true);
}
else
{
// Create a temporary buffer.
BufferHandle handle = CreateWithHandle(size, out BufferHolder holder);
return new ScopedTemporaryBuffer(this, holder, handle, 0, size, false);
}
}
public BufferHolder Create(int size)
{
MTLBuffer buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared);
if (buffer != IntPtr.Zero)
{
return new BufferHolder(_renderer, _pipeline, buffer, size);
}
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}.");
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, bool isWrite, out int size)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
size = holder.Size;
return holder.GetBuffer(isWrite);
}
size = 0;
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, int offset, int size, bool isWrite)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetBuffer(offset, size, isWrite);
}
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, bool isWrite)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetBuffer(isWrite);
}
return null;
}
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetBufferI8ToI16(cbs, offset, size);
}
return null;
}
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
}
return null;
}
public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetData(offset, size);
}
return new PinnedSpan<byte>();
}
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
{
SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null);
}
public void SetData(BufferHandle handle, int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
holder.SetData(offset, data, cbs);
}
}
public void Delete(BufferHandle handle)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
holder.Dispose();
_buffers.Remove((int)Unsafe.As<BufferHandle, ulong>(ref handle));
}
}
private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder)
{
return _buffers.TryGetValue((int)Unsafe.As<BufferHandle, ulong>(ref handle), out holder);
}
public void Dispose()
{
StagingBuffer.Dispose();
foreach (BufferHolder buffer in _buffers)
{
buffer.Dispose();
}
}
}
}

View File

@ -1,85 +0,0 @@
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
internal class BufferUsageBitmap
{
private readonly BitMap _bitmap;
private readonly int _size;
private readonly int _granularity;
private readonly int _bits;
private readonly int _writeBitOffset;
private readonly int _intsPerCb;
private readonly int _bitsPerCb;
public BufferUsageBitmap(int size, int granularity)
{
_size = size;
_granularity = granularity;
// There are two sets of bits - one for read tracking, and the other for write.
int bits = (size + (granularity - 1)) / granularity;
_writeBitOffset = bits;
_bits = bits << 1;
_intsPerCb = (_bits + (BitMap.IntSize - 1)) / BitMap.IntSize;
_bitsPerCb = _intsPerCb * BitMap.IntSize;
_bitmap = new BitMap(_bitsPerCb * CommandBufferPool.MaxCommandBuffers);
}
public void Add(int cbIndex, int offset, int size, bool write)
{
if (size == 0)
{
return;
}
// Some usages can be out of bounds (vertex buffer on amd), so bound if necessary.
if (offset + size > _size)
{
size = _size - offset;
}
int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0);
int start = cbBase + offset / _granularity;
int end = cbBase + (offset + size - 1) / _granularity;
_bitmap.SetRange(start, end);
}
public bool OverlapsWith(int cbIndex, int offset, int size, bool write = false)
{
if (size == 0)
{
return false;
}
int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0);
int start = cbBase + offset / _granularity;
int end = cbBase + (offset + size - 1) / _granularity;
return _bitmap.IsSet(start, end);
}
public bool OverlapsWith(int offset, int size, bool write)
{
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
{
if (OverlapsWith(i, offset, size, write))
{
return true;
}
}
return false;
}
public void Clear(int cbIndex)
{
_bitmap.ClearInt(cbIndex * _intsPerCb, (cbIndex + 1) * _intsPerCb - 1);
}
}
}

View File

@ -1,294 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
interface ICacheKey : IDisposable
{
bool KeyEqual(ICacheKey other);
}
[SupportedOSPlatform("macos")]
struct I8ToI16CacheKey : ICacheKey
{
// Used to notify the pipeline that bindings have invalidated on dispose.
// private readonly MetalRenderer _renderer;
// private Auto<DisposableBuffer> _buffer;
public I8ToI16CacheKey(MetalRenderer renderer)
{
// _renderer = renderer;
// _buffer = null;
}
public readonly bool KeyEqual(ICacheKey other)
{
return other is I8ToI16CacheKey;
}
public readonly void SetBuffer(Auto<DisposableBuffer> buffer)
{
// _buffer = buffer;
}
public readonly void Dispose()
{
// TODO: Tell pipeline buffer is dirty!
// _renderer.PipelineInternal.DirtyIndexBuffer(_buffer);
}
}
[SupportedOSPlatform("macos")]
readonly struct TopologyConversionCacheKey : ICacheKey
{
private readonly IndexBufferPattern _pattern;
private readonly int _indexSize;
// Used to notify the pipeline that bindings have invalidated on dispose.
// private readonly MetalRenderer _renderer;
// private Auto<DisposableBuffer> _buffer;
public TopologyConversionCacheKey(MetalRenderer renderer, IndexBufferPattern pattern, int indexSize)
{
// _renderer = renderer;
// _buffer = null;
_pattern = pattern;
_indexSize = indexSize;
}
public readonly bool KeyEqual(ICacheKey other)
{
return other is TopologyConversionCacheKey entry &&
entry._pattern == _pattern &&
entry._indexSize == _indexSize;
}
public void SetBuffer(Auto<DisposableBuffer> buffer)
{
// _buffer = buffer;
}
public readonly void Dispose()
{
// TODO: Tell pipeline buffer is dirty!
// _renderer.PipelineInternal.DirtyVertexBuffer(_buffer);
}
}
[SupportedOSPlatform("macos")]
readonly struct Dependency
{
private readonly BufferHolder _buffer;
private readonly int _offset;
private readonly int _size;
private readonly ICacheKey _key;
public Dependency(BufferHolder buffer, int offset, int size, ICacheKey key)
{
_buffer = buffer;
_offset = offset;
_size = size;
_key = key;
}
public void RemoveFromOwner()
{
_buffer.RemoveCachedConvertedBuffer(_offset, _size, _key);
}
}
[SupportedOSPlatform("macos")]
struct CacheByRange<T> where T : IDisposable
{
private struct Entry
{
public readonly ICacheKey Key;
public readonly T Value;
public List<Dependency> DependencyList;
public Entry(ICacheKey key, T value)
{
Key = key;
Value = value;
DependencyList = null;
}
public readonly void InvalidateDependencies()
{
if (DependencyList != null)
{
foreach (Dependency dependency in DependencyList)
{
dependency.RemoveFromOwner();
}
DependencyList.Clear();
}
}
}
private Dictionary<ulong, List<Entry>> _ranges;
public void Add(int offset, int size, ICacheKey key, T value)
{
List<Entry> entries = GetEntries(offset, size);
entries.Add(new Entry(key, value));
}
public void AddDependency(int offset, int size, ICacheKey key, Dependency dependency)
{
List<Entry> entries = GetEntries(offset, size);
for (int i = 0; i < entries.Count; i++)
{
Entry entry = entries[i];
if (entry.Key.KeyEqual(key))
{
if (entry.DependencyList == null)
{
entry.DependencyList = [];
entries[i] = entry;
}
entry.DependencyList.Add(dependency);
break;
}
}
}
public void Remove(int offset, int size, ICacheKey key)
{
List<Entry> entries = GetEntries(offset, size);
for (int i = 0; i < entries.Count; i++)
{
Entry entry = entries[i];
if (entry.Key.KeyEqual(key))
{
entries.RemoveAt(i--);
DestroyEntry(entry);
}
}
if (entries.Count == 0)
{
_ranges.Remove(PackRange(offset, size));
}
}
public bool TryGetValue(int offset, int size, ICacheKey key, out T value)
{
List<Entry> entries = GetEntries(offset, size);
foreach (Entry entry in entries)
{
if (entry.Key.KeyEqual(key))
{
value = entry.Value;
return true;
}
}
value = default;
return false;
}
public void Clear()
{
if (_ranges != null)
{
foreach (List<Entry> entries in _ranges.Values)
{
foreach (Entry entry in entries)
{
DestroyEntry(entry);
}
}
_ranges.Clear();
_ranges = null;
}
}
public readonly void ClearRange(int offset, int size)
{
if (_ranges != null && _ranges.Count > 0)
{
int end = offset + size;
List<ulong> toRemove = null;
foreach (KeyValuePair<ulong, List<Entry>> range in _ranges)
{
(int rOffset, int rSize) = UnpackRange(range.Key);
int rEnd = rOffset + rSize;
if (rEnd > offset && rOffset < end)
{
List<Entry> entries = range.Value;
foreach (Entry entry in entries)
{
DestroyEntry(entry);
}
(toRemove ??= []).Add(range.Key);
}
}
if (toRemove != null)
{
foreach (ulong range in toRemove)
{
_ranges.Remove(range);
}
}
}
}
private List<Entry> GetEntries(int offset, int size)
{
_ranges ??= new Dictionary<ulong, List<Entry>>();
ulong key = PackRange(offset, size);
if (!_ranges.TryGetValue(key, out List<Entry> value))
{
value = [];
_ranges.Add(key, value);
}
return value;
}
private static void DestroyEntry(Entry entry)
{
entry.Key.Dispose();
entry.Value?.Dispose();
entry.InvalidateDependencies();
}
private static ulong PackRange(int offset, int size)
{
return (uint)offset | ((ulong)size << 32);
}
private static (int offset, int size) UnpackRange(ulong range)
{
return ((int)range, (int)(range >> 32));
}
public void Dispose()
{
Clear();
}
}
}

View File

@ -1,170 +0,0 @@
using Ryujinx.Graphics.Metal;
using SharpMetal.Metal;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
interface IEncoderFactory
{
MTLRenderCommandEncoder CreateRenderCommandEncoder();
MTLComputeCommandEncoder CreateComputeCommandEncoder();
}
/// <summary>
/// Tracks active encoder object for a command buffer.
/// </summary>
[SupportedOSPlatform("macos")]
class CommandBufferEncoder
{
public EncoderType CurrentEncoderType { get; private set; } = EncoderType.None;
public MTLBlitCommandEncoder BlitEncoder => new(CurrentEncoder.Value);
public MTLComputeCommandEncoder ComputeEncoder => new(CurrentEncoder.Value);
public MTLRenderCommandEncoder RenderEncoder => new(CurrentEncoder.Value);
internal MTLCommandEncoder? CurrentEncoder { get; private set; }
private MTLCommandBuffer _commandBuffer;
private IEncoderFactory _encoderFactory;
public void Initialize(MTLCommandBuffer commandBuffer, IEncoderFactory encoderFactory)
{
_commandBuffer = commandBuffer;
_encoderFactory = encoderFactory;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLRenderCommandEncoder EnsureRenderEncoder()
{
if (CurrentEncoderType != EncoderType.Render)
{
return BeginRenderPass();
}
return RenderEncoder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLBlitCommandEncoder EnsureBlitEncoder()
{
if (CurrentEncoderType != EncoderType.Blit)
{
return BeginBlitPass();
}
return BlitEncoder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLComputeCommandEncoder EnsureComputeEncoder()
{
if (CurrentEncoderType != EncoderType.Compute)
{
return BeginComputePass();
}
return ComputeEncoder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetRenderEncoder(out MTLRenderCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Render)
{
encoder = default;
return false;
}
encoder = RenderEncoder;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetBlitEncoder(out MTLBlitCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Blit)
{
encoder = default;
return false;
}
encoder = BlitEncoder;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetComputeEncoder(out MTLComputeCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Compute)
{
encoder = default;
return false;
}
encoder = ComputeEncoder;
return true;
}
public void EndCurrentPass()
{
if (CurrentEncoder != null)
{
switch (CurrentEncoderType)
{
case EncoderType.Blit:
BlitEncoder.EndEncoding();
CurrentEncoder = null;
break;
case EncoderType.Compute:
ComputeEncoder.EndEncoding();
CurrentEncoder = null;
break;
case EncoderType.Render:
RenderEncoder.EndEncoding();
CurrentEncoder = null;
break;
default:
throw new InvalidOperationException();
}
CurrentEncoderType = EncoderType.None;
}
}
private MTLRenderCommandEncoder BeginRenderPass()
{
EndCurrentPass();
MTLRenderCommandEncoder renderCommandEncoder = _encoderFactory.CreateRenderCommandEncoder();
CurrentEncoder = renderCommandEncoder;
CurrentEncoderType = EncoderType.Render;
return renderCommandEncoder;
}
private MTLBlitCommandEncoder BeginBlitPass()
{
EndCurrentPass();
using MTLBlitPassDescriptor descriptor = new();
MTLBlitCommandEncoder blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor);
CurrentEncoder = blitCommandEncoder;
CurrentEncoderType = EncoderType.Blit;
return blitCommandEncoder;
}
private MTLComputeCommandEncoder BeginComputePass()
{
EndCurrentPass();
MTLComputeCommandEncoder computeCommandEncoder = _encoderFactory.CreateComputeCommandEncoder();
CurrentEncoder = computeCommandEncoder;
CurrentEncoderType = EncoderType.Compute;
return computeCommandEncoder;
}
}

View File

@ -1,289 +0,0 @@
using SharpMetal.Metal;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class CommandBufferPool : IDisposable
{
public const int MaxCommandBuffers = 16;
private readonly int _totalCommandBuffers;
private readonly int _totalCommandBuffersMask;
private readonly MTLCommandQueue _queue;
private readonly Thread _owner;
private IEncoderFactory _defaultEncoderFactory;
public bool OwnedByCurrentThread => _owner == Thread.CurrentThread;
[SupportedOSPlatform("macos")]
private struct ReservedCommandBuffer
{
public bool InUse;
public bool InConsumption;
public int SubmissionCount;
public MTLCommandBuffer CommandBuffer;
public CommandBufferEncoder Encoders;
public FenceHolder Fence;
public List<IAuto> Dependants;
public List<MultiFenceHolder> Waitables;
public void Use(MTLCommandQueue queue, IEncoderFactory stateManager)
{
MTLCommandBufferDescriptor descriptor = new();
#if DEBUG
descriptor.ErrorOptions = MTLCommandBufferErrorOption.EncoderExecutionStatus;
#endif
CommandBuffer = queue.CommandBuffer(descriptor);
Fence = new FenceHolder(CommandBuffer);
Encoders.Initialize(CommandBuffer, stateManager);
InUse = true;
}
public void Initialize()
{
Dependants = [];
Waitables = [];
Encoders = new CommandBufferEncoder();
}
}
private readonly ReservedCommandBuffer[] _commandBuffers;
private readonly int[] _queuedIndexes;
private int _queuedIndexesPtr;
private int _queuedCount;
private int _inUseCount;
public CommandBufferPool(MTLCommandQueue queue, bool isLight = false)
{
_queue = queue;
_owner = Thread.CurrentThread;
_totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
_totalCommandBuffersMask = _totalCommandBuffers - 1;
_commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers];
_queuedIndexes = new int[_totalCommandBuffers];
_queuedIndexesPtr = 0;
_queuedCount = 0;
}
public void Initialize(IEncoderFactory encoderFactory)
{
_defaultEncoderFactory = encoderFactory;
for (int i = 0; i < _totalCommandBuffers; i++)
{
_commandBuffers[i].Initialize();
WaitAndDecrementRef(i);
}
}
public void AddDependant(int cbIndex, IAuto dependant)
{
dependant.IncrementReferenceCount();
_commandBuffers[cbIndex].Dependants.Add(dependant);
}
public void AddWaitable(MultiFenceHolder waitable)
{
lock (_commandBuffers)
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
if (entry.InConsumption)
{
AddWaitable(i, waitable);
}
}
}
}
public void AddInUseWaitable(MultiFenceHolder waitable)
{
lock (_commandBuffers)
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
if (entry.InUse)
{
AddWaitable(i, waitable);
}
}
}
}
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
if (waitable.AddFence(cbIndex, entry.Fence))
{
entry.Waitables.Add(waitable);
}
}
public bool IsFenceOnRentedCommandBuffer(FenceHolder fence)
{
lock (_commandBuffers)
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
if (entry.InUse && entry.Fence == fence)
{
return true;
}
}
}
return false;
}
public FenceHolder GetFence(int cbIndex)
{
return _commandBuffers[cbIndex].Fence;
}
public int GetSubmissionCount(int cbIndex)
{
return _commandBuffers[cbIndex].SubmissionCount;
}
private int FreeConsumed(bool wait)
{
int freeEntry = 0;
while (_queuedCount > 0)
{
int index = _queuedIndexes[_queuedIndexesPtr];
ref ReservedCommandBuffer entry = ref _commandBuffers[index];
if (wait || !entry.InConsumption || entry.Fence.IsSignaled())
{
WaitAndDecrementRef(index);
wait = false;
freeEntry = index;
_queuedCount--;
_queuedIndexesPtr = (_queuedIndexesPtr + 1) % _totalCommandBuffers;
}
else
{
break;
}
}
return freeEntry;
}
public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs)
{
Return(cbs);
return Rent();
}
public CommandBufferScoped Rent()
{
lock (_commandBuffers)
{
int cursor = FreeConsumed(_inUseCount + _queuedCount == _totalCommandBuffers);
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[cursor];
if (!entry.InUse && !entry.InConsumption)
{
entry.Use(_queue, _defaultEncoderFactory);
_inUseCount++;
return new CommandBufferScoped(this, entry.CommandBuffer, entry.Encoders, cursor);
}
cursor = (cursor + 1) & _totalCommandBuffersMask;
}
}
throw new InvalidOperationException($"Out of command buffers (In use: {_inUseCount}, queued: {_queuedCount}, total: {_totalCommandBuffers})");
}
public void Return(CommandBufferScoped cbs)
{
// Ensure the encoder is committed.
cbs.Encoders.EndCurrentPass();
lock (_commandBuffers)
{
int cbIndex = cbs.CommandBufferIndex;
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
Debug.Assert(entry.InUse);
Debug.Assert(entry.CommandBuffer.NativePtr == cbs.CommandBuffer.NativePtr);
entry.InUse = false;
entry.InConsumption = true;
entry.SubmissionCount++;
_inUseCount--;
MTLCommandBuffer commandBuffer = entry.CommandBuffer;
commandBuffer.Commit();
int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers;
_queuedIndexes[ptr] = cbIndex;
_queuedCount++;
}
}
private void WaitAndDecrementRef(int cbIndex)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
if (entry.InConsumption)
{
entry.Fence.Wait();
entry.InConsumption = false;
}
foreach (IAuto dependant in entry.Dependants)
{
dependant.DecrementReferenceCount(cbIndex);
}
foreach (MultiFenceHolder waitable in entry.Waitables)
{
waitable.RemoveFence(cbIndex);
waitable.RemoveBufferUses(cbIndex);
}
entry.Dependants.Clear();
entry.Waitables.Clear();
entry.Fence?.Dispose();
}
public void Dispose()
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
WaitAndDecrementRef(i);
}
}
}
}

View File

@ -1,43 +0,0 @@
using SharpMetal.Metal;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
readonly struct CommandBufferScoped : IDisposable
{
private readonly CommandBufferPool _pool;
public MTLCommandBuffer CommandBuffer { get; }
public CommandBufferEncoder Encoders { get; }
public int CommandBufferIndex { get; }
public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, CommandBufferEncoder encoders, int commandBufferIndex)
{
_pool = pool;
CommandBuffer = commandBuffer;
Encoders = encoders;
CommandBufferIndex = commandBufferIndex;
}
public void AddDependant(IAuto dependant)
{
_pool.AddDependant(CommandBufferIndex, dependant);
}
public void AddWaitable(MultiFenceHolder waitable)
{
_pool.AddWaitable(CommandBufferIndex, waitable);
}
public FenceHolder GetFence()
{
return _pool.GetFence(CommandBufferIndex);
}
public void Dispose()
{
_pool?.Return(this);
}
}
}

View File

@ -1,41 +0,0 @@
namespace Ryujinx.Graphics.Metal
{
static class Constants
{
public const int MaxShaderStages = 5;
public const int MaxVertexBuffers = 16;
public const int MaxUniformBuffersPerStage = 18;
public const int MaxStorageBuffersPerStage = 16;
public const int MaxTexturesPerStage = 64;
public const int MaxImagesPerStage = 16;
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
public const int MaxColorAttachments = 8;
public const int MaxViewports = 16;
// TODO: Check this value
public const int MaxVertexAttributes = 31;
public const int MinResourceAlignment = 16;
// Must match constants set in shader generation
public const uint ZeroBufferIndex = MaxVertexBuffers;
public const uint BaseSetIndex = MaxVertexBuffers + 1;
public const uint ConstantBuffersIndex = BaseSetIndex;
public const uint StorageBuffersIndex = BaseSetIndex + 1;
public const uint TexturesIndex = BaseSetIndex + 2;
public const uint ImagesIndex = BaseSetIndex + 3;
public const uint ConstantBuffersSetIndex = 0;
public const uint StorageBuffersSetIndex = 1;
public const uint TexturesSetIndex = 2;
public const uint ImagesSetIndex = 3;
public const uint MaximumBufferArgumentTableEntries = 31;
public const uint MaximumExtraSets = MaximumBufferArgumentTableEntries - ImagesIndex;
}
}

View File

@ -1,22 +0,0 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Metal
{
class CounterEvent : ICounterEvent
{
public CounterEvent()
{
Invalid = false;
}
public bool Invalid { get; set; }
public bool ReserveForHostAccess()
{
return true;
}
public void Flush() { }
public void Dispose() { }
}
}

View File

@ -1,68 +0,0 @@
using Ryujinx.Graphics.Metal.State;
using SharpMetal.Metal;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class DepthStencilCache : StateCache<MTLDepthStencilState, DepthStencilUid, DepthStencilUid>
{
private readonly MTLDevice _device;
public DepthStencilCache(MTLDevice device)
{
_device = device;
}
protected override DepthStencilUid GetHash(DepthStencilUid descriptor)
{
return descriptor;
}
protected override MTLDepthStencilState CreateValue(DepthStencilUid descriptor)
{
// Create descriptors
ref StencilUid frontUid = ref descriptor.FrontFace;
using MTLStencilDescriptor frontFaceStencil = new()
{
StencilFailureOperation = frontUid.StencilFailureOperation,
DepthFailureOperation = frontUid.DepthFailureOperation,
DepthStencilPassOperation = frontUid.DepthStencilPassOperation,
StencilCompareFunction = frontUid.StencilCompareFunction,
ReadMask = frontUid.ReadMask,
WriteMask = frontUid.WriteMask
};
ref StencilUid backUid = ref descriptor.BackFace;
using MTLStencilDescriptor backFaceStencil = new()
{
StencilFailureOperation = backUid.StencilFailureOperation,
DepthFailureOperation = backUid.DepthFailureOperation,
DepthStencilPassOperation = backUid.DepthStencilPassOperation,
StencilCompareFunction = backUid.StencilCompareFunction,
ReadMask = backUid.ReadMask,
WriteMask = backUid.WriteMask
};
MTLDepthStencilDescriptor mtlDescriptor = new()
{
DepthCompareFunction = descriptor.DepthCompareFunction,
DepthWriteEnabled = descriptor.DepthWriteEnabled
};
if (descriptor.StencilTestEnabled)
{
mtlDescriptor.BackFaceStencil = backFaceStencil;
mtlDescriptor.FrontFaceStencil = frontFaceStencil;
}
using (mtlDescriptor)
{
return _device.NewDepthStencilState(mtlDescriptor);
}
}
}
}

View File

@ -1,26 +0,0 @@
using SharpMetal.Metal;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
readonly struct DisposableBuffer : IDisposable
{
public MTLBuffer Value { get; }
public DisposableBuffer(MTLBuffer buffer)
{
Value = buffer;
}
public void Dispose()
{
if (Value != IntPtr.Zero)
{
Value.SetPurgeableState(MTLPurgeableState.Empty);
Value.Dispose();
}
}
}
}

View File

@ -1,22 +0,0 @@
using SharpMetal.Metal;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
readonly struct DisposableSampler : IDisposable
{
public MTLSamplerState Value { get; }
public DisposableSampler(MTLSamplerState sampler)
{
Value = sampler;
}
public void Dispose()
{
Value.Dispose();
}
}
}

View File

@ -1,10 +0,0 @@
using System;
namespace Ryujinx.Graphics.Metal.Effects
{
internal interface IPostProcessingEffect : IDisposable
{
const int LocalGroupSize = 64;
Texture Run(Texture view, int width, int height);
}
}

View File

@ -1,18 +0,0 @@
using Ryujinx.Graphics.GAL;
using System;
namespace Ryujinx.Graphics.Metal.Effects
{
internal interface IScalingFilter : IDisposable
{
float Level { get; set; }
void Run(
Texture view,
Texture destinationTexture,
Format format,
int width,
int height,
Extents2D source,
Extents2D destination);
}
}

View File

@ -1,63 +0,0 @@
using SharpMetal.Metal;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Metal
{
public struct RenderEncoderBindings
{
public List<Resource> Resources = [];
public List<BufferResource> VertexBuffers = [];
public List<BufferResource> FragmentBuffers = [];
public RenderEncoderBindings() { }
public readonly void Clear()
{
Resources.Clear();
VertexBuffers.Clear();
FragmentBuffers.Clear();
}
}
public struct ComputeEncoderBindings
{
public List<Resource> Resources = [];
public List<BufferResource> Buffers = [];
public ComputeEncoderBindings() { }
public readonly void Clear()
{
Resources.Clear();
Buffers.Clear();
}
}
public struct BufferResource
{
public MTLBuffer Buffer;
public ulong Offset;
public ulong Binding;
public BufferResource(MTLBuffer buffer, ulong offset, ulong binding)
{
Buffer = buffer;
Offset = offset;
Binding = binding;
}
}
public struct Resource
{
public MTLResource MtlResource;
public MTLResourceUsage ResourceUsage;
public MTLRenderStages Stages;
public Resource(MTLResource resource, MTLResourceUsage resourceUsage, MTLRenderStages stages)
{
MtlResource = resource;
ResourceUsage = resourceUsage;
Stages = stages;
}
}
}

View File

@ -1,206 +0,0 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Metal.State;
using Ryujinx.Graphics.Shader;
using SharpMetal.Metal;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[Flags]
enum DirtyFlags
{
None = 0,
RenderPipeline = 1 << 0,
ComputePipeline = 1 << 1,
DepthStencil = 1 << 2,
DepthClamp = 1 << 3,
DepthBias = 1 << 4,
CullMode = 1 << 5,
FrontFace = 1 << 6,
StencilRef = 1 << 7,
Viewports = 1 << 8,
Scissors = 1 << 9,
Uniforms = 1 << 10,
Storages = 1 << 11,
Textures = 1 << 12,
Images = 1 << 13,
ArgBuffers = Uniforms | Storages | Textures | Images,
RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | ArgBuffers,
ComputeAll = ComputePipeline | ArgBuffers,
All = RenderAll | ComputeAll,
}
record struct BufferRef
{
public Auto<DisposableBuffer> Buffer;
public BufferRange? Range;
public BufferRef(Auto<DisposableBuffer> buffer)
{
Buffer = buffer;
}
public BufferRef(Auto<DisposableBuffer> buffer, ref BufferRange range)
{
Buffer = buffer;
Range = range;
}
}
record struct TextureRef
{
public ShaderStage Stage;
public TextureBase Storage;
public Auto<DisposableSampler> Sampler;
public Format ImageFormat;
public TextureRef(ShaderStage stage, TextureBase storage, Auto<DisposableSampler> sampler)
{
Stage = stage;
Storage = storage;
Sampler = sampler;
}
}
record struct ImageRef
{
public ShaderStage Stage;
public Texture Storage;
public ImageRef(ShaderStage stage, Texture storage)
{
Stage = stage;
Storage = storage;
}
}
struct PredrawState
{
public MTLCullMode CullMode;
public DepthStencilUid DepthStencilUid;
public PrimitiveTopology Topology;
public MTLViewport[] Viewports;
}
struct RenderTargetCopy
{
public MTLScissorRect[] Scissors;
public Texture DepthStencil;
public Texture[] RenderTargets;
}
[SupportedOSPlatform("macos")]
class EncoderState
{
public Program RenderProgram = null;
public Program ComputeProgram = null;
public PipelineState Pipeline;
public DepthStencilUid DepthStencilUid;
public readonly record struct ArrayRef<T>(ShaderStage Stage, T Array);
public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings * 2];
public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxImageBindings * 2];
public ArrayRef<TextureArray>[] TextureArrayRefs = [];
public ArrayRef<ImageArray>[] ImageArrayRefs = [];
public ArrayRef<TextureArray>[] TextureArrayExtraRefs = [];
public ArrayRef<ImageArray>[] ImageArrayExtraRefs = [];
public IndexBufferState IndexBuffer = default;
public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip;
public float DepthBias;
public float SlopeScale;
public float Clamp;
public int BackRefValue = 0;
public int FrontRefValue = 0;
public PrimitiveTopology Topology = PrimitiveTopology.Triangles;
public MTLCullMode CullMode = MTLCullMode.None;
public MTLWinding Winding = MTLWinding.CounterClockwise;
public bool CullBoth = false;
public MTLViewport[] Viewports = new MTLViewport[Constants.MaxViewports];
public MTLScissorRect[] Scissors = new MTLScissorRect[Constants.MaxViewports];
// Changes to attachments take recreation!
public Texture DepthStencil;
public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments];
public ITexture PreMaskDepthStencil = default;
public ITexture[] PreMaskRenderTargets;
public bool FramebufferUsingColorWriteMask;
public Array8<ColorBlendStateUid> StoredBlend;
public ColorF BlendColor = new();
public readonly VertexBufferState[] VertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers];
public readonly VertexAttribDescriptor[] VertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttributes];
// Dirty flags
public DirtyFlags Dirty = DirtyFlags.None;
// Only to be used for present
public bool ClearLoadAction = false;
public RenderEncoderBindings RenderEncoderBindings = new();
public ComputeEncoderBindings ComputeEncoderBindings = new();
public EncoderState()
{
Pipeline.Initialize();
DepthStencilUid.DepthCompareFunction = MTLCompareFunction.Always;
}
public RenderTargetCopy InheritForClear(EncoderState other, bool depth, int singleIndex = -1)
{
// Inherit render target related information without causing a render encoder split.
RenderTargetCopy oldState = new()
{
Scissors = other.Scissors,
RenderTargets = other.RenderTargets,
DepthStencil = other.DepthStencil
};
Scissors = other.Scissors;
RenderTargets = other.RenderTargets;
DepthStencil = other.DepthStencil;
Pipeline.ColorBlendAttachmentStateCount = other.Pipeline.ColorBlendAttachmentStateCount;
Pipeline.Internal.ColorBlendState = other.Pipeline.Internal.ColorBlendState;
Pipeline.DepthStencilFormat = other.Pipeline.DepthStencilFormat;
ref Array8<ColorBlendStateUid> blendStates = ref Pipeline.Internal.ColorBlendState;
// Mask out irrelevant attachments.
for (int i = 0; i < blendStates.Length; i++)
{
if (depth || (singleIndex != -1 && singleIndex != i))
{
blendStates[i].WriteMask = MTLColorWriteMask.None;
}
}
return oldState;
}
public void Restore(RenderTargetCopy copy)
{
Scissors = copy.Scissors;
RenderTargets = copy.RenderTargets;
DepthStencil = copy.DepthStencil;
Pipeline.Internal.ResetColorState();
}
}
}

File diff suppressed because it is too large Load Diff

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