Compare commits

..

3 Commits

Author SHA1 Message Date
Evan Husted d64bf7d93c small simplification 2025-01-29 18:15:15 -06:00
Evan Husted 52338db1e8 docs on the new ApplicationLibrary method 2025-01-29 18:09:10 -06:00
Evan Husted aa01f70f46 UI: Pretty Atmosphère mod names 2025-01-29 17:56:43 -06:00
296 changed files with 2834 additions and 7005 deletions
+2 -2
View File
@@ -29,7 +29,7 @@ env:
jobs: jobs:
tag: tag:
name: Create tag name: Create tag
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- name: Get version info - name: Get version info
id: version_info id: version_info
@@ -202,7 +202,7 @@ jobs:
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
+2 -2
View File
@@ -18,7 +18,7 @@ env:
jobs: jobs:
tag: tag:
name: Create tag name: Create tag
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- name: Get version info - name: Get version info
id: version_info id: version_info
@@ -183,7 +183,7 @@ jobs:
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
+2 -2
View File
@@ -39,12 +39,12 @@
<p align="center"> <p align="center">
Click below to join the Discord: Click below to join the Discord:
<br> <br>
<a href="https://discord.gg/PEuzjrFXUA"> <a href="https://discord.gg/dHPrkBkkyA">
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord"> <img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
</a> </a>
<br> <br>
<br> <br>
<img src="https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/docs/shell.png"> <img src="https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/docs/shell.png">
</p> </p>
## Usage ## Usage
+14 -28
View File
@@ -332,7 +332,6 @@
0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21 0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21
01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26 01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26
01004DA012976000,"Area 86",,playable,2020-12-16 16:45:52 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 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 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 0100C56012C96000,"Arkanoid vs. Space Invaders",services,ingame,2021-01-21 12:50:30
@@ -427,7 +426,6 @@
0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07 0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07
0100CD801CE5E000,"Balatro",,ingame,2024-04-21 02:01:53 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 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 0100BC400FB64000,"Balthazar's Dream",,playable,2022-09-13 00:13:22
01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39 01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39
010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37 010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37
@@ -530,7 +528,6 @@
01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43 01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43
0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26 0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26
0100CFA00CC74000,"Blades of Time",deadlock;online,boots,2022-07-17 19:19:58 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 01006CC01182C000,"Blair Witch",nvdec;UE4,playable,2022-10-01 14:06:16
010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13 010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13
0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31 0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31
@@ -958,7 +955,7 @@
010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28 010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28
01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12 01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12
010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24 010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,ingame,2025-02-03 22:12:30 010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,menus,2023-08-13 17:20:03
01001770115C8000,"Dodo Peak",nvdec;UE4,playable,2022-10-04 16:13:05 01001770115C8000,"Dodo Peak",nvdec;UE4,playable,2022-10-04 16:13:05
010077B0100DA000,"Dogurai",,playable,2020-10-04 02:40:16 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 010048100D51A000,"Dokapon Up! Mugen no Roulette",gpu;Needs Update,menus,2022-12-08 19:39:10
@@ -969,7 +966,6 @@
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34 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 010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10 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 0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07 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 010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
@@ -1159,7 +1155,7 @@
010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22 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 0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47
0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39 0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39
01000EA014150000,"FINAL FANTASY",,playable,2025-02-16 21:27:30 01000EA014150000,"FINAL FANTASY",crash,nothing,2024-09-05 20:55:30
01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04 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 01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00
0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55 0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55
@@ -1250,7 +1246,7 @@
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08 0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40 0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21 010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
0100E1F013674000,"FUSER™",nvdec;UE4;slow;gpu,ingame,2025-02-12 16:03:00 0100E1F013674000,"FUSER™",nvdec;UE4,playable,2022-10-17 20:58:32
0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02 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 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 010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53
@@ -1658,7 +1654,7 @@
0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03 0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03
0100A7700B46C000,"Legendary Fishing",online,playable,2021-04-14 15:08:46 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 0100739018020000,"LEGO® 2K Drive",gpu;ldn-works,ingame,2024-04-09 02:05:12
010085500130a000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27 01003A30012C0000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
010070D009FEC000,"LEGO® DC Super-Villains",,playable,2021-05-27 18:10:37 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 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 010073C01AF34000,"LEGO® Horizon Adventures™",vulkan-backend-bug;opengl-backend-bug;UE4,ingame,2025-01-07 04:24:56
@@ -1917,7 +1913,6 @@
010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17 010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17
0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04 0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04
0100211005E94000,"Mulaka",,playable,2021-01-28 18:07:20 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 010038B00B9AE000,"Mummy Pinball",,playable,2022-08-05 16:08:11
01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29 01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29
010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08 010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08
@@ -2033,7 +2028,6 @@
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26 010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29 0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
0100C9A00ECE6000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07 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 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 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 01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
@@ -2064,7 +2058,7 @@
010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07 010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07
010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23 010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23
010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22 010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22
,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32 0000000000000000,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
,"nxquake2",services;crash;homebrew,nothing,2022-08-04 23:14:04 ,"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 010049F00EC30000,"Nyan Cat: Lost in Space",online,playable,2021-06-12 13:22:03
01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14 01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14
@@ -2472,7 +2466,7 @@
0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38 0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38
0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01 0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01
01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18 01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18
,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34 0000000000000000,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
010009B00D33C000,"Rugby Challenge 4",slow;online-broken;UE4,playable,2022-10-06 12:45:53 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 01006EC00F2CC000,"RUINER",UE4,playable,2022-10-03 14:11:33
010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28 010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28
@@ -2481,7 +2475,6 @@
010081C0191D8000,"Rune Factory 3 Special",,playable,2023-10-15 08:32:49 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 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 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 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 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 0100B8B012ECA000,"S.N.I.P.E.R. - Hunter Scope",,playable,2021-04-19 15:58:09
@@ -2490,7 +2483,7 @@
0100A5200C2E0000,"Safety First!",,playable,2021-01-06 09:05:23 0100A5200C2E0000,"Safety First!",,playable,2021-01-06 09:05:23
0100A51013530000,"SaGa Frontier Remastered",nvdec,playable,2022-11-03 13:54:56 0100A51013530000,"SaGa Frontier Remastered",nvdec,playable,2022-11-03 13:54:56
010003A00D0B4000,"SaGa SCARLET GRACE: AMBITIONS™",,playable,2022-10-06 13:20:31 010003A00D0B4000,"SaGa SCARLET GRACE: AMBITIONS™",,playable,2022-10-06 13:20:31
01008D100D43E000,"Saints Row IV®: Re-Elected™",ldn-untested;LAN;deadlock,ingame,2025-02-02 16:57:53 01008D100D43E000,"Saints Row IV®: Re-Elected™",ldn-untested;LAN,playable,2023-12-04 18:33:37
0100DE600BEEE000,"SAINTS ROW®: THE THIRD™ - THE FULL PACKAGE",slow;LAN,playable,2023-08-24 02:40:58 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 01007F000EB36000,"Sakai and...",nvdec,playable,2022-12-15 13:53:19
0100B1400E8FE000,"Sakuna: Of Rice and Ruin",,playable,2023-07-24 13:47:13 0100B1400E8FE000,"Sakuna: Of Rice and Ruin",,playable,2023-07-24 13:47:13
@@ -2539,7 +2532,7 @@
0100C3E00B700000,"SEGA AGES Space Harrier",,playable,2021-01-11 12:57:40 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 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 01001E700AC60000,"SEGA AGES Wonder Boy: Monster Land",online,playable,2021-05-05 16:28:25
0100B3C014BDA000,"SEGA Genesis™ Nintendo Switch Online",crash;regression,ingame,2025-02-03 22:13:30 0100B3C014BDA000,"SEGA Genesis™ Nintendo Switch Online",crash;regression,nothing,2022-04-11 07:27:21
0100F7300B24E000,"SEGA Mega Drive Classics",online,playable,2021-01-05 11:08:00 0100F7300B24E000,"SEGA Mega Drive Classics",online,playable,2021-01-05 11:08:00
01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31 01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31
0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10 0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10
@@ -2675,10 +2668,10 @@
01004F401BEBE000,"Song of Nunu: A League of Legends Story",,ingame,2024-07-12 18:53:44 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 0100E5400BF94000,"Songbird Symphony",,playable,2021-02-27 02:44:04
010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02 010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02
,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20 0000000000000000,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30 0000000000000000,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32 0000000000000000,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31 0000000000000000,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26 010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26
01001270012B6000,"SONIC FORCES™",,playable,2024-07-28 13:11:21 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 01004AD014BF0000,"Sonic Frontiers",gpu;deadlock;amd-vendor-bug;intel-vendor-bug,ingame,2024-09-05 09:18:53
@@ -2695,7 +2688,7 @@
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05 0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37 010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37
010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10 010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10
,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04 0000000000000000,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
0100D9B0041CE000,"Spacecats with Lasers",,playable,2022-08-15 17:22:44 0100D9B0041CE000,"Spacecats with Lasers",,playable,2022-08-15 17:22:44
010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56 010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56
010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39 010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39
@@ -2839,9 +2832,8 @@
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19 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 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 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 0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
,"Super Mario World",homebrew,boots,2024-06-13 01:40:31 0000000000000000,"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 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 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 01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
@@ -2972,7 +2964,6 @@
0100C38004DCC000,"The Flame In The Flood: Complete Edition",gpu;nvdec;UE4,ingame,2022-08-22 16:23:49 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 010007700D4AC000,"The Forbidden Arts",,playable,2021-01-26 16:26:24
010030700CBBC000,"The friends of Ringo Ishikawa",,playable,2022-08-22 16:33:17 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 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 0100B13007A6A000,"The Gardens Between",,playable,2021-01-29 16:16:53
010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29 010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29
@@ -2990,8 +2981,6 @@
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40 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 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 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 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 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 01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
@@ -3170,7 +3159,6 @@
010055E00CA68000,"Trine 4: The Nightmare Prince",gpu,nothing,2025-01-07 05:47:46 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 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 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 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 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 0100145011008000,"Trollhunters: Defenders of Arcadia",gpu;nvdec,ingame,2020-11-30 13:27:09
@@ -3220,7 +3208,6 @@
0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27 0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27
0100A3E011CB0000,"Unlock the King 2",,playable,2021-06-15 20:43:55 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 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 0100E5D00CC0C000,"Unravel Two",nvdec,playable,2024-05-23 15:45:05
010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31 010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31
0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49 0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49
@@ -3385,7 +3372,6 @@
0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51 0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51
010012F00B6F2000,"Yomawari: The Long Night Collection",,playable,2022-09-03 14:36:59 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 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 0100BE50042F6000,"Yono and the Celestial Elephants",,playable,2021-01-28 18:23:58
0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45 0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45
010022F00DA66000,"Yooka-Laylee and the Impossible Lair",,playable,2021-03-05 17:32:21 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
01008d8006a6a000 Arena of Valor crash boots 2025-02-03 22:19:34
335 0100691013C46000 ARIA CHRONICLE playable 2022-11-16 13:50:55
336 0100D4A00B284000 ARK: Survival Evolved gpu;nvdec;online-broken;UE4;ldn-untested ingame 2024-04-16 00:53:56
337 0100C56012C96000 Arkanoid vs. Space Invaders services ingame 2021-01-21 12:50:30
426 0100E48013A34000 Balan Wonderworld Demo gpu;services;UE4;demo ingame 2023-02-16 20:05:07
427 0100CD801CE5E000 Balatro ingame 2024-04-21 02:01:53
428 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
429 0100BC400FB64000 Balthazar's Dream playable 2022-09-13 00:13:22
430 01008D30128E0000 Bamerang playable 2022-10-26 00:29:39
431 010013C010C5C000 Banner of the Maid playable 2021-06-14 15:23:37
528 01005950022EC000 Blade Strangers nvdec playable 2022-07-17 19:02:43
529 0100DF0011A6A000 Bladed Fury playable 2022-10-26 11:36:26
530 0100CFA00CC74000 Blades of Time deadlock;online boots 2022-07-17 19:19:58
01003d700dd8a000 Blades boots 2025-02-03 22:22:00
531 01006CC01182C000 Blair Witch nvdec;UE4 playable 2022-10-01 14:06:16
532 010039501405E000 Blanc gpu;slow ingame 2023-02-22 14:00:13
533 0100698009C6E000 Blasphemous nvdec playable 2021-03-01 12:15:31
955 010012800EBAE000 Disney TSUM TSUM FESTIVAL crash menus 2020-07-14 14:05:28
956 01009740120FE000 DISTRAINT 2 playable 2020-09-03 16:08:12
957 010075B004DD2000 DISTRAINT: Deluxe Edition playable 2020-06-15 23:42:24
958 010027400CDC6000 Divinity: Original Sin 2 - Definitive Edition services;crash;online-broken;regression ingame menus 2025-02-03 22:12:30 2023-08-13 17:20:03
959 01001770115C8000 Dodo Peak nvdec;UE4 playable 2022-10-04 16:13:05
960 010077B0100DA000 Dogurai playable 2020-10-04 02:40:16
961 010048100D51A000 Dokapon Up! Mugen no Roulette gpu;Needs Update menus 2022-12-08 19:39:10
966 0100751007ADA000 Don't Starve: Nintendo Switch Edition nvdec playable 2022-02-05 20:43:34
967 010088B010DD2000 Dongo Adventure playable 2022-10-04 16:22:26
968 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
969 0100F2C00F060000 Doodle Derby boots 2020-12-04 22:51:48
970 0100416004C00000 DOOM gpu;slow;nvdec;online-broken ingame 2024-09-23 15:40:07
971 010018900DD00000 DOOM (1993) nvdec;online-broken menus 2022-09-06 13:32:19
1155 010095600AA36000 Fill-a-Pix: Phil's Epic Adventure playable 2020-12-22 13:48:22
1156 0100C3A00BB76000 Fimbul nvdec playable 2022-07-26 13:31:47
1157 0100C8200E942000 Fin and the Ancient Mystery nvdec playable 2020-12-17 16:40:39
1158 01000EA014150000 FINAL FANTASY crash playable nothing 2025-02-16 21:27:30 2024-09-05 20:55:30
1159 01006B7014156000 FINAL FANTASY II crash nothing 2024-04-13 19:18:04
1160 01006F000B056000 FINAL FANTASY IX audout;nvdec playable 2021-06-05 11:35:00
1161 0100AA201415C000 FINAL FANTASY V playable 2023-04-26 01:11:55
1246 0100A6B00D4EC000 Furwind playable 2021-02-19 19:44:08
1247 0100ECE00C0C4000 Fury Unleashed crash;services ingame 2020-10-18 11:52:40
1248 010070000ED9E000 Fury Unleashed Demo playable 2020-10-08 20:09:21
1249 0100E1F013674000 FUSER™ nvdec;UE4;slow;gpu nvdec;UE4 ingame playable 2025-02-12 16:03:00 2022-10-17 20:58:32
1250 0100A7A015E4C000 Fushigi no Gensokyo Lotus Labyrinth Needs Update;audio;gpu;nvdec ingame 2021-01-20 15:30:02
1251 01003C300B274000 Futari de! Nyanko Daisensou playable 2024-01-05 22:26:52
1252 010055801134E000 FUZE Player online-broken;vulkan-backend-bug ingame 2022-10-18 12:23:53
1654 0100A73006E74000 Legendary Eleven playable 2021-06-08 12:09:03
1655 0100A7700B46C000 Legendary Fishing online playable 2021-04-14 15:08:46
1656 0100739018020000 LEGO® 2K Drive gpu;ldn-works ingame 2024-04-09 02:05:12
1657 010085500130a000 01003A30012C0000 LEGO® CITY Undercover nvdec playable 2024-09-30 08:44:27
1658 010070D009FEC000 LEGO® DC Super-Villains playable 2021-05-27 18:10:37
1659 010052A00B5D2000 LEGO® Harry Potter™ Collection crash ingame 2024-01-31 10:28:07
1660 010073C01AF34000 LEGO® Horizon Adventures™ vulkan-backend-bug;opengl-backend-bug;UE4 ingame 2025-01-07 04:24:56
1913 010073E008E6E000 Mugsters playable 2021-01-28 17:57:17
1914 0100A8400471A000 MUJO playable 2020-05-08 16:31:04
1915 0100211005E94000 Mulaka playable 2021-01-28 18:07:20
01008e2013fb4000 Multi Quiz ldn-untested ingame 2025-02-03 22:26:00
1916 010038B00B9AE000 Mummy Pinball playable 2022-08-05 16:08:11
1917 01008E200C5C2000 Muse Dash playable 2020-06-06 14:41:29
1918 010035901046C000 Mushroom Quest playable 2020-05-17 13:07:08
2028 010003C00B868000 Ninjin: Clash of Carrots online-broken playable 2024-07-10 05:12:26
2029 0100746010E4C000 NinNinDays playable 2022-11-20 15:17:29
2030 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
2031 0100D870045B6000 Nintendo Entertainment System™ - Nintendo Switch Online online playable 2022-07-01 15:45:06
2032 0100C4B0034B2000 Nintendo Labo Toy-Con 01 Variety Kit gpu ingame 2022-08-07 12:56:07
2033 01001E9003502000 Nintendo Labo Toy-Con 03 Vehicle Kit services;crash menus 2022-08-03 17:20:11
2058 010002700C34C000 Numbala playable 2020-05-11 12:01:07
2059 010020500C8C8000 Number Place 10000 gpu menus 2021-11-24 09:14:23
2060 010003701002C000 Nurse Love Syndrome playable 2022-10-13 10:05:22
2061 0000000000000000 nx-hbmenu Needs Update;homebrew boots 2024-04-06 22:05:32
2062 nxquake2 services;crash;homebrew nothing 2022-08-04 23:14:04
2063 010049F00EC30000 Nyan Cat: Lost in Space online playable 2021-06-12 13:22:03
2064 01002E6014FC4000 O---O playable 2022-10-29 12:12:14
2466 0100AFE00DDAC000 Royal Roads playable 2020-11-17 12:54:38
2467 0100E2C00B414000 RPG Maker MV nvdec playable 2021-01-05 20:12:01
2468 01005CD015986000 rRootage Reloaded playable 2022-08-05 23:20:18
2469 0000000000000000 RSDKv5u homebrew ingame 2024-04-01 16:25:34
2470 010009B00D33C000 Rugby Challenge 4 slow;online-broken;UE4 playable 2022-10-06 12:45:53
2471 01006EC00F2CC000 RUINER UE4 playable 2022-10-03 14:11:33
2472 010074F00DE4A000 Run the Fan playable 2021-02-27 13:36:28
2475 010081C0191D8000 Rune Factory 3 Special playable 2023-10-15 08:32:49
2476 010051D00E3A4000 Rune Factory 4 Special 32-bit;crash;nvdec ingame 2023-05-06 08:49:17
2477 010014D01216E000 Rune Factory 5 (JP) gpu ingame 2021-06-01 12:00:36
010071E0145F8000 Rustler playable 2025-02-10 20:17:12
2478 0100E21013908000 RWBY: Grimm Eclipse - Definitive Edition online-broken playable 2022-11-03 10:44:01
2479 010012C0060F0000 RXN -Raijin- nvdec playable 2021-01-10 16:05:43
2480 0100B8B012ECA000 S.N.I.P.E.R. - Hunter Scope playable 2021-04-19 15:58:09
2483 0100A5200C2E0000 Safety First! playable 2021-01-06 09:05:23
2484 0100A51013530000 SaGa Frontier Remastered nvdec playable 2022-11-03 13:54:56
2485 010003A00D0B4000 SaGa SCARLET GRACE: AMBITIONS™ playable 2022-10-06 13:20:31
2486 01008D100D43E000 Saints Row IV®: Re-Elected™ ldn-untested;LAN;deadlock ldn-untested;LAN ingame playable 2025-02-02 16:57:53 2023-12-04 18:33:37
2487 0100DE600BEEE000 SAINTS ROW®: THE THIRD™ - THE FULL PACKAGE slow;LAN playable 2023-08-24 02:40:58
2488 01007F000EB36000 Sakai and... nvdec playable 2022-12-15 13:53:19
2489 0100B1400E8FE000 Sakuna: Of Rice and Ruin playable 2023-07-24 13:47:13
2532 0100C3E00B700000 SEGA AGES Space Harrier playable 2021-01-11 12:57:40
2533 010054400D2E6000 SEGA AGES Virtua Racing online-broken playable 2023-01-29 17:08:39
2534 01001E700AC60000 SEGA AGES Wonder Boy: Monster Land online playable 2021-05-05 16:28:25
2535 0100B3C014BDA000 SEGA Genesis™ – Nintendo Switch Online crash;regression ingame nothing 2025-02-03 22:13:30 2022-04-11 07:27:21
2536 0100F7300B24E000 SEGA Mega Drive Classics online playable 2021-01-05 11:08:00
2537 01009840046BC000 Semispheres playable 2021-01-06 23:08:31
2538 0100D1800D902000 SENRAN KAGURA Peach Ball playable 2021-06-03 15:12:10
2668 01004F401BEBE000 Song of Nunu: A League of Legends Story ingame 2024-07-12 18:53:44
2669 0100E5400BF94000 Songbird Symphony playable 2021-02-27 02:44:04
2670 010031D00A604000 Songbringer playable 2020-06-22 10:42:02
2671 0000000000000000 Sonic 1 (2013) crash;homebrew ingame 2024-04-06 18:31:20
2672 0000000000000000 Sonic 2 (2013) crash;homebrew ingame 2024-04-01 16:25:30
2673 0000000000000000 Sonic A.I.R homebrew ingame 2024-04-01 16:25:32
2674 0000000000000000 Sonic CD crash;homebrew ingame 2024-04-01 16:25:31
2675 010040E0116B8000 Sonic Colors: Ultimate playable 2022-11-12 21:24:26
2676 01001270012B6000 SONIC FORCES™ playable 2024-07-28 13:11:21
2677 01004AD014BF0000 Sonic Frontiers gpu;deadlock;amd-vendor-bug;intel-vendor-bug ingame 2024-09-05 09:18:53
2688 0100707011722000 Space Elite Force playable 2020-11-27 15:21:05
2689 010047B010260000 Space Pioneer playable 2022-10-20 12:24:37
2690 010010A009830000 Space Ribbon playable 2022-08-15 17:17:10
2691 0000000000000000 SpaceCadetPinball homebrew ingame 2024-04-18 19:30:04
2692 0100D9B0041CE000 Spacecats with Lasers playable 2022-08-15 17:22:44
2693 010034800FB60000 Spaceland playable 2020-11-01 14:31:56
2694 010028D0045CE000 Sparkle 2 playable 2020-10-19 11:51:39
2832 01009B90006DC000 Super Mario Maker™ 2 online-broken;ldn-broken playable 2024-08-25 11:05:19
2833 0100000000010000 Super Mario Odyssey™ nvdec;intel-vendor-bug;mac-bug playable 2024-08-25 01:32:34
2834 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
2835 0100BC0018138000 Super Mario RPG™ gpu;audio;nvdec ingame 2024-06-19 17:43:42
2836 0000000000000000 Super Mario World homebrew boots 2024-06-13 01:40:31
2837 010049900F546000 Super Mario™ 3D All-Stars services-horizon;slow;vulkan;amd-vendor-bug ingame 2024-05-07 02:38:16
2838 010028600EBDA000 Super Mario™ 3D World + Bowser’s Fury ldn-works playable 2024-07-31 10:45:37
2839 01004F8006A78000 Super Meat Boy services playable 2020-04-02 23:10:07
2964 0100C38004DCC000 The Flame In The Flood: Complete Edition gpu;nvdec;UE4 ingame 2022-08-22 16:23:49
2965 010007700D4AC000 The Forbidden Arts playable 2021-01-26 16:26:24
2966 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
2967 01006350148DA000 The Gardener and the Wild Vines gpu ingame 2024-04-29 16:32:10
2968 0100B13007A6A000 The Gardens Between playable 2021-01-29 16:16:53
2969 010036E00FB20000 The Great Ace Attorney Chronicles playable 2023-06-22 21:26:29
2981 010015D003EE4000 The Jackbox Party Pack 2 online-working playable 2022-08-22 18:23:40
2982 0100CC80013D6000 The Jackbox Party Pack 3 slow;online-working playable 2022-08-22 18:41:06
2983 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
2984 010052C00B184000 The Journey Down: Chapter One nvdec playable 2021-02-24 13:32:41
2985 01006BC00B188000 The Journey Down: Chapter Three nvdec playable 2021-02-24 13:45:27
2986 01009AB00B186000 The Journey Down: Chapter Two nvdec playable 2021-02-24 13:32:13
3159 010055E00CA68000 Trine 4: The Nightmare Prince gpu nothing 2025-01-07 05:47:46
3160 0100D9000A930000 Trine Enchanted Edition ldn-untested;nvdec playable 2021-06-03 11:28:15
3161 01002D7010A54000 Trinity Trigger crash ingame 2023-03-03 03:09:09
010020700a5e0000 TRIVIAL PURSUIT Live! ldn-untested ingame 2025-02-03 22:35:00
3162 0100868013FFC000 TRIVIAL PURSUIT Live! 2 boots 2022-12-19 00:04:33
3163 0100F78002040000 Troll and I™ gpu;nvdec ingame 2021-06-04 16:58:50
3164 0100145011008000 Trollhunters: Defenders of Arcadia gpu;nvdec ingame 2020-11-30 13:27:09
3208 0100AB2010B4C000 Unlock The King playable 2020-09-01 13:58:27
3209 0100A3E011CB0000 Unlock the King 2 playable 2021-06-15 20:43:55
3210 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
3211 0100E5D00CC0C000 Unravel Two nvdec playable 2024-05-23 15:45:05
3212 010001300CC4A000 Unruly Heroes playable 2021-01-07 18:09:31
3213 0100B410138C0000 Unspottable playable 2022-10-25 19:28:49
3372 0100F47016F26000 Yomawari 3 playable 2022-05-10 08:26:51
3373 010012F00B6F2000 Yomawari: The Long Night Collection playable 2022-09-03 14:36:59
3374 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
3375 0100BE50042F6000 Yono and the Celestial Elephants playable 2021-01-28 18:23:58
3376 0100F110029C8000 Yooka-Laylee playable 2021-01-28 14:21:45
3377 010022F00DA66000 Yooka-Laylee and the Impossible Lair playable 2021-03-05 17:32:21
@@ -1,6 +1,7 @@
using ARMeilleure.CodeGen.Linking; using ARMeilleure.CodeGen.Linking;
using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Translation.Cache; using ARMeilleure.Translation.Cache;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ARMeilleure.CodeGen namespace ARMeilleure.CodeGen
@@ -1,3 +1,4 @@
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
-2
View File
@@ -7,7 +7,6 @@ namespace ARMeilleure.Memory
public const int DefaultGranularity = 65536; // Mapping granularity in Windows. public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
public IJitMemoryBlock Block { get; } public IJitMemoryBlock Block { get; }
public IJitMemoryAllocator Allocator { get; }
public nint Pointer => Block.Pointer; public nint Pointer => Block.Pointer;
@@ -22,7 +21,6 @@ namespace ARMeilleure.Memory
granularity = DefaultGranularity; granularity = DefaultGranularity;
} }
Allocator = allocator;
Block = allocator.Reserve(maxSize); Block = allocator.Reserve(maxSize);
_maxSize = maxSize; _maxSize = maxSize;
_sizeGranularity = granularity; _sizeGranularity = granularity;
@@ -1,3 +1,4 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
+1
View File
@@ -1,5 +1,6 @@
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory; using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
@@ -1,6 +1,7 @@
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using Ryujinx.Common.Memory.PartialUnmaps; using Ryujinx.Common.Memory.PartialUnmaps;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory; using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
@@ -1,4 +1,5 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using System;
namespace ARMeilleure.State namespace ARMeilleure.State
{ {
@@ -6,6 +6,7 @@ using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.State; using ARMeilleure.State;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory; using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
+35 -77
View File
@@ -2,8 +2,6 @@ using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.Native; using ARMeilleure.Native;
using Humanizer;
using Ryujinx.Common.Logging;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -20,8 +18,9 @@ namespace ARMeilleure.Translation.Cache
private static readonly int _pageMask = _pageSize - 1; private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4; // Bytes. private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 256 * 1024 * 1024; private const int CacheSize = 2047 * 1024 * 1024;
private static ReservedRegion _jitRegion;
private static JitCacheInvalidation _jitCacheInvalidator; private static JitCacheInvalidation _jitCacheInvalidator;
private static CacheMemoryAllocator _cacheAllocator; private static CacheMemoryAllocator _cacheAllocator;
@@ -31,9 +30,6 @@ namespace ARMeilleure.Translation.Cache
private static readonly Lock _lock = new(); private static readonly Lock _lock = new();
private static bool _initialized; private static bool _initialized;
private static readonly List<ReservedRegion> _jitRegions = new();
private static int _activeRegionIndex = 0;
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)] [LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize); public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
@@ -52,9 +48,7 @@ namespace ARMeilleure.Translation.Cache
return; return;
} }
ReservedRegion firstRegion = new(allocator, CacheSize); _jitRegion = new ReservedRegion(allocator, CacheSize);
_jitRegions.Add(firstRegion);
_activeRegionIndex = 0;
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
{ {
@@ -65,9 +59,7 @@ namespace ARMeilleure.Translation.Cache
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
JitUnwindWindows.InstallFunctionTableHandler( JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize));
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
);
} }
_initialized = true; _initialized = true;
@@ -83,8 +75,8 @@ namespace ARMeilleure.Translation.Cache
Debug.Assert(_initialized); Debug.Assert(_initialized);
int funcOffset = Allocate(code.Length); int funcOffset = Allocate(code.Length);
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
nint funcPtr = targetRegion.Pointer + funcOffset; nint funcPtr = _jitRegion.Pointer + funcOffset;
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{ {
@@ -98,9 +90,9 @@ namespace ARMeilleure.Translation.Cache
} }
else else
{ {
ReprotectAsWritable(targetRegion, funcOffset, code.Length); ReprotectAsWritable(funcOffset, code.Length);
Marshal.Copy(code, 0, funcPtr, code.Length); Marshal.Copy(code, 0, funcPtr, code.Length);
ReprotectAsExecutable(targetRegion, funcOffset, code.Length); ReprotectAsExecutable(funcOffset, code.Length);
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{ {
@@ -124,83 +116,52 @@ namespace ARMeilleure.Translation.Cache
{ {
Debug.Assert(_initialized); Debug.Assert(_initialized);
foreach (ReservedRegion region in _jitRegions) int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
{ {
if (pointer.ToInt64() < region.Pointer.ToInt64() || _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64()) _cacheEntries.RemoveAt(entryIndex);
{
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(ReservedRegion region, int offset, int size) private static void ReprotectAsWritable(int offset, int size)
{ {
int endOffs = offset + size; int endOffs = offset + size;
int regionStart = offset & ~_pageMask; int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask; int regionEnd = (endOffs + _pageMask) & ~_pageMask;
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); _jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
} }
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size) private static void ReprotectAsExecutable(int offset, int size)
{ {
int endOffs = offset + size; int endOffs = offset + size;
int regionStart = offset & ~_pageMask; int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask; int regionEnd = (endOffs + _pageMask) & ~_pageMask;
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart)); _jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
} }
private static int Allocate(int codeSize) private static int Allocate(int codeSize)
{ {
codeSize = AlignCodeSize(codeSize); codeSize = AlignCodeSize(codeSize);
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++) int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset < 0)
{ {
int allocOffset = _cacheAllocator.Allocate(codeSize); throw new OutOfMemoryException("JIT Cache exhausted.");
if (allocOffset >= 0)
{
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
_activeRegionIndex = i;
return allocOffset;
}
} }
int exhaustedRegion = _activeRegionIndex; _jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
_jitRegions.Add(newRegion);
_activeRegionIndex = _jitRegions.Count - 1;
int newRegionNumber = _activeRegionIndex;
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); return allocOffset;
_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) private static int AlignCodeSize(int codeSize)
{ {
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1); return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
@@ -224,21 +185,18 @@ namespace ARMeilleure.Translation.Cache
{ {
lock (_lock) lock (_lock)
{ {
foreach (ReservedRegion _ in _jitRegions) int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
if (index < 0)
{ {
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default)); index = ~index - 1;
}
if (index < 0) if (index >= 0)
{ {
index = ~index - 1; entry = _cacheEntries[index];
} entryIndex = index;
return true;
if (index >= 0)
{
entry = _cacheEntries[index];
entryIndex = index;
return true;
}
} }
} }
@@ -1,4 +1,5 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ARMeilleure.Translation.Cache namespace ARMeilleure.Translation.Cache
@@ -1,3 +1,5 @@
using System;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
class DelegateInfo class DelegateInfo
@@ -1,3 +1,5 @@
using System;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
delegate void DispatcherFunction(nint nativeContext, ulong startAddress); delegate void DispatcherFunction(nint nativeContext, ulong startAddress);
@@ -1,3 +1,5 @@
using System;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
delegate ulong GuestFunction(nint nativeContextPtr); delegate ulong GuestFunction(nint nativeContextPtr);
@@ -144,15 +144,17 @@ namespace ARMeilleure.Translation.PTC
public List<ulong> GetBlacklistedFunctions() public List<ulong> GetBlacklistedFunctions()
{ {
List<ulong> funcs = []; List<ulong> funcs = new List<ulong>();
foreach ((ulong ptr, FuncProfile funcProfile) in ProfiledFuncs) foreach (var profiledFunc in ProfiledFuncs)
{ {
if (!funcProfile.Blacklist) if (profiledFunc.Value.Blacklist)
continue; {
if (!funcs.Contains(profiledFunc.Key))
if (!funcs.Contains(ptr)) {
funcs.Add(ptr); funcs.Add(profiledFunc.Key);
}
}
} }
return funcs; return funcs;
@@ -1,4 +1,5 @@
using ARMeilleure.Common; using ARMeilleure.Common;
using System;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
@@ -5,6 +5,7 @@ using ARMeilleure.Diagnostics;
using ARMeilleure.Instructions; using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.Signal;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation.Cache; using ARMeilleure.Translation.Cache;
using ARMeilleure.Translation.PTC; using ARMeilleure.Translation.PTC;
@@ -4,6 +4,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation.Cache; using ARMeilleure.Translation.Cache;
using System; using System;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory; using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
@@ -4,6 +4,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading; using System.Threading;
@@ -1,4 +1,5 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -1,3 +1,4 @@
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo; using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
@@ -4,6 +4,7 @@ using Ryujinx.Audio.Common;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
@@ -1,6 +1,7 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System; using System;
using System.Buffers;
using System.Threading; using System.Threading;
namespace Ryujinx.Audio.Backends.Common namespace Ryujinx.Audio.Backends.Common
@@ -1,3 +1,4 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using CpuAddress = System.UInt64; using CpuAddress = System.UInt64;
using DspAddress = System.UInt64; using DspAddress = System.UInt64;
@@ -6,16 +6,4 @@ namespace Ryujinx.Common.Configuration
Unbounded, Unbounded,
Custom Custom
} }
public static class VSyncModeExtensions
{
public static VSyncMode Next(this VSyncMode vsync, bool customEnabled = false) =>
vsync switch
{
VSyncMode.Switch => customEnabled ? VSyncMode.Custom : VSyncMode.Unbounded,
VSyncMode.Unbounded => VSyncMode.Switch,
VSyncMode.Custom => VSyncMode.Unbounded,
_ => VSyncMode.Switch
};
}
} }
@@ -1,4 +1,5 @@
using Microsoft.Win32; using Microsoft.Win32;
using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
+1
View File
@@ -1,3 +1,4 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
-118
View File
@@ -1,118 +0,0 @@
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
}
}
-58
View File
@@ -1,58 +0,0 @@
using Gommon;
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Common.Helper
{
public class RefEvent<T>
{
public delegate void Handler(ref T arg);
private readonly Lock _subLock = new();
private readonly List<Handler> _subscriptions = [];
public bool HasSubscribers
{
get
{
lock (_subLock)
return _subscriptions.Count != 0;
}
}
public IReadOnlyList<Handler> Subscriptions
{
get
{
lock (_subLock)
return _subscriptions;
}
}
public void Add(Handler subscriber)
{
Guard.Require(subscriber, nameof(subscriber));
lock (_subLock)
_subscriptions.Add(subscriber);
}
public void Remove(Handler subscriber)
{
Guard.Require(subscriber, nameof(subscriber));
lock (_subLock)
_subscriptions.Remove(subscriber);
}
public void Clear()
{
lock (_subLock)
_subscriptions.Clear();
}
public void Call(ref T arg)
{
foreach (Handler subscription in Subscriptions)
subscription(ref arg);
}
}
}
+6 -10
View File
@@ -10,18 +10,14 @@ namespace Ryujinx.Common.Helper
public static bool IsMacOS => OperatingSystem.IsMacOS(); public static bool IsMacOS => OperatingSystem.IsMacOS();
public static bool IsWindows => OperatingSystem.IsWindows(); public static bool IsWindows => OperatingSystem.IsWindows();
public static bool IsLinux => OperatingSystem.IsLinux(); 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 && IsX64; public static bool IsIntelMac => IsMacOS && RuntimeInformation.OSArchitecture is Architecture.X64;
public static bool IsArmMac => IsMacOS && IsArm; public static bool IsArmMac => IsMacOS && RuntimeInformation.OSArchitecture is Architecture.Arm64;
public static bool IsX64Windows => IsWindows && IsX64; public static bool IsX64Windows => IsWindows && (RuntimeInformation.OSArchitecture is Architecture.X64);
public static bool IsArmWindows => IsWindows && IsArm; public static bool IsArmWindows => IsWindows && (RuntimeInformation.OSArchitecture is Architecture.Arm64);
public static bool IsX64Linux => IsLinux && IsX64; public static bool IsX64Linux => IsLinux && (RuntimeInformation.OSArchitecture is Architecture.X64);
public static bool IsArmLinux => IsLinux && IsArmMac; public static bool IsArmLinux => IsLinux && (RuntimeInformation.OSArchitecture is Architecture.Arm64);
} }
} }
+7 -14
View File
@@ -3,6 +3,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using System; using System;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Common namespace Ryujinx.Common
{ {
@@ -29,11 +30,10 @@ namespace Ryujinx.Common
public static readonly string[] GreatMetalTitles = public static readonly string[] GreatMetalTitles =
[ [
"01009b500007c000", // ARMS "010076f0049a2000", // Bayonetta
"0100a5c00d162000", // Cuphead "0100a5c00d162000", // Cuphead
"010023800d64a000", // Deltarune "010023800d64a000", // Deltarune
"01003a30012c0000", // LEGO City Undercover "01003a30012c0000", // LEGO City Undercover
"010048701995e000", // Luigi's Manion 2 HD
"010028600EBDA000", // Mario 3D World "010028600EBDA000", // Mario 3D World
"0100152000022000", // Mario Kart 8 Deluxe "0100152000022000", // Mario Kart 8 Deluxe
"010075a016a3a000", // Persona 4 Arena Ultimax "010075a016a3a000", // Persona 4 Arena Ultimax
@@ -47,15 +47,11 @@ namespace Ryujinx.Common
"01006f8002326000", // Animal Crossings: New Horizons "01006f8002326000", // Animal Crossings: New Horizons
"01009bf0072d4000", // Captain Toad: Treasure Tracker "01009bf0072d4000", // Captain Toad: Treasure Tracker
"01009510001ca000", // Fast RMX "01009510001ca000", // Fast RMX
"01005CA01580E000", // Persona 5 Royal "01005CA01580E000", // Persona 5 Royale
"0100b880154fc000", // Persona 5 The Royal (Japan)
"010015100b514000", // Super Mario Bros. Wonder
"0100000000010000", // Super Mario Odyssey "0100000000010000", // Super Mario Odyssey
// Further testing is appreciated, I did not test the entire game: //Isaac claims it has a issue in level 2, but I am not able to replicate it on my M3. More testing would be appreciated:
//"010076f0049a2000", // Bayonetta "010015100b514000", // Super Mario Bros. Wonder
//"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
//"0100f4300bf2c000", // New Pokemon Snap
]; ];
public static string GetDiscordGameAsset(string titleId) public static string GetDiscordGameAsset(string titleId)
@@ -163,16 +159,15 @@ namespace Ryujinx.Common
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere "0100ba0018500000", // Splatoon 3: Splatfest World Premiere
//NSO Membership games //NSO Membership games
"0100ccf019c8c000", // F-ZERO 99
"0100c62011050000", // GB - Nintendo Switch Online "0100c62011050000", // GB - Nintendo Switch Online
"010012f017576000", // GBA - Nintendo Switch Online "010012f017576000", // GBA - Nintendo Switch Online
"0100c9a00ece6000", // N64 - Nintendo Switch Online "0100c9a00ece6000", // N64 - Nintendo Switch Online
"0100e0601c632000", // N64 - Nintendo Switch Online 18+ "0100e0601c632000", // N64 - Nintendo Switch Online 18+
"0100d870045b6000", // NES - Nintendo Switch Online "0100d870045b6000", // NES - Nintendo Switch Online
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100ccf019c8c000", // F-ZERO 99
"0100ad9012510000", // PAC-MAN 99 "0100ad9012510000", // PAC-MAN 99
"010040600c5ce000", // Tetris 99 "010040600c5ce000", // Tetris 99
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100277011f1a000", // Super Mario Bros. 35 "0100277011f1a000", // Super Mario Bros. 35
//Misc Nintendo 1st party games //Misc Nintendo 1st party games
@@ -218,7 +213,6 @@ namespace Ryujinx.Common
//Misc Games //Misc Games
"010056e00853a000", // A Hat in Time "010056e00853a000", // A Hat in Time
"0100fd1014726000", // Baldurs Gate: Dark Alliance "0100fd1014726000", // Baldurs Gate: Dark Alliance
"01008c2019598000", // Bluey: The Video Game
"0100c6800b934000", // Brawlhalla "0100c6800b934000", // Brawlhalla
"0100dbf01000a000", // Burnout Paradise Remastered "0100dbf01000a000", // Burnout Paradise Remastered
"0100744001588000", // Cars 3: Driven to Win "0100744001588000", // Cars 3: Driven to Win
@@ -229,7 +223,6 @@ namespace Ryujinx.Common
"01008c8012920000", // Dying Light Platinum Edition "01008c8012920000", // Dying Light Platinum Edition
"01001cc01b2d4000", // Goat Simulator 3 "01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2 "01003620068ea000", // Hand of Fate 2
"0100f7e00c70e000", // Hogwarts Legacy
"010085500130a000", // Lego City: Undercover "010085500130a000", // Lego City: Undercover
"010073c01af34000", // LEGO Horizon Adventures "010073c01af34000", // LEGO Horizon Adventures
"0100d71004694000", // Minecraft "0100d71004694000", // Minecraft
+3 -4
View File
@@ -1,5 +1,4 @@
using Gommon; using Gommon;
using Ryujinx.Common.Helper;
using System; using System;
using System.Drawing; using System.Drawing;
using System.Threading; using System.Threading;
@@ -56,7 +55,7 @@ namespace Ryujinx.Common.Utilities
{ {
_color = HsbToRgb((_color.GetHue() + Speed) / 360); _color = HsbToRgb((_color.GetHue() + Speed) / 360);
_updatedHandler.Call(ref _color); _updatedHandler.Call(_color.ToArgb());
} }
} }
@@ -68,13 +67,13 @@ namespace Ryujinx.Common.Utilities
_color = Color.Blue; _color = Color.Blue;
} }
public static event RefEvent<Color>.Handler Updated public static event Action<int> Updated
{ {
add => _updatedHandler.Add(value); add => _updatedHandler.Add(value);
remove => _updatedHandler.Remove(value); remove => _updatedHandler.Remove(value);
} }
private static readonly RefEvent<Color> _updatedHandler = new(); private static readonly Event<int> _updatedHandler = new();
private static Color HsbToRgb(float hue, float saturation = 1, float brightness = 1) private static Color HsbToRgb(float hue, float saturation = 1, float brightness = 1)
{ {
@@ -1,5 +1,6 @@
using ARMeilleure.State; using ARMeilleure.State;
using Ryujinx.Memory; using Ryujinx.Memory;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
@@ -5,6 +5,7 @@ using Ryujinx.Memory.Tracking;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
+1
View File
@@ -1,4 +1,5 @@
using Ryujinx.Memory; using Ryujinx.Memory;
using System;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading; using System.Threading;
@@ -3,7 +3,7 @@ using Ryujinx.Memory.Tracking;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Memory namespace Ryujinx.Cpu
{ {
public interface IVirtualMemoryManagerTracked : IVirtualMemoryManager public interface IVirtualMemoryManagerTracked : IVirtualMemoryManager
{ {
+1
View File
@@ -2,6 +2,7 @@ using ARMeilleure.Common;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using Ryujinx.Cpu.Signal; using Ryujinx.Cpu.Signal;
using Ryujinx.Memory;
namespace Ryujinx.Cpu.Jit namespace Ryujinx.Cpu.Jit
{ {
+1
View File
@@ -5,6 +5,7 @@ using Ryujinx.Memory.Tracking;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
@@ -8,6 +8,7 @@ using Ryujinx.Memory.Tracking;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Cpu.Jit namespace Ryujinx.Cpu.Jit
@@ -3,6 +3,7 @@ using ARMeilleure.Memory;
using Ryujinx.Cpu.LightningJit.Arm32; using Ryujinx.Cpu.LightningJit.Arm32;
using Ryujinx.Cpu.LightningJit.Arm64; using Ryujinx.Cpu.LightningJit.Arm64;
using Ryujinx.Cpu.LightningJit.State; using Ryujinx.Cpu.LightningJit.State;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.LightningJit namespace Ryujinx.Cpu.LightningJit
@@ -2,6 +2,7 @@ using ARMeilleure.Common;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using Ryujinx.Cpu.LightningJit.CodeGen; using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Numerics; using System.Numerics;
@@ -1,4 +1,5 @@
using Ryujinx.Cpu.LightningJit.CodeGen; using Ryujinx.Cpu.LightningJit.CodeGen;
using System;
using System.Diagnostics; using System.Diagnostics;
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
@@ -1,3 +1,5 @@
using Ryujinx.Cpu.LightningJit.CodeGen;
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
{ {
static class InstEmitVfpMove static class InstEmitVfpMove
@@ -1,3 +1,5 @@
using System.Diagnostics;
namespace Ryujinx.Cpu.LightningJit.Arm64 namespace Ryujinx.Cpu.LightningJit.Arm64
{ {
static class SysUtils static class SysUtils
@@ -3,6 +3,7 @@ using ARMeilleure.Memory;
using Ryujinx.Cpu.LightningJit.CodeGen; using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using Ryujinx.Cpu.LightningJit.Graph; using Ryujinx.Cpu.LightningJit.Graph;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Numerics; using System.Numerics;
+33 -61
View File
@@ -1,6 +1,4 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using Humanizer;
using Ryujinx.Common.Logging;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -17,8 +15,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly int _pageMask = _pageSize - 1; private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4; // Bytes. private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 256 * 1024 * 1024; private const int CacheSize = 2047 * 1024 * 1024;
private static ReservedRegion _jitRegion;
private static JitCacheInvalidation _jitCacheInvalidator; private static JitCacheInvalidation _jitCacheInvalidator;
private static CacheMemoryAllocator _cacheAllocator; private static CacheMemoryAllocator _cacheAllocator;
@@ -27,8 +26,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly Lock _lock = new(); private static readonly Lock _lock = new();
private static bool _initialized; private static bool _initialized;
private static readonly List<ReservedRegion> _jitRegions = new();
private static int _activeRegionIndex = 0;
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)] [LibraryImport("kernel32.dll", SetLastError = true)]
@@ -48,9 +45,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
return; return;
} }
ReservedRegion firstRegion = new(allocator, CacheSize); _jitRegion = new ReservedRegion(allocator, CacheSize);
_jitRegions.Add(firstRegion);
_activeRegionIndex = 0;
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
{ {
@@ -70,8 +65,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
Debug.Assert(_initialized); Debug.Assert(_initialized);
int funcOffset = Allocate(code.Length); int funcOffset = Allocate(code.Length);
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
nint funcPtr = targetRegion.Pointer + funcOffset; nint funcPtr = _jitRegion.Pointer + funcOffset;
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{ {
@@ -85,11 +80,18 @@ namespace Ryujinx.Cpu.LightningJit.Cache
} }
else else
{ {
ReprotectAsWritable(targetRegion, funcOffset, code.Length); ReprotectAsWritable(funcOffset, code.Length);
Marshal.Copy(code.ToArray(), 0, funcPtr, code.Length); code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
ReprotectAsExecutable(targetRegion, funcOffset, code.Length); ReprotectAsExecutable(funcOffset, code.Length);
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length); if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
}
else
{
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
}
} }
Add(funcOffset, code.Length); Add(funcOffset, code.Length);
@@ -104,80 +106,50 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{ {
Debug.Assert(_initialized); Debug.Assert(_initialized);
foreach (ReservedRegion region in _jitRegions) int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
{ {
if (pointer.ToInt64() < region.Pointer.ToInt64() || _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64()) _cacheEntries.RemoveAt(entryIndex);
{
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(ReservedRegion region, int offset, int size) private static void ReprotectAsWritable(int offset, int size)
{ {
int endOffs = offset + size; int endOffs = offset + size;
int regionStart = offset & ~_pageMask; int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask; int regionEnd = (endOffs + _pageMask) & ~_pageMask;
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); _jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
} }
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size) private static void ReprotectAsExecutable(int offset, int size)
{ {
int endOffs = offset + size; int endOffs = offset + size;
int regionStart = offset & ~_pageMask; int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask; int regionEnd = (endOffs + _pageMask) & ~_pageMask;
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart)); _jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
} }
private static int Allocate(int codeSize) private static int Allocate(int codeSize)
{ {
codeSize = AlignCodeSize(codeSize); codeSize = AlignCodeSize(codeSize);
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++) int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset < 0)
{ {
int allocOffset = _cacheAllocator.Allocate(codeSize); throw new OutOfMemoryException("JIT Cache exhausted.");
if (allocOffset >= 0)
{
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
_activeRegionIndex = i;
return allocOffset;
}
} }
int exhaustedRegion = _activeRegionIndex; _jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
_jitRegions.Add(newRegion);
_activeRegionIndex = _jitRegions.Count - 1;
int newRegionNumber = _activeRegionIndex;
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); return allocOffset;
_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) private static int AlignCodeSize(int codeSize)
@@ -1,4 +1,5 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.LightningJit.Cache namespace Ryujinx.Cpu.LightningJit.Cache
@@ -1,3 +1,4 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{ {
private const int CodeAlignment = 4; // Bytes. private const int CodeAlignment = 4; // Bytes.
private const int SharedCacheSize = 2047 * 1024 * 1024; private const int SharedCacheSize = 2047 * 1024 * 1024;
private const int LocalCacheSize = 256 * 1024 * 1024; private const int LocalCacheSize = 128 * 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 // 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. // and allow the guest to take the fast path.
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Cpu.LightningJit namespace Ryujinx.Cpu.LightningJit
@@ -1,3 +1,5 @@
using System;
namespace Ryujinx.Cpu.LightningJit namespace Ryujinx.Cpu.LightningJit
{ {
class TranslatedFunction class TranslatedFunction
@@ -5,6 +5,7 @@ using Ryujinx.Cpu.LightningJit.Cache;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using Ryujinx.Cpu.LightningJit.State; using Ryujinx.Cpu.LightningJit.State;
using Ryujinx.Cpu.Signal; using Ryujinx.Cpu.Signal;
using Ryujinx.Memory;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@@ -1,3 +1,4 @@
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.Signal namespace Ryujinx.Cpu.Signal
@@ -1,4 +1,6 @@
using Ryujinx.Common.Logging;
using System; using System;
using System.Threading;
namespace Ryujinx.Graphics.Device namespace Ryujinx.Graphics.Device
{ {
@@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
// Make sure all pending uniform buffer data is written to memory. // Make sure all pending uniform buffer data is written to memory.
_3dEngine.FlushUboDirty(); _3dEngine.FlushUboDirty();
uint qmdAddress = _state.State.SendPcasA; uint qmdAddress = _state.State.SendPcasA;
ComputeQmd qmd = _channel.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8); ComputeQmd qmd = _channel.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
@@ -106,8 +106,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
ulong shaderGpuVa = ((ulong)_state.State.SetProgramRegionAAddressUpper << 32) | _state.State.SetProgramRegionB; ulong shaderGpuVa = ((ulong)_state.State.SetProgramRegionAAddressUpper << 32) | _state.State.SetProgramRegionB;
shaderGpuVa += (uint)qmd.ProgramOffset; shaderGpuVa += (uint)qmd.ProgramOffset;
ShaderCache shaderCache = memoryManager.GetBackingMemory(shaderGpuVa).ShaderCache;
int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize; int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize;
@@ -144,7 +142,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
sharedMemorySize, sharedMemorySize,
_channel.BufferManager.HasUnalignedStorageBuffers); _channel.BufferManager.HasUnalignedStorageBuffers);
CachedShaderProgram cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa); CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
_context.Renderer.Pipeline.SetProgram(cs.HostProgram); _context.Renderer.Pipeline.SetProgram(cs.HostProgram);
@@ -158,10 +156,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
{ {
BufferDescriptor sb = info.SBuffers[index]; BufferDescriptor sb = info.SBuffers[index];
(PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot); ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4; sbDescAddress += (ulong)sb.SbCbOffset * 4;
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress); SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
uint size; uint size;
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer) if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
@@ -189,7 +187,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
sharedMemorySize, sharedMemorySize,
_channel.BufferManager.HasUnalignedStorageBuffers); _channel.BufferManager.HasUnalignedStorageBuffers);
cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa); cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
_context.Renderer.Pipeline.SetProgram(cs.HostProgram); _context.Renderer.Pipeline.SetProgram(cs.HostProgram);
} }
@@ -215,10 +215,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
_channel.TextureManager.RefreshModifiedTextures(); _channel.TextureManager.RefreshModifiedTextures();
_3dEngine.CreatePendingSyncs(); _3dEngine.CreatePendingSyncs();
_3dEngine.FlushUboDirty(); _3dEngine.FlushUboDirty();
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcGpuVa);
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstGpuVa);
if (copy2D) if (copy2D)
{ {
// Buffer to texture copy. // Buffer to texture copy.
@@ -296,7 +293,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
if (completeSource && completeDest && !srcLinear && isIdentityRemap) if (completeSource && completeDest && !srcLinear && isIdentityRemap)
{ {
Image.Texture source = srcPhysical.TextureCache.FindTexture( Image.Texture source = memoryManager.Physical.TextureCache.FindTexture(
memoryManager, memoryManager,
srcGpuVa, srcGpuVa,
srcBpp, srcBpp,
@@ -312,7 +309,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
{ {
source.SynchronizeMemory(); source.SynchronizeMemory();
Image.Texture target = dstPhysical.TextureCache.FindOrCreateTexture( Image.Texture target = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager, memoryManager,
source.Info.FormatInfo, source.Info.FormatInfo,
dstGpuVa, dstGpuVa,
@@ -342,7 +339,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap) if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap)
{ {
Image.Texture target = dstPhysical.TextureCache.FindTexture( Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
memoryManager, memoryManager,
dstGpuVa, dstGpuVa,
dstBpp, dstBpp,
@@ -465,7 +462,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
} }
else else
{ {
BufferCache bufferCache = dstPhysical.BufferCache;
if (remap && if (remap &&
_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA && _state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA &&
_state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA && _state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA &&
@@ -476,7 +472,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
_state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four) _state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four)
{ {
// Fast path for clears when remap is enabled. // Fast path for clears when remap is enabled.
bufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA); memoryManager.Physical.BufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
} }
else else
{ {
@@ -496,7 +492,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
} }
else else
{ {
BufferCache.CopyBuffer(_context,memoryManager, srcGpuVa, dstGpuVa, size); memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
} }
} }
} }
@@ -185,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
// Right now the copy code at the bottom assumes that it is used on both which might be incorrect. // Right now the copy code at the bottom assumes that it is used on both which might be incorrect.
if (!_isLinear) if (!_isLinear)
{ {
Image.Texture target = memoryManager.GetBackingMemory(_dstGpuVa).TextureCache.FindTexture( Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
memoryManager, memoryManager,
_dstGpuVa, _dstGpuVa,
1, 1,
@@ -384,7 +384,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
ulong indirectBufferGpuVa = count.GpuVa; ulong indirectBufferGpuVa = count.GpuVa;
BufferCache bufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache; BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress); bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
@@ -394,8 +394,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
_processor.ThreedClass.DrawIndirect( _processor.ThreedClass.DrawIndirect(
topology, topology,
bufferCache,
null,
new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize), new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
default, default,
1, 1,
@@ -493,24 +491,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
} }
} }
} }
BufferCache indirectBufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
BufferCache parameterBufferCache = _processor.MemoryManager.GetBackingMemory(parameterBufferGpuVa).BufferCache; BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride; ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
MultiRange indirectBufferRange = indirectBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect); MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
MultiRange parameterBufferRange = parameterBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect); MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
_processor.ThreedClass.DrawIndirect( _processor.ThreedClass.DrawIndirect(
topology, topology,
indirectBufferCache,
parameterBufferCache,
indirectBufferRange, indirectBufferRange,
parameterBufferRange, parameterBufferRange,
maxDrawCount, maxDrawCount,
stride, stride,
indexCount, indexCount,
IndirectDrawType.DrawIndexedIndirectCount); Threed.IndirectDrawType.DrawIndexedIndirectCount);
} }
/// <summary> /// <summary>
@@ -1,5 +1,6 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
@@ -11,8 +11,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
/// </summary> /// </summary>
class VtgAsComputeContext : IDisposable class VtgAsComputeContext : IDisposable
{ {
private const int DummyBufferSize = 16;
private readonly GpuContext _context; private readonly GpuContext _context;
/// <summary> /// <summary>
@@ -48,7 +46,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
1, 1,
1, 1,
1, 1,
1, format.GetBytesPerElement(),
format, format,
DepthStencilMode.Depth, DepthStencilMode.Depth,
Target.TextureBuffer, Target.TextureBuffer,
@@ -521,21 +519,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write); 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> /// <summary>
/// Gets the range for a sequential index buffer, with ever incrementing index values. /// Gets the range for a sequential index buffer, with ever incrementing index values.
/// </summary> /// </summary>
@@ -147,7 +147,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{ {
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
continue; continue;
} }
@@ -163,15 +162,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{ {
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
continue; continue;
} }
int vbStride = vertexBuffer.UnpackStride(); int vbStride = vertexBuffer.UnpackStride();
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count); ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
ulong oldVbSize = vbSize;
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset(); ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
int componentSize = format.GetScalarSize(); int componentSize = format.GetScalarSize();
@@ -200,11 +196,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding; int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize); BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) }); _context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding; int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true); BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) }); _context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
_vacContext.VertexInfoBufferUpdater.Commit(); _vacContext.VertexInfoBufferUpdater.Commit();
@@ -232,7 +228,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding; int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize); BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) }); _context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding; int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
@@ -250,12 +246,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
BufferRange vertexBuffer = _vacContext.GetGeometryVertexDataBufferRange(_geometryVertexDataOffset, _geometryVertexDataSize, write: true); BufferRange vertexBuffer = _vacContext.GetGeometryVertexDataBufferRange(_geometryVertexDataOffset, _geometryVertexDataSize, write: true);
BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true); BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] _context.Renderer.Pipeline.SetStorageBuffers([
{
new BufferAssignment(vertexDataBinding, vertexDataRange), new BufferAssignment(vertexDataBinding, vertexDataRange),
new BufferAssignment(geometryVbBinding, vertexBuffer), new BufferAssignment(geometryVbBinding, vertexBuffer),
new BufferAssignment(geometryIbBinding, indexBuffer), new BufferAssignment(geometryIbBinding, indexBuffer)
}); ]);
_context.Renderer.Pipeline.DispatchCompute( _context.Renderer.Pipeline.DispatchCompute(
BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize), BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize),
@@ -299,7 +294,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram); _context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
_context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt); _context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexBuffer) }); _context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexBuffer)]);
_context.Renderer.Pipeline.SetPrimitiveRestart(true, -1); _context.Renderer.Pipeline.SetPrimitiveRestart(true, -1);
_context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive)); _context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive));
@@ -314,7 +309,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false); BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false);
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram); _context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) }); _context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
_context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0); _context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0);
} }
} }
@@ -345,20 +340,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
return maxOutputVertices / verticesPerPrimitive; 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> /// <summary>
/// Binds a vertex buffer into a buffer texture. /// Binds a vertex buffer into a buffer texture.
/// </summary> /// </summary>
@@ -371,7 +352,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{ {
MemoryManager memoryManager = _channel.MemoryManager; MemoryManager memoryManager = _channel.MemoryManager;
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer); BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer);
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format); ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
bufferTexture.SetStorage(range); bufferTexture.SetStorage(range);
@@ -413,7 +394,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
MemoryManager memoryManager = _channel.MemoryManager; MemoryManager memoryManager = _channel.MemoryManager;
ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1); ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1);
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange( BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(
memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign), memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign),
BufferStage.IndexBuffer); BufferStage.IndexBuffer);
misalignedOffset = (int)misalign >> shift; misalignedOffset = (int)misalign >> shift;
@@ -16,7 +16,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// State associated with direct uniform buffer updates. // State associated with direct uniform buffer updates.
// This state is used to attempt to batch together consecutive updates. // This state is used to attempt to batch together consecutive updates.
private ulong _ubBeginGpuAddress = 0;
private ulong _ubBeginCpuAddress = 0; private ulong _ubBeginCpuAddress = 0;
private ulong _ubFollowUpAddress = 0; private ulong _ubFollowUpAddress = 0;
private ulong _ubByteCount = 0; private ulong _ubByteCount = 0;
@@ -114,13 +113,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (_ubFollowUpAddress != 0) if (_ubFollowUpAddress != 0)
{ {
MemoryManager memoryManager = _channel.MemoryManager; MemoryManager memoryManager = _channel.MemoryManager;
PhysicalMemory physicalMemory = memoryManager.GetBackingMemory(_ubBeginGpuAddress);
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4))); Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
if (physicalMemory.WriteWithRedundancyCheck(_ubBeginCpuAddress, data)) if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
{ {
physicalMemory.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount); memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
} }
_ubFollowUpAddress = 0; _ubFollowUpAddress = 0;
@@ -641,8 +641,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public void DrawIndirect( public void DrawIndirect(
ThreedClass engine, ThreedClass engine,
PrimitiveTopology topology, PrimitiveTopology topology,
BufferCache indirectBufferCache,
BufferCache parameterBufferCache,
MultiRange indirectBufferRange, MultiRange indirectBufferRange,
MultiRange parameterBufferRange, MultiRange parameterBufferRange,
int maxDrawCount, int maxDrawCount,
@@ -664,6 +662,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return; return;
} }
PhysicalMemory memory = _channel.MemoryManager.Physical;
bool hasCount = (drawType & IndirectDrawType.Count) != 0; bool hasCount = (drawType & IndirectDrawType.Count) != 0;
bool indexed = (drawType & IndirectDrawType.Indexed) != 0; bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
@@ -684,8 +684,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (hasCount) if (hasCount)
{ {
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect); BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
BufferRange parameterBuffer = parameterBufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect); BufferRange parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
if (indexed) if (indexed)
{ {
@@ -698,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
else else
{ {
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect); BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
if (indexed) if (indexed)
{ {
@@ -1,3 +1,4 @@
using Ryujinx.Graphics.Device;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -381,10 +381,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
BufferDescriptor sb = info.SBuffers[index]; BufferDescriptor sb = info.SBuffers[index];
(PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot); ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4; sbDescAddress += (ulong)sb.SbCbOffset * 4;
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress); SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
uint size; uint size;
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer) if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
@@ -505,9 +505,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
rtNoAlphaMask |= 1u << index; rtNoAlphaMask |= 1u << index;
} }
TextureCache colorTextureCache = memoryManager.GetBackingMemory(colorState.Address.Pack()).TextureCache; Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
Image.Texture color = colorTextureCache.FindOrCreateTexture(
memoryManager, memoryManager,
colorState, colorState,
_vtgWritesRtLayer || layered, _vtgWritesRtLayer || layered,
@@ -515,7 +513,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
samplesInX, samplesInX,
samplesInY, samplesInY,
sizeHint); sizeHint);
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color); changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
if (color != null) if (color != null)
@@ -545,9 +543,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
RtDepthStencilState dsState = _state.State.RtDepthStencilState; RtDepthStencilState dsState = _state.State.RtDepthStencilState;
Size3D dsSize = _state.State.RtDepthStencilSize; Size3D dsSize = _state.State.RtDepthStencilSize;
TextureCache dsTextureCache = memoryManager.GetBackingMemory(dsState.Address.Pack()).TextureCache;
depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
depthStencil = dsTextureCache.FindOrCreateTexture(
memoryManager, memoryManager,
dsState, dsState,
dsSize, dsSize,
@@ -1412,6 +1409,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
private void UpdateShaderState() private void UpdateShaderState()
{ {
ShaderCache shaderCache = _channel.MemoryManager.Physical.ShaderCache;
_vtgWritesRtLayer = false; _vtgWritesRtLayer = false;
ShaderAddresses addresses = new(); ShaderAddresses addresses = new();
@@ -1434,9 +1433,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
? _state.State.TexturePoolState.MaximumId ? _state.State.TexturePoolState.MaximumId
: _state.State.SamplerPoolState.MaximumId; : _state.State.SamplerPoolState.MaximumId;
// Shader stages on different address spaces are not supported right now,
// but it should never happen in practice anyway.
ShaderCache shaderCache = _channel.MemoryManager.GetBackingMemory(addresses.VertexB).ShaderCache;
CachedShaderProgram gs = shaderCache.GetGraphicsShader( CachedShaderProgram gs = shaderCache.GetGraphicsShader(
ref _state.State, ref _state.State,
ref _pipeline, ref _pipeline,
@@ -5,7 +5,6 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory; using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender; using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Synchronization; using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
@@ -805,8 +804,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Performs a indirect draw, with parameters from a GPU buffer. /// Performs a indirect draw, with parameters from a GPU buffer.
/// </summary> /// </summary>
/// <param name="topology">Primitive topology</param> /// <param name="topology">Primitive topology</param>
/// <param name="indirectBufferCache">Buffer cache owning the buffer with the draw parameters</param>
/// <param name="parameterBufferCache">Buffer cache owning the buffer with the draw count</param>
/// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param> /// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param> /// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param> /// <param name="maxDrawCount">Maximum number of draws that can be made</param>
@@ -815,8 +812,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param> /// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
public void DrawIndirect( public void DrawIndirect(
PrimitiveTopology topology, PrimitiveTopology topology,
BufferCache indirectBufferCache,
BufferCache parameterBufferCache,
MultiRange indirectBufferRange, MultiRange indirectBufferRange,
MultiRange parameterBufferRange, MultiRange parameterBufferRange,
int maxDrawCount, int maxDrawCount,
@@ -824,7 +819,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int indexCount, int indexCount,
IndirectDrawType drawType) IndirectDrawType drawType)
{ {
_drawManager.DrawIndirect(this, topology, indirectBufferCache, parameterBufferCache, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType); _drawManager.DrawIndirect(this, topology, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType);
} }
/// <summary> /// <summary>
@@ -233,9 +233,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
TwodTexture dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat); TwodTexture dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
TwodTexture srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat); TwodTexture srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
TextureCache srcTextureCache = memoryManager.GetBackingMemory(srcCopyTexture.Address.Pack()).TextureCache;
TextureCache dstTextureCache = memoryManager.GetBackingMemory(dstCopyTexture.Address.Pack()).TextureCache;
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac; long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac; long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
@@ -308,7 +305,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
// are the same, as we can't blit between different depth formats. // are the same, as we can't blit between different depth formats.
bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format; bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format;
Image.Texture srcTexture = srcTextureCache.FindOrCreateTexture( Image.Texture srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager, memoryManager,
srcCopyTexture, srcCopyTexture,
offset, offset,
@@ -329,7 +326,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
return; return;
} }
srcTextureCache.Lift(srcTexture); memoryManager.Physical.TextureCache.Lift(srcTexture);
// When the source texture that was found has a depth format, // When the source texture that was found has a depth format,
// we must enforce the target texture also has a depth format, // we must enforce the target texture also has a depth format,
@@ -345,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
dstCopyTextureFormat = dstCopyTexture.Format.Convert(); dstCopyTextureFormat = dstCopyTexture.Format.Convert();
} }
Image.Texture dstTexture = dstTextureCache.FindOrCreateTexture( Image.Texture dstTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager, memoryManager,
dstCopyTexture, dstCopyTexture,
0, 0,
+8 -9
View File
@@ -58,24 +58,22 @@ namespace Ryujinx.Graphics.Gpu
public void BindMemory(MemoryManager memoryManager) public void BindMemory(MemoryManager memoryManager)
{ {
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager))); MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
if (oldMemoryManager == memoryManager)
{
return;
}
memoryManager.AttachToChannel(BufferManager.Rebind); memoryManager.Physical.IncrementReferenceCount();
if (oldMemoryManager != null) if (oldMemoryManager != null)
{ {
oldMemoryManager.DetachFromChannel(BufferManager.Rebind); oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler; oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
} }
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
memoryManager.MemoryUnmapped += MemoryUnmappedHandler; memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager. // Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
TextureManager.ReloadPools(); TextureManager.ReloadPools();
memoryManager.QueuePrune(); memoryManager.Physical.BufferCache.QueuePrune();
} }
/// <summary> /// <summary>
@@ -88,7 +86,7 @@ namespace Ryujinx.Graphics.Gpu
TextureManager.ReloadPools(); TextureManager.ReloadPools();
MemoryManager memoryManager = Volatile.Read(ref _memoryManager); MemoryManager memoryManager = Volatile.Read(ref _memoryManager);
memoryManager?.QueuePrune(); memoryManager?.Physical.BufferCache.QueuePrune();
} }
/// <summary> /// <summary>
@@ -143,7 +141,8 @@ namespace Ryujinx.Graphics.Gpu
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null); MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
if (oldMemoryManager != null) if (oldMemoryManager != null)
{ {
oldMemoryManager.DetachFromChannel(BufferManager.Rebind); oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler; oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
} }
} }
+2 -3
View File
@@ -6,7 +6,6 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Gpu.Synchronization; using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@@ -173,7 +172,7 @@ namespace Ryujinx.Graphics.Gpu
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid)); throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
} }
return new MemoryManager(this, physicalMemory, cpuMemorySize); return new MemoryManager(physicalMemory, cpuMemorySize);
} }
/// <summary> /// <summary>
@@ -198,7 +197,7 @@ namespace Ryujinx.Graphics.Gpu
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param> /// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
/// <param name="cpuMemory">Virtual memory owned by the process</param> /// <param name="cpuMemory">Virtual memory owned by the process</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception> /// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
public void RegisterProcess(ulong pid, IVirtualMemoryManagerTracked cpuMemory) public void RegisterProcess(ulong pid, Cpu.IVirtualMemoryManagerTracked cpuMemory)
{ {
PhysicalMemory physicalMemory = new(this, cpuMemory); PhysicalMemory physicalMemory = new(this, cpuMemory);
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory)) if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))
+3 -5
View File
@@ -1,4 +1,3 @@
using Ryujinx.Graphics.Gpu.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -65,7 +64,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="maximumId">Maximum ID of the texture pool</param> /// <param name="maximumId">Maximum ID of the texture pool</param>
/// <param name="bindingsArrayCache">Cache of texture array bindings</param> /// <param name="bindingsArrayCache">Cache of texture array bindings</param>
/// <returns>The found or newly created texture pool</returns> /// <returns>The found or newly created texture pool</returns>
public T FindOrCreate(GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache) public T FindOrCreate(GpuChannel channel, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
{ {
// Remove old entries from the cache, if possible. // Remove old entries from the cache, if possible.
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval) while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
@@ -100,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
// If not found, create a new one. // If not found, create a new one.
pool = CreatePool(_context, channel, physicalMemory, address, maximumId); pool = CreatePool(_context, channel, address, maximumId);
pool.CacheNode = _pools.AddLast(pool); pool.CacheNode = _pools.AddLast(pool);
pool.CacheTimestamp = _currentTimestamp; pool.CacheTimestamp = _currentTimestamp;
@@ -113,10 +112,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="context">GPU context that the pool belongs to</param> /// <param name="context">GPU context that the pool belongs to</param>
/// <param name="channel">GPU channel that the pool belongs to</param> /// <param name="channel">GPU channel that the pool belongs to</param>
/// <param name="physicalMemory">GPU backing memory of the pool</param>
/// <param name="address">Address of the pool in guest memory</param> /// <param name="address">Address of the pool in guest memory</param>
/// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param> /// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
protected abstract T CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId); protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId);
public void Dispose() public void Dispose()
{ {
@@ -1,5 +1,3 @@
using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
/// <summary> /// <summary>
@@ -22,12 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="context">GPU context that the sampler pool belongs to</param> /// <param name="context">GPU context that the sampler pool belongs to</param>
/// <param name="channel">GPU channel that the texture pool belongs to</param> /// <param name="channel">GPU channel that the texture pool belongs to</param>
/// <param name="physicalMemory">GPU backing memory of the pool</param>
/// <param name="address">Address of the sampler pool in guest memory</param> /// <param name="address">Address of the sampler pool in guest memory</param>
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param> /// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId) protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
{ {
return new SamplerPool(context, physicalMemory, address, maximumId); return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId);
} }
} }
} }
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
@@ -660,7 +661,6 @@ namespace Ryujinx.Graphics.Gpu.Image
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength]; ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
ITexture[] textures = new ITexture[bindingInfo.ArrayLength]; ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
BufferCache bufferCache = null;
for (int index = 0; index < length; index++) for (int index = 0; index < length; index++)
{ {
@@ -674,7 +674,7 @@ namespace Ryujinx.Graphics.Gpu.Image
else else
{ {
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture); ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
if (texture != null) if (texture != null)
{ {
entry.Textures[texture] = texture.InvalidatedSequence; entry.Textures[texture] = texture.InvalidatedSequence;
@@ -703,10 +703,11 @@ namespace Ryujinx.Graphics.Gpu.Image
// to ensure we're not using a old buffer that was already deleted. // to ensure we're not using a old buffer that was already deleted.
if (isImage) if (isImage)
{ {
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); } _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
}
else else
{ {
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
} }
} }
else if (isImage) else if (isImage)
@@ -797,11 +798,11 @@ namespace Ryujinx.Graphics.Gpu.Image
return; return;
} }
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range)); cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
if (separateSamplerBuffer) if (separateSamplerBuffer)
{ {
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range)); cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
} }
else else
{ {
@@ -828,10 +829,11 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
else else
{ {
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range)); cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
if (separateSamplerBuffer) if (separateSamplerBuffer)
{ {
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range)); cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
} }
else else
{ {
@@ -900,18 +902,16 @@ namespace Ryujinx.Graphics.Gpu.Image
if (hostTexture != null && texture.Target == Target.TextureBuffer) if (hostTexture != null && texture.Target == Target.TextureBuffer)
{ {
BufferCache bufferCache = textureBufferBounds.BufferCache;
// Ensure that the buffer texture is using the correct buffer as storage. // Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind // Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted. // to ensure we're not using a old buffer that was already deleted.
if (isImage) if (isImage)
{ {
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
} }
else else
{ {
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
} }
} }
else if (isImage) else if (isImage)
@@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex); ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range)); cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedTextureBufferIndex = textureBufferIndex; cachedTextureBufferIndex = textureBufferIndex;
if (samplerBufferIndex == textureBufferIndex) if (samplerBufferIndex == textureBufferIndex)
@@ -410,7 +410,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex); ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range)); cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedSamplerBufferIndex = samplerBufferIndex; cachedSamplerBufferIndex = samplerBufferIndex;
} }
} }
@@ -524,8 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// Ensure that the buffer texture is using the correct buffer as storage. // Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind // Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted. // to ensure we're not using a old buffer that was already deleted.
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache; _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, false);
// Cache is not used for buffer texture, it must always rebind. // Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null; state.CachedTexture = null;
@@ -660,8 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind // Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted. // to ensure we're not using a old buffer that was already deleted.
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache; _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, true);
// Cache is not used for buffer texture, it must always rebind. // Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null; state.CachedTexture = null;
@@ -717,10 +715,9 @@ namespace Ryujinx.Graphics.Gpu.Image
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex); int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
int textureId = TextureHandle.UnpackTextureId(packedId); int textureId = TextureHandle.UnpackTextureId(packedId);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolGpuVa);
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache); TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
TextureDescriptor descriptor; TextureDescriptor descriptor;
@@ -754,12 +751,12 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset); (int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
(PhysicalMemory texturePhysicalMemory, ulong textureBufferAddress) = _isCompute ulong textureBufferAddress = _isCompute
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex) ? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex); : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
int handle = textureBufferAddress != MemoryManager.PteUnmapped int handle = textureBufferAddress != MemoryManager.PteUnmapped
? texturePhysicalMemory.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4) ? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
: 0; : 0;
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader) // The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
@@ -774,12 +771,12 @@ namespace Ryujinx.Graphics.Gpu.Image
if (handleType != TextureHandleType.SeparateConstantSamplerHandle) if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
{ {
(PhysicalMemory samplerPhysicalMemory, ulong samplerBufferAddress) = _isCompute ulong samplerBufferAddress = _isCompute
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex) ? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex); : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped
? samplerPhysicalMemory.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4) ? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
: 0; : 0;
} }
else else
@@ -816,8 +813,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (poolAddress != MemoryManager.PteUnmapped) if (poolAddress != MemoryManager.PteUnmapped)
{ {
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_texturePoolGpuVa); texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
_texturePool = texturePool; _texturePool = texturePool;
} }
} }
@@ -828,8 +824,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (poolAddress != MemoryManager.PteUnmapped) if (poolAddress != MemoryManager.PteUnmapped)
{ {
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_samplerPoolGpuVa); samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
samplerPool = _samplerPoolCache.FindOrCreate(_channel, physical, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
_samplerPool = samplerPool; _samplerPool = samplerPool;
} }
} }
@@ -1,6 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
using System; using System;
@@ -386,9 +385,8 @@ namespace Ryujinx.Graphics.Gpu.Image
public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId) public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
{ {
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolAddress);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache); TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
return texturePool; return texturePool;
} }
+5 -11
View File
@@ -160,10 +160,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param> /// <param name="context">GPU context that the texture pool belongs to</param>
/// <param name="channel">GPU channel that the texture pool belongs to</param> /// <param name="channel">GPU channel that the texture pool belongs to</param>
/// <param name="physicalMemory">Backing memory of the pool</param>
/// <param name="address">Address of the texture pool in guest memory</param> /// <param name="address">Address of the texture pool in guest memory</param>
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param> /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
public TexturePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId) public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
{ {
_channel = channel; _channel = channel;
_aliasLists = new Dictionary<Texture, TextureAliasList>(); _aliasLists = new Dictionary<Texture, TextureAliasList>();
@@ -194,9 +193,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
TextureInfo info = GetInfo(descriptor, out int layerSize); TextureInfo info = GetInfo(descriptor, out int layerSize);
MemoryManager memoryManager = _channel.MemoryManager; texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache;
texture = textureCache.FindOrCreateTexture(memoryManager, TextureSearchFlags.ForSampler, info, layerSize);
// If this happens, then the texture address is invalid, we can't add it to the cache. // If this happens, then the texture address is invalid, we can't add it to the cache.
if (texture == null) if (texture == null)
@@ -424,8 +421,7 @@ namespace Ryujinx.Graphics.Gpu.Image
continue; continue;
} }
TextureCache textureCache = _channel.MemoryManager.GetBackingMemory(address).TextureCache; MultiRange range = _channel.MemoryManager.Physical.TextureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
MultiRange range = textureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
// If the texture is not mapped at all, delete its reference. // If the texture is not mapped at all, delete its reference.
@@ -450,7 +446,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!range.Equals(texture.Range)) if (!range.Equals(texture.Range))
{ {
// Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles. // Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
if (!textureCache.UpdateMapping(texture, range)) if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range))
{ {
// Texture could not be remapped due to a collision, just delete it. // Texture could not be remapped due to a collision, just delete it.
if (Interlocked.Exchange(ref Items[request.ID], null) != null) if (Interlocked.Exchange(ref Items[request.ID], null) != null)
@@ -485,7 +481,6 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="size">Size of the range being invalidated</param> /// <param name="size">Size of the range being invalidated</param>
protected override void InvalidateRangeImpl(ulong address, ulong size) protected override void InvalidateRangeImpl(ulong address, ulong size)
{ {
MemoryManager memoryManager = _channel.MemoryManager;
ProcessDereferenceQueue(); ProcessDereferenceQueue();
ulong endAddress = address + size; ulong endAddress = address + size;
@@ -510,8 +505,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (texture.HasOneReference()) if (texture.HasOneReference())
{ {
TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache; _channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
textureCache.AddShortCache(texture, ref cachedDescriptor);
} }
if (Interlocked.Exchange(ref Items[id], null) != null) if (Interlocked.Exchange(ref Items[id], null) != null)
@@ -1,5 +1,3 @@
using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
/// <summary> /// <summary>
@@ -22,17 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param> /// <param name="context">GPU context that the texture pool belongs to</param>
/// <param name="channel">GPU channel that the texture pool belongs to</param> /// <param name="channel">GPU channel that the texture pool belongs to</param>
/// <param name="physicalMemory">Backing memory of the pool</param>
/// <param name="address">Address of the texture pool in guest memory</param> /// <param name="address">Address of the texture pool in guest memory</param>
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param> /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
protected override TexturePool CreatePool( protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
GpuContext context,
GpuChannel channel,
PhysicalMemory physicalMemory,
ulong address,
int maximumId)
{ {
return new TexturePool(context, channel, physicalMemory, address, maximumId); return new TexturePool(context, channel, address, maximumId);
} }
} }
} }
@@ -9,16 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
readonly struct BufferBounds : IEquatable<BufferBounds> readonly struct BufferBounds : IEquatable<BufferBounds>
{ {
/// <summary>
/// Physical memory backing the buffer.
/// </summary>
public PhysicalMemory Physical { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache => Physical.BufferCache;
/// <summary> /// <summary>
/// Physical memory ranges where the buffer is mapped. /// Physical memory ranges where the buffer is mapped.
/// </summary> /// </summary>
@@ -39,9 +29,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
/// <param name="range">Physical memory ranges where the buffer is mapped</param> /// <param name="range">Physical memory ranges where the buffer is mapped</param>
/// <param name="flags">Buffer usage flags</param> /// <param name="flags">Buffer usage flags</param>
public BufferBounds(PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None) public BufferBounds(MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
{ {
Physical = physical;
Range = range; Range = range;
Flags = flags; Flags = flags;
} }
+11 -23
View File
@@ -735,22 +735,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <remarks> /// <remarks>
/// This does a GPU side copy. /// This does a GPU side copy.
/// </remarks> /// </remarks>
/// <param name="context">GPU context</param>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param> /// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="srcVa">GPU virtual address of the copy source</param> /// <param name="srcVa">GPU virtual address of the copy source</param>
/// <param name="dstVa">GPU virtual address of the copy destination</param> /// <param name="dstVa">GPU virtual address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param> /// <param name="size">Size in bytes of the copy</param>
public static void CopyBuffer(GpuContext context, MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size) public void CopyBuffer(MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
{ {
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcVa); MultiRange srcRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, srcVa, size, BufferStage.Copy);
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstVa); MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size, BufferStage.Copy);
MultiRange srcRange = srcPhysical.BufferCache.TranslateAndCreateBuffer(memoryManager, srcVa, size, BufferStage.Copy);
MultiRange dstRange = dstPhysical.BufferCache.TranslateAndCreateBuffer(memoryManager, dstVa, size, BufferStage.Copy);
if (srcRange.Count == 1 && dstRange.Count == 1) if (srcRange.Count == 1 && dstRange.Count == 1)
{ {
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size); CopyBufferSingleRange(memoryManager, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size);
} }
else else
{ {
@@ -781,7 +777,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong dstSize = dstSubRange.Size - dstOffset; ulong dstSize = dstSubRange.Size - dstOffset;
ulong copySize = Math.Min(srcSize, dstSize); ulong copySize = Math.Min(srcSize, dstSize);
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize); CopyBufferSingleRange(memoryManager, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
srcOffset += copySize; srcOffset += copySize;
dstOffset += copySize; dstOffset += copySize;
@@ -797,26 +793,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// This does a GPU side copy. /// This does a GPU side copy.
/// </remarks> /// </remarks>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param> /// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="srcPhysical">Physical memory backing the source buffer.</param>
/// <param name="dstPhysical">Physical memory backing the destination buffer.</param>
/// <param name="srcAddress">Physical address of the copy source</param> /// <param name="srcAddress">Physical address of the copy source</param>
/// <param name="dstAddress">Physical address of the copy destination</param> /// <param name="dstAddress">Physical address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param> /// <param name="size">Size in bytes of the copy</param>
private static void CopyBufferSingleRange( private void CopyBufferSingleRange(MemoryManager memoryManager, ulong srcAddress, ulong dstAddress, ulong size)
GpuContext context,
PhysicalMemory srcPhysical,
PhysicalMemory dstPhysical,
ulong srcAddress,
ulong dstAddress,
ulong size)
{ {
Buffer srcBuffer = srcPhysical.BufferCache.GetBuffer(srcAddress, size, BufferStage.Copy); Buffer srcBuffer = GetBuffer(srcAddress, size, BufferStage.Copy);
Buffer dstBuffer = dstPhysical.BufferCache.GetBuffer(dstAddress, size, BufferStage.Copy); Buffer dstBuffer = GetBuffer(dstAddress, size, BufferStage.Copy);
int srcOffset = (int)(srcAddress - srcBuffer.Address); int srcOffset = (int)(srcAddress - srcBuffer.Address);
int dstOffset = (int)(dstAddress - dstBuffer.Address); int dstOffset = (int)(dstAddress - dstBuffer.Address);
context.Renderer.Pipeline.CopyBuffer( _context.Renderer.Pipeline.CopyBuffer(
srcBuffer.Handle, srcBuffer.Handle,
dstBuffer.Handle, dstBuffer.Handle,
srcOffset, srcOffset,
@@ -832,7 +820,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU. // Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
dstBuffer.ClearModified(dstAddress, size); dstBuffer.ClearModified(dstAddress, size);
dstPhysical.WriteTrackedResource(dstAddress, srcPhysical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer); memoryManager.Physical.WriteTrackedResource(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
} }
dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size); dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size);
@@ -861,7 +849,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value); _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
memoryManager.GetBackingMemory(gpuVa).FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer); memoryManager.Physical.FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size); buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size);
} }
@@ -66,19 +66,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
Buffers = new BufferBounds[count]; Buffers = new BufferBounds[count];
Unaligned = new bool[count]; Unaligned = new bool[count];
Buffers.AsSpan().Fill(new BufferBounds(null, new MultiRange(MemoryManager.PteUnmapped, 0UL))); Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL)));
} }
/// <summary> /// <summary>
/// Sets the region of a buffer at a given slot. /// Sets the region of a buffer at a given slot.
/// </summary> /// </summary>
/// <param name="index">Buffer slot</param> /// <param name="index">Buffer slot</param>
/// <param name="physical">Physical memory backing the buffer</param>
/// <param name="range">Physical memory regions where the buffer is mapped</param> /// <param name="range">Physical memory regions where the buffer is mapped</param>
/// <param name="flags">Buffer usage flags</param> /// <param name="flags">Buffer usage flags</param>
public void SetBounds(int index, PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None) public void SetBounds(int index, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
{ {
Buffers[index] = new BufferBounds(physical, range, flags); Buffers[index] = new BufferBounds(range, flags);
} }
/// <summary> /// <summary>
@@ -157,10 +156,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="type">Type of each index buffer element</param> /// <param name="type">Type of each index buffer element</param>
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type) public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
{ {
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache; MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer);
MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer);
_indexBuffer.BufferCache = bufferCache;
_indexBuffer.Range = range; _indexBuffer.Range = range;
_indexBuffer.Type = type; _indexBuffer.Type = type;
@@ -189,15 +186,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param> /// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor) public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
{ {
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache; MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer);
MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer);
ref VertexBuffer vb = ref _vertexBuffers[index]; _vertexBuffers[index].Range = range;
_vertexBuffers[index].Stride = stride;
vb.BufferCache = bufferCache; _vertexBuffers[index].Divisor = divisor;
vb.Range = range;
vb.Stride = stride;
vb.Divisor = divisor;
_vertexBuffersDirty = true; _vertexBuffersDirty = true;
@@ -220,10 +213,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the transform feedback buffer</param> /// <param name="size">Size in bytes of the transform feedback buffer</param>
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
{ {
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback);
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback);
_transformFeedbackBuffers[index] = new BufferBounds(physical, range); _transformFeedbackBuffers[index] = new BufferBounds(range);
_transformFeedbackBuffersDirty = true; _transformFeedbackBuffersDirty = true;
} }
@@ -266,12 +258,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa); RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
_cpStorageBuffers.SetBounds(index, physical, range, flags); _cpStorageBuffers.SetBounds(index, range, flags);
} }
/// <summary> /// <summary>
@@ -291,17 +282,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(buffers, index, gpuVa); RecordStorageAlignment(buffers, index, gpuVa);
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
if (!buffers.Buffers[index].Range.Equals(range)) if (!buffers.Buffers[index].Range.Equals(range))
{ {
_gpStorageBuffersDirty = true; _gpStorageBuffersDirty = true;
} }
buffers.SetBounds(index, physical, range, flags); buffers.SetBounds(index, range, flags);
} }
/// <summary> /// <summary>
@@ -313,10 +303,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param> /// <param name="size">Size in bytes of the storage buffer</param>
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size) public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
{ {
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute);
MultiRange range = physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute);
_cpUniformBuffers.SetBounds(index, physical, range); _cpUniformBuffers.SetBounds(index, range);
} }
/// <summary> /// <summary>
@@ -329,10 +318,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param> /// <param name="size">Size in bytes of the storage buffer</param>
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size) public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
{ {
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage));
MultiRange range = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage));
_gpUniformBuffers[stage].SetBounds(index, physical, range); _gpUniformBuffers[stage].SetBounds(index, range);
_gpUniformBuffersDirty = true; _gpUniformBuffersDirty = true;
} }
@@ -428,10 +416,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
/// <param name="index">Index of the uniform buffer binding</param> /// <param name="index">Index of the uniform buffer binding</param>
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns> /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
public (PhysicalMemory, ulong) GetComputeUniformBufferAddress(int index) public ulong GetComputeUniformBufferAddress(int index)
{ {
ref BufferBounds buffer = ref _cpUniformBuffers.Buffers[index]; return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address;
return (buffer.Physical, buffer.Range.GetSubRange(0).Address);
} }
/// <summary> /// <summary>
@@ -450,10 +437,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">Index of the shader stage</param> /// <param name="stage">Index of the shader stage</param>
/// <param name="index">Index of the uniform buffer binding</param> /// <param name="index">Index of the uniform buffer binding</param>
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns> /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
public (PhysicalMemory, ulong) GetGraphicsUniformBufferAddress(int stage, int index) public ulong GetGraphicsUniformBufferAddress(int stage, int index)
{ {
ref BufferBounds buffer = ref _gpUniformBuffers[stage].Buffers[index]; return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address;
return (buffer.Physical, buffer.Range.GetSubRange(0).Address);
} }
/// <summary> /// <summary>
@@ -492,10 +478,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
public void CommitComputeBindings() public void CommitComputeBindings()
{ {
BindBuffers(_cpStorageBuffers, isStorage: true); BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
BindBuffers(_cpUniformBuffers, isStorage: false);
CommitBufferTextureBindings(); BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
CommitBufferTextureBindings(bufferCache);
// Force rebind after doing compute work. // Force rebind after doing compute work.
Rebind(); Rebind();
@@ -507,14 +495,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Commit any queued buffer texture bindings. /// Commit any queued buffer texture bindings.
/// </summary> /// </summary>
/// <param name="bufferCache">Buffer cache</param> /// <param name="bufferCache">Buffer cache</param>
private void CommitBufferTextureBindings() private void CommitBufferTextureBindings(BufferCache bufferCache)
{ {
if (_bufferTextures.Count > 0) if (_bufferTextures.Count > 0)
{ {
foreach (BufferTextureBinding binding in _bufferTextures) foreach (BufferTextureBinding binding in _bufferTextures)
{ {
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore); BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore);
binding.Texture.SetStorage(range); binding.Texture.SetStorage(range);
// The texture must be rebound to use the new storage if it was updated. // The texture must be rebound to use the new storage if it was updated.
@@ -538,7 +526,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
foreach (BufferTextureArrayBinding<ITextureArray> binding in _bufferTextureArrays) foreach (BufferTextureArrayBinding<ITextureArray> binding in _bufferTextureArrays)
{ {
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None); BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None);
binding.Texture.SetStorage(range); binding.Texture.SetStorage(range);
textureArray[0] = binding.Texture; textureArray[0] = binding.Texture;
@@ -548,7 +536,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
foreach (BufferTextureArrayBinding<IImageArray> binding in _bufferImageArrays) foreach (BufferTextureArrayBinding<IImageArray> binding in _bufferImageArrays)
{ {
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore); BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
binding.Texture.SetStorage(range); binding.Texture.SetStorage(range);
textureArray[0] = binding.Texture; textureArray[0] = binding.Texture;
@@ -567,6 +555,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="indexed">True if the index buffer is in use</param> /// <param name="indexed">True if the index buffer is in use</param>
public void CommitGraphicsBindings(bool indexed) public void CommitGraphicsBindings(bool indexed)
{ {
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
if (indexed) if (indexed)
{ {
if (_indexBufferDirty || _rebind) if (_indexBufferDirty || _rebind)
@@ -575,14 +565,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (!_indexBuffer.Range.IsUnmapped) if (!_indexBuffer.Range.IsUnmapped)
{ {
BufferRange buffer = _indexBuffer.BufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer); BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer);
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
} }
} }
else if (!_indexBuffer.Range.IsUnmapped) else if (!_indexBuffer.Range.IsUnmapped)
{ {
_indexBuffer.BufferCache.SynchronizeBufferRange(_indexBuffer.Range); bufferCache.SynchronizeBufferRange(_indexBuffer.Range);
} }
} }
else if (_rebind) else if (_rebind)
@@ -607,7 +597,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; continue;
} }
BufferRange buffer = vb.BufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer); BufferRange buffer = bufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer);
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor); vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
} }
@@ -625,7 +615,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; continue;
} }
vb.BufferCache.SynchronizeBufferRange(vb.Range); bufferCache.SynchronizeBufferRange(vb.Range);
} }
} }
@@ -647,7 +637,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; continue;
} }
tfbs[index] = tfb.BufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true); tfbs[index] = bufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true);
} }
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@@ -694,7 +684,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset); _context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
buffers[index] = new BufferAssignment(index, tfb.BufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true)); buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true));
} }
} }
@@ -712,7 +702,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; continue;
} }
tfb.BufferCache.SynchronizeBufferRange(tfb.Range); bufferCache.SynchronizeBufferRange(tfb.Range);
} }
} }
@@ -720,7 +710,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
_gpStorageBuffersDirty = false; _gpStorageBuffersDirty = false;
BindBuffers(_gpStorageBuffers, isStorage: true); BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
} }
else else
{ {
@@ -731,14 +721,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
_gpUniformBuffersDirty = false; _gpUniformBuffersDirty = false;
BindBuffers(_gpUniformBuffers, isStorage: false); BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
} }
else else
{ {
UpdateBuffers(_gpUniformBuffers); UpdateBuffers(_gpUniformBuffers);
} }
CommitBufferTextureBindings(); CommitBufferTextureBindings(bufferCache);
_rebind = false; _rebind = false;
@@ -752,7 +742,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="bindings">Buffer memory ranges to bind</param> /// <param name="bindings">Buffer memory ranges to bind</param>
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param> /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage) private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
{ {
int rangesCount = 0; int rangesCount = 0;
@@ -773,8 +763,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage BufferRange range = isStorage
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite) ? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
: bounds.BufferCache.GetBufferRange(bounds.Range, bufferStage); : bufferCache.GetBufferRange(bounds.Range, bufferStage);
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
} }
@@ -790,10 +780,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Bind respective buffer bindings on the host API. /// Bind respective buffer bindings on the host API.
/// </summary> /// </summary>
/// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
/// <param name="buffers">Buffer memory ranges to bind</param> /// <param name="buffers">Buffer memory ranges to bind</param>
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param> /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BindBuffers(BuffersPerStage buffers, bool isStorage) private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
{ {
int rangesCount = 0; int rangesCount = 0;
@@ -809,8 +800,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage BufferRange range = isStorage
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite) ? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
: bounds.BufferCache.GetBufferRange(bounds.Range, BufferStage.Compute); : bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
} }
@@ -863,7 +854,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; continue;
} }
bounds.BufferCache.SynchronizeBufferRange(bounds.Range); _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Range);
} }
} }
} }
@@ -880,14 +871,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void SetBufferTextureStorage( public void SetBufferTextureStorage(
ShaderStage stage, ShaderStage stage,
ITexture texture, ITexture texture,
BufferCache bufferCache,
MultiRange range, MultiRange range,
TextureBindingInfo bindingInfo, TextureBindingInfo bindingInfo,
bool isImage) bool isImage)
{ {
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferTextures.Add(new BufferTextureBinding(stage, texture, bufferCache, range, bindingInfo, isImage)); _bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage));
} }
/// <summary> /// <summary>
@@ -904,14 +894,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
ShaderStage stage, ShaderStage stage,
ITextureArray array, ITextureArray array,
ITexture texture, ITexture texture,
BufferCache bufferCache,
MultiRange range, MultiRange range,
TextureBindingInfo bindingInfo, TextureBindingInfo bindingInfo,
int index) int index)
{ {
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, bufferCache, range, bindingInfo, index)); _bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index));
} }
/// <summary> /// <summary>
@@ -928,14 +917,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
ShaderStage stage, ShaderStage stage,
IImageArray array, IImageArray array,
ITexture texture, ITexture texture,
BufferCache bufferCache,
MultiRange range, MultiRange range,
TextureBindingInfo bindingInfo, TextureBindingInfo bindingInfo,
int index) int index)
{ {
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, bufferCache, range, bindingInfo, index)); _bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index));
} }
/// <summary> /// <summary>
@@ -19,11 +19,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
public ITexture Texture { get; } public ITexture Texture { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache { get; }
/// <summary> /// <summary>
/// Physical ranges of memory where the buffer texture data is located. /// Physical ranges of memory where the buffer texture data is located.
/// </summary> /// </summary>
@@ -44,21 +39,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
/// <param name="array">Array</param> /// <param name="array">Array</param>
/// <param name="texture">Buffer texture</param> /// <param name="texture">Buffer texture</param>
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param> /// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info</param> /// <param name="bindingInfo">Binding info</param>
/// <param name="index">Index of the binding on the array</param> /// <param name="index">Index of the binding on the array</param>
public BufferTextureArrayBinding( public BufferTextureArrayBinding(
T array, T array,
ITexture texture, ITexture texture,
BufferCache bufferCache,
MultiRange range, MultiRange range,
TextureBindingInfo bindingInfo, TextureBindingInfo bindingInfo,
int index) int index)
{ {
Array = array; Array = array;
Texture = texture; Texture = texture;
BufferCache = bufferCache;
Range = range; Range = range;
BindingInfo = bindingInfo; BindingInfo = bindingInfo;
Index = index; Index = index;
@@ -20,11 +20,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
public ITexture Texture { get; } public ITexture Texture { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache { get; }
/// <summary> /// <summary>
/// Physical ranges of memory where the buffer texture data is located. /// Physical ranges of memory where the buffer texture data is located.
/// </summary> /// </summary>
@@ -45,21 +40,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
/// <param name="stage">Shader stage accessing the texture</param> /// <param name="stage">Shader stage accessing the texture</param>
/// <param name="texture">Buffer texture</param> /// <param name="texture">Buffer texture</param>
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param> /// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info</param> /// <param name="bindingInfo">Binding info</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param> /// <param name="isImage">Whether the binding is for an image or a sampler</param>
public BufferTextureBinding( public BufferTextureBinding(
ShaderStage stage, ShaderStage stage,
ITexture texture, ITexture texture,
BufferCache bufferCache,
MultiRange range, MultiRange range,
TextureBindingInfo bindingInfo, TextureBindingInfo bindingInfo,
bool isImage) bool isImage)
{ {
Stage = stage; Stage = stage;
Texture = texture; Texture = texture;
BufferCache = bufferCache;
Range = range; Range = range;
BindingInfo = bindingInfo; BindingInfo = bindingInfo;
IsImage = isImage; IsImage = isImage;
@@ -8,7 +8,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
struct IndexBuffer struct IndexBuffer
{ {
public BufferCache BufferCache;
public MultiRange Range; public MultiRange Range;
public IndexType Type; public IndexType Type;
} }
+40 -281
View File
@@ -36,9 +36,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
public event EventHandler<UnmapEventArgs> MemoryUnmapped; public event EventHandler<UnmapEventArgs> MemoryUnmapped;
private readonly GpuContext _context; /// <summary>
private readonly List<PhysicalMemory> _physicalMemoryList; /// Physical memory where the virtual memory is mapped into.
private readonly Dictionary<PhysicalMemory, byte> _physicalMemoryMap; /// </summary>
internal PhysicalMemory Physical { get; }
/// <summary> /// <summary>
/// Virtual range cache. /// Virtual range cache.
@@ -53,65 +54,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Creates a new instance of the GPU memory manager. /// Creates a new instance of the GPU memory manager.
/// </summary> /// </summary>
/// <param name="context">GPU context</param>
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param> /// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param> /// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize)
internal MemoryManager(GpuContext context, PhysicalMemory physicalMemory, ulong cpuMemorySize)
{ {
_context = context; Physical = physicalMemory;
_physicalMemoryList = new List<PhysicalMemory>()
{
physicalMemory
};
_physicalMemoryMap = new Dictionary<PhysicalMemory, byte>
{
{ physicalMemory, 0 }
};
VirtualRangeCache = new VirtualRangeCache(this); VirtualRangeCache = new VirtualRangeCache(this);
CounterCache = new CounterCache(); CounterCache = new CounterCache();
_pageTable = new ulong[PtLvl0Size][]; _pageTable = new ulong[PtLvl0Size][];
MemoryUnmapped += physicalMemory.TextureCache.MemoryUnmappedHandler; MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += physicalMemory.BufferCache.MemoryUnmappedHandler; MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler; MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.MemoryUnmappedHandler; MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
physicalMemory.TextureCache.Initialize(cpuMemorySize); Physical.TextureCache.Initialize(cpuMemorySize);
}
/// <summary>
/// Attaches the memory manager to a new GPU channel.
/// </summary>
/// <param name="rebind">Action to be performed when the buffer cache changes</param>
internal void AttachToChannel(Action rebind)
{
PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
physicalMemory.IncrementReferenceCount();
physicalMemory.BufferCache.NotifyBuffersModified += rebind;
physicalMemory.BufferCache.QueuePrune();
}
/// <summary>
/// Attaches the memory manager to a new GPU channel.
/// </summary>
/// <param name="rebind">Action that was performed when the buffer cache changed</param>
internal void DetachFromChannel(Action rebind)
{
PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
physicalMemory.BufferCache.NotifyBuffersModified -= rebind;
physicalMemory.DecrementReferenceCount();
}
/// <summary>
/// Queues a prune of invalid entries on the buffer cache.
/// </summary>
internal void QueuePrune()
{
GetOwnPhysicalMemory().BufferCache.QueuePrune();
} }
/// <summary> /// <summary>
@@ -127,15 +82,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (IsContiguous(va, size)) if (IsContiguous(va, size))
{ {
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); ulong address = Translate(va);
if (tracked) if (tracked)
{ {
return physicalMemory.ReadTracked<T>(address); return Physical.ReadTracked<T>(address);
} }
else else
{ {
return physicalMemory.Read<T>(address); return Physical.Read<T>(address);
} }
} }
else else
@@ -159,9 +114,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (IsContiguous(va, size)) if (IsContiguous(va, size))
{ {
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); return Physical.GetSpan(Translate(va), size, tracked);
return physicalMemory.GetSpan(address, size, tracked);
} }
else else
{ {
@@ -186,7 +139,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
bool isContiguous = true; bool isContiguous = true;
int mappedSize; int mappedSize;
if (ValidateAddress(va) && IsMappedOnGpuAndPhysical(va)) if (ValidateAddress(va) && GetPte(va) != PteUnmapped && Physical.IsMapped(Translate(va)))
{ {
ulong endVa = va + (ulong)size; ulong endVa = va + (ulong)size;
ulong endVaAligned = (endVa + PageMask) & ~PageMask; ulong endVaAligned = (endVa + PageMask) & ~PageMask;
@@ -199,7 +152,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong nextVa = currentVa + PageSize; ulong nextVa = currentVa + PageSize;
ulong nextPa = Translate(nextVa); ulong nextPa = Translate(nextVa);
if (!ValidateAddress(nextVa) || !IsMappedOnGpuAndPhysical(nextVa)) if (!ValidateAddress(nextVa) || GetPte(nextVa) == PteUnmapped || !Physical.IsMapped(nextPa))
{ {
break; break;
} }
@@ -228,9 +181,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (isContiguous) if (isContiguous)
{ {
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); return Physical.GetSpan(Translate(va), mappedSize, tracked);
return physicalMemory.GetSpan(address, mappedSize, tracked);
} }
else else
{ {
@@ -242,23 +193,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Checks if a page of memory is mapped on the GPU and its backing memory.
/// </summary>
/// <param name="va">GPU virtual address of the page</param>
/// <returns>True if mapped, false otherwise</returns>
private bool IsMappedOnGpuAndPhysical(ulong va)
{
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
if (address == PteUnmapped)
{
return false;
}
return physicalMemory.IsMapped(address);
}
/// <summary> /// <summary>
/// Reads data from a possibly non-contiguous region of GPU mapped memory. /// Reads data from a possibly non-contiguous region of GPU mapped memory.
/// </summary> /// </summary>
@@ -276,22 +210,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0) if ((va & PageMask) != 0)
{ {
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va); ulong pa = Translate(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
physicalMemory.GetSpan(pa, size, tracked).CopyTo(data[..size]); Physical.GetSpan(pa, size, tracked).CopyTo(data[..size]);
offset += size; offset += size;
} }
for (; offset < data.Length; offset += size) for (; offset < data.Length; offset += size)
{ {
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset); ulong pa = Translate(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize); size = Math.Min(data.Length - offset, (int)PageSize);
physicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size)); Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
} }
} }
@@ -306,17 +240,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (IsContiguous(va, size)) if (IsContiguous(va, size))
{ {
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); return Physical.GetWritableRegion(Translate(va), size, tracked);
return physicalMemory.GetWritableRegion(address, size, tracked);
} }
else else
{ {
Memory<byte> memory = new byte[size]; MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
GetSpan(va, size).CopyTo(memory.Span); ReadImpl(va, memoryOwner.Span, tracked);
return new WritableRegion(this, va, memory, tracked); return new WritableRegion(this, va, memoryOwner, tracked);
} }
} }
@@ -338,7 +270,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param> /// <param name="data">The data to be written</param>
public void Write(ulong va, ReadOnlySpan<byte> data) public void Write(ulong va, ReadOnlySpan<byte> data)
{ {
WriteImpl(va, data, (physical, va, data) => physical.Write(va, data)); WriteImpl(va, data, Physical.Write);
} }
/// <summary> /// <summary>
@@ -348,7 +280,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param> /// <param name="data">The data to be written</param>
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data) public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
{ {
WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data)); WriteImpl(va, data, Physical.WriteTrackedResource);
} }
/// <summary> /// <summary>
@@ -358,10 +290,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param> /// <param name="data">The data to be written</param>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data) public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
{ {
WriteImpl(va, data, (physical, va, data) => physical.WriteUntracked(va, data)); WriteImpl(va, data, Physical.WriteUntracked);
} }
private delegate void WriteCallback(PhysicalMemory physicalMemory, ulong address, ReadOnlySpan<byte> data); private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
/// <summary> /// <summary>
/// Writes data to possibly non-contiguous GPU mapped memory. /// Writes data to possibly non-contiguous GPU mapped memory.
@@ -373,9 +305,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (IsContiguous(va, data.Length)) if (IsContiguous(va, data.Length))
{ {
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); writeCallback(Translate(va), data);
writeCallback(physicalMemory, address, data);
} }
else else
{ {
@@ -383,67 +313,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0) if ((va & PageMask) != 0)
{ {
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va); ulong pa = Translate(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
writeCallback(physicalMemory, pa, data[..size]); writeCallback(pa, data[..size]);
offset += size; offset += size;
} }
for (; offset < data.Length; offset += size) for (; offset < data.Length; offset += size)
{ {
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset); ulong pa = Translate(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize); size = Math.Min(data.Length - offset, (int)PageSize);
writeCallback(physicalMemory, pa, data.Slice(offset, size)); writeCallback(pa, data.Slice(offset, size));
}
}
}
/// <summary>
/// Writes data to GPU mapped memory, stopping at the first unmapped page at the memory region, if any.
/// </summary>
/// <param name="va">GPU virtual address to write the data into</param>
/// <param name="data">The data to be written</param>
public void WriteMapped(ulong va, ReadOnlySpan<byte> data)
{
if (IsContiguous(va, data.Length))
{
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
physicalMemory.Write(address, data);
}
else
{
int offset = 0, size;
if ((va & PageMask) != 0)
{
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
if (pa != PteUnmapped && physicalMemory.IsMapped(pa))
{
physicalMemory.Write(pa, data[..size]);
}
offset += size;
}
for (; offset < data.Length; offset += size)
{
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
if (pa != PteUnmapped && physicalMemory.IsMapped(pa))
{
physicalMemory.Write(pa, data.Slice(offset, size));
}
} }
} }
} }
@@ -475,51 +360,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the mapping</param> /// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param> /// <param name="kind">Kind of the resource located at the mapping</param>
public void Map(ulong pa, ulong va, ulong size, PteKind kind) public void Map(ulong pa, ulong va, ulong size, PteKind kind)
{
MapImpl(pa, va, size, kind);
}
/// <summary>
/// Maps a given range of pages to the specified CPU virtual address from a different process.
/// </summary>
/// <remarks>
/// All addresses and sizes must be page aligned.
/// </remarks>
/// <param name="pa">CPU virtual address to map into</param>
/// <param name="va">GPU virtual address to be mapped</param>
/// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
/// <param name="ownedPid">PID of the process that owns the mapping</param>
public void MapForeign(ulong pa, ulong va, ulong size, PteKind kind, ulong ownedPid)
{
if (_context.PhysicalMemoryRegistry.TryGetValue(ownedPid, out PhysicalMemory physicalMemory))
{
MapImpl(pa, va, size, kind, physicalMemory);
}
}
/// <summary>
/// Maps a given range of pages to the specified CPU virtual address.
/// </summary>
/// <remarks>
/// All addresses and sizes must be page aligned.
/// </remarks>
/// <param name="pa">CPU virtual address to map into</param>
/// <param name="va">GPU virtual address to be mapped</param>
/// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
/// <param name="physicalMemory">Optional physical memory to import for the mapping</param>
private void MapImpl(ulong pa, ulong va, ulong size, PteKind kind, PhysicalMemory physicalMemory = null)
{ {
lock (_pageTable) lock (_pageTable)
{ {
UnmapEventArgs e = new(va, size); UnmapEventArgs e = new(va, size);
MemoryUnmapped?.Invoke(this, e); MemoryUnmapped?.Invoke(this, e);
byte pIndex = physicalMemory != null ? GetOrAddPhysicalMemory(physicalMemory) : (byte)0;
for (ulong offset = 0; offset < size; offset += PageSize) for (ulong offset = 0; offset < size; offset += PageSize)
{ {
SetPte(va + offset, PackPte(pa + offset, pIndex, kind)); SetPte(va + offset, PackPte(pa + offset, kind));
} }
RunRemapActions(e); RunRemapActions(e);
@@ -570,14 +419,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int page = 0; page < pages - 1; page++) for (int page = 0; page < pages - 1; page++)
{ {
ulong nextPte = GetPte(va + PageSize); if (!ValidateAddress(va + PageSize) || GetPte(va + PageSize) == PteUnmapped)
if (!ValidateAddress(va + PageSize) || nextPte == PteUnmapped)
{ {
return false; return false;
} }
if (GetPte(va) + PageSize != nextPte) if (Translate(va) + PageSize != Translate(va + PageSize))
{ {
return false; return false;
} }
@@ -611,7 +458,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
int pages = (int)((endVaRounded - va) / PageSize); int pages = (int)((endVaRounded - va) / PageSize);
List<MemoryRange> regions = new(); List<MemoryRange> regions = [];
for (int page = 0; page < pages - 1; page++) for (int page = 0; page < pages - 1; page++)
{ {
@@ -689,49 +536,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return true; return true;
} }
/// <summary>
/// Gets the backing memory for a given GPU virtual address.
/// </summary>
/// <param name="va">GPU virtual address to get the backing memory from</param>
/// <returns>The backing memory for the specified GPU virtual address</returns>
internal PhysicalMemory GetBackingMemory(ulong va)
{
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return GetOwnPhysicalMemory();
}
return _physicalMemoryList[UnpackPIndexFromPte(pte)];
}
/// <summary>
/// Gets the backing memory that is owned by this GPU memory manager.
/// </summary>
/// <returns>The backing memory owned by this memory manager</returns>
private PhysicalMemory GetOwnPhysicalMemory()
{
return _physicalMemoryList[0];
}
/// <summary>
/// Gets the index for a given physical memory on the list, adding it to the list if needed.
/// </summary>
/// <param name="physicalMemory">Physical memory to get the index from</param>
/// <returns>The index of the physical memory on the list</returns>
private byte GetOrAddPhysicalMemory(PhysicalMemory physicalMemory)
{
if (!_physicalMemoryMap.TryGetValue(physicalMemory, out byte pIndex))
{
pIndex = checked((byte)_physicalMemoryList.Count);
_physicalMemoryList.Add(physicalMemory);
_physicalMemoryMap.Add(physicalMemory, pIndex);
}
return pIndex;
}
/// <summary> /// <summary>
/// Validates a GPU virtual address. /// Validates a GPU virtual address.
/// </summary> /// </summary>
@@ -833,28 +637,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return Math.Min(maxSize, va - startVa); return Math.Min(maxSize, va - startVa);
} }
/// <summary>
/// Translates a GPU virtual address to a CPU virtual address and the associated physical memory.
/// </summary>
/// <param name="va">GPU virtual address to be translated</param>
/// <returns>CPU virtual address with the physical memory, or <see cref="PteUnmapped"/> if unmapped</returns>
private (PhysicalMemory, ulong) TranslateWithPhysicalMemory(ulong va)
{
if (!ValidateAddress(va))
{
return (GetOwnPhysicalMemory(), PteUnmapped);
}
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return (GetOwnPhysicalMemory(), PteUnmapped);
}
return (_physicalMemoryList[UnpackPIndexFromPte(pte)], UnpackPaFromPte(pte) + (va & PageMask));
}
/// <summary> /// <summary>
/// Gets the kind of a given memory page. /// Gets the kind of a given memory page.
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling. /// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
@@ -878,18 +660,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return UnpackKindFromPte(pte); return UnpackKindFromPte(pte);
} }
public bool IsForeignMapping(ulong va)
{
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return false;
}
return UnpackPIndexFromPte(pte) != 0;
}
/// <summary> /// <summary>
/// Gets the Page Table entry for a given GPU virtual address. /// Gets the Page Table entry for a given GPU virtual address.
/// </summary> /// </summary>
@@ -935,12 +705,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a page table entry from a physical address and kind. /// Creates a page table entry from a physical address and kind.
/// </summary> /// </summary>
/// <param name="pa">Physical address</param> /// <param name="pa">Physical address</param>
/// <param name="pIndex">Index of the physical memory on the list</param>
/// <param name="kind">Kind</param> /// <param name="kind">Kind</param>
/// <returns>Page table entry</returns> /// <returns>Page table entry</returns>
private static ulong PackPte(ulong pa, byte pIndex, PteKind kind) private static ulong PackPte(ulong pa, PteKind kind)
{ {
return pa | ((ulong)pIndex << 48) | ((ulong)kind << 56); return pa | ((ulong)kind << 56);
} }
/// <summary> /// <summary>
@@ -953,16 +722,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return (PteKind)(pte >> 56); return (PteKind)(pte >> 56);
} }
/// <summary>
/// Unpacks the physical memory index in the list from a page table entry.
/// </summary>
/// <param name="pte">Page table entry</param>
/// <returns>Physical memory index</returns>
private static byte UnpackPIndexFromPte(ulong pte)
{
return (byte)(pte >> 48);
}
/// <summary> /// <summary>
/// Unpacks physical address from a page table entry. /// Unpacks physical address from a page table entry.
/// </summary> /// </summary>
@@ -970,7 +729,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>Physical address</returns> /// <returns>Physical address</returns>
private static ulong UnpackPaFromPte(ulong pte) private static ulong UnpackPaFromPte(ulong pte)
{ {
return pte & 0xffffffffffffUL; return pte & 0xffffffffffffffUL;
} }
} }
} }
@@ -7,6 +7,7 @@ using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -7,7 +7,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
struct VertexBuffer struct VertexBuffer
{ {
public BufferCache BufferCache;
public MultiRange Range; public MultiRange Range;
public int Stride; public int Stride;
public int Divisor; public int Divisor;
@@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine; using Ryujinx.Graphics.Gpu.Engine;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System;
using System.Linq; using System.Linq;
namespace Ryujinx.Graphics.Gpu.Shader namespace Ryujinx.Graphics.Gpu.Shader
@@ -1,6 +1,5 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System; using System;
@@ -67,11 +66,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <inheritdoc/> /// <inheritdoc/>
public uint ConstantBuffer1Read(int offset) public uint ConstantBuffer1Read(int offset)
{ {
(PhysicalMemory physical, ulong baseAddress) = _compute ulong baseAddress = _compute
? _channel.BufferManager.GetComputeUniformBufferAddress(1) ? _channel.BufferManager.GetComputeUniformBufferAddress(1)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1); : _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1);
return physical.Read<uint>(baseAddress + (ulong)offset); return _channel.MemoryManager.Physical.Read<uint>(baseAddress + (ulong)offset);
} }
/// <inheritdoc/> /// <inheritdoc/>
+12 -11
View File
@@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
@@ -733,15 +734,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
byte[] codeB, byte[] codeB,
bool asCompute) bool asCompute)
{ {
(PhysicalMemory physical, ulong cb1DataAddress) = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1); ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
MemoryManager memoryManager = channel.MemoryManager; MemoryManager memoryManager = channel.MemoryManager;
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray(); codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
byte[] cb1DataA = ReadArray(physical, cb1DataAddress, vertexA.Cb1DataSize); byte[] cb1DataA = ReadArray(memoryManager, cb1DataAddress, vertexA.Cb1DataSize);
byte[] cb1DataB = ReadArray(physical, cb1DataAddress, currentStage.Cb1DataSize); byte[] cb1DataB = ReadArray(memoryManager, cb1DataAddress, currentStage.Cb1DataSize);
ShaderDumpPaths pathsA = default; ShaderDumpPaths pathsA = default;
ShaderDumpPaths pathsB = default; ShaderDumpPaths pathsB = default;
@@ -775,11 +776,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
MemoryManager memoryManager = channel.MemoryManager; MemoryManager memoryManager = channel.MemoryManager;
(PhysicalMemory physical, ulong cb1DataAddress) = context.Stage == ShaderStage.Compute ulong cb1DataAddress = context.Stage == ShaderStage.Compute
? channel.BufferManager.GetComputeUniformBufferAddress(1) ? channel.BufferManager.GetComputeUniformBufferAddress(1)
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1); : channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
byte[] cb1Data = ReadArray(physical, cb1DataAddress, context.Cb1DataSize); byte[] cb1Data = ReadArray(memoryManager, cb1DataAddress, context.Cb1DataSize);
code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray(); code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default; ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
@@ -793,18 +794,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0. /// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0.
/// </summary> /// </summary>
/// <param name="physicalMemory">Physical memory to read the data from, might be null</param> /// <param name="memoryManager">Memory manager with the physical memory to read from</param>
/// <param name="address">Physical address of the region to read</param> /// <param name="address">Physical address of the region to read</param>
/// <param name="size">Size in bytes of the data</param> /// <param name="size">Size in bytes of the data</param>
/// <returns>An array with the data at the specified memory location</returns> /// <returns>An array with the data at the specified memory location</returns>
private static byte[] ReadArray(PhysicalMemory physicalMemory, ulong address, int size) private static byte[] ReadArray(MemoryManager memoryManager, ulong address, int size)
{ {
if (address == MemoryManager.PteUnmapped || size == 0 || physicalMemory == null) if (address == MemoryManager.PteUnmapped || size == 0)
{ {
return []; return [];
} }
return physicalMemory.GetSpan(address, size).ToArray(); return memoryManager.Physical.GetSpan(address, size).ToArray();
} }
/// <summary> /// <summary>
@@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex); ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range)); cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedTextureBufferIndex = textureBufferIndex; cachedTextureBufferIndex = textureBufferIndex;
if (samplerBufferIndex == textureBufferIndex) if (samplerBufferIndex == textureBufferIndex)
@@ -710,7 +710,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex); ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range)); cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedSamplerBufferIndex = samplerBufferIndex; cachedSamplerBufferIndex = samplerBufferIndex;
} }
@@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
} }
using ManualResetEvent waitEvent = new(false); using ManualResetEvent waitEvent = new(false);
SyncpointWaiterHandle info = _syncpoints[id].RegisterCallback(threshold, _ => waitEvent.Set()); SyncpointWaiterHandle info = _syncpoints[id].RegisterCallback(threshold, (x) => waitEvent.Set());
if (info == null) if (info == null)
{ {
@@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
bool signaled = waitEvent.WaitOne(timeout); bool signaled = waitEvent.WaitOne(timeout);
if (!signaled) if (!signaled && info != null)
{ {
Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution..."); Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution...");
-160
View File
@@ -9,18 +9,11 @@ using System.Threading;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Graphics.Gpu
{ {
using Texture = Image.Texture;
public record TextureData(int Width, int Height, byte[] Data);
/// <summary> /// <summary>
/// GPU image presentation window. /// GPU image presentation window.
/// </summary> /// </summary>
public class Window public class Window
{ {
private const int CaptureTextureWidth = 1280;
private const int CaptureTextureHeight = 720;
private readonly GpuContext _context; private readonly GpuContext _context;
/// <summary> /// <summary>
@@ -92,21 +85,7 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
private class PresentedTexture
{
public readonly Texture Texture;
public readonly ImageCrop Crop;
public PresentedTexture(Texture texture, ImageCrop crop)
{
Texture = texture;
Crop = crop;
}
}
private readonly ConcurrentQueue<PresentationTexture> _frameQueue; private readonly ConcurrentQueue<PresentationTexture> _frameQueue;
private PresentedTexture _lastPresentedTexture;
private ITexture _captureTexture;
private int _framesAvailable; private int _framesAvailable;
@@ -209,51 +188,6 @@ namespace Ryujinx.Graphics.Gpu
return true; return true;
} }
public TextureData GetLastPresentedData()
{
PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture);
if (pt != null)
{
byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, pt.Crop);
int size = SizeCalculator.GetBlockLinearTextureSize(
CaptureTextureWidth,
CaptureTextureHeight,
1,
1,
1,
1,
1,
4,
16,
1,
1).TotalSize;
byte[] data = new byte[size];
LayoutConverter.ConvertLinearToBlockLinear(data, CaptureTextureWidth, CaptureTextureHeight, CaptureTextureWidth * 4, 4, 16, inputData);
return new TextureData(CaptureTextureWidth, CaptureTextureHeight, data);
}
return new TextureData(0, 0, Array.Empty<byte>());
}
public TextureData GetLastPresentedDataLinear()
{
PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture);
if (pt != null)
{
byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, new ImageCrop());
return new TextureData(pt.Texture.Info.Width, pt.Texture.Info.Height, inputData);
}
return new TextureData(0, 0, Array.Empty<byte>());
}
/// <summary> /// <summary>
/// Presents a texture on the queue. /// Presents a texture on the queue.
/// If the queue is empty, then no texture is presented. /// If the queue is empty, then no texture is presented.
@@ -271,10 +205,6 @@ namespace Ryujinx.Graphics.Gpu
pt.Cache.Tick(); pt.Cache.Tick();
EnsureCaptureTexture();
Volatile.Write(ref _lastPresentedTexture, new PresentedTexture(texture, pt.Crop));
texture.SynchronizeMemory(); texture.SynchronizeMemory();
ImageCrop crop = new( ImageCrop crop = new(
@@ -314,96 +244,6 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
private void EnsureCaptureTexture()
{
if (_captureTexture == null)
{
_captureTexture = _context.Renderer.CreateTexture(new TextureCreateInfo(
1280,
720,
1,
1,
1,
1,
1,
4,
Format.R8G8B8A8Unorm,
DepthStencilMode.Depth,
Target.Texture2D,
SwizzleComponent.Red,
SwizzleComponent.Green,
SwizzleComponent.Blue,
SwizzleComponent.Alpha));
}
}
private byte[] CaptureLastFrame(ITexture lastFrame, ImageCrop crop)
{
int cropLeft, cropRight, cropTop, cropBottom;
if (crop.Left == 0 && crop.Right == 0)
{
cropLeft = 0;
cropRight = lastFrame.Width;
}
else
{
cropLeft = crop.Left;
cropRight = crop.Right;
}
if (crop.Top == 0 && crop.Bottom == 0)
{
cropTop = 0;
cropBottom = lastFrame.Height;
}
else
{
cropTop = crop.Top;
cropBottom = crop.Bottom;
}
int x1, y1, x2, y2;
if (crop.FlipX)
{
x1 = cropRight;
x2 = cropLeft;
}
else
{
x1 = cropLeft;
x2 = cropRight;
}
if (crop.FlipY)
{
y1 = cropBottom;
y2 = cropTop;
}
else
{
y1 = cropTop;
y2 = cropBottom;
}
Extents2D srcRegion = new(x1, y1, x2, y2);
Extents2D dstRegion = new(0, 0, CaptureTextureWidth, CaptureTextureHeight);
byte[] outputData = null;
_context.Renderer.BackgroundContextAction(() =>
{
lastFrame.CopyTo(_captureTexture, srcRegion, dstRegion, true);
using var data = _captureTexture.GetData();
outputData = data.Get().ToArray();
});
return outputData;
}
/// <summary> /// <summary>
/// Indicate that a frame on the queue is ready to be acquired. /// Indicate that a frame on the queue is ready to be acquired.
/// </summary> /// </summary>
@@ -1,6 +1,7 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using SharpMetal.Metal; using SharpMetal.Metal;
using System;
using System.Runtime.Versioning; using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal namespace Ryujinx.Graphics.Metal
@@ -1,3 +1,5 @@
using System;
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{ {
struct AVCodec struct AVCodec
@@ -1,3 +1,5 @@
using System;
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{ {
struct AVCodec501 struct AVCodec501
@@ -1,4 +1,5 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System;
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{ {

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