Compare commits

..

4 Commits

Author SHA1 Message Date
Jacobwasbeast
965fb9dd5f Implement Surface Flinger shared layers.
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
2025-02-07 04:27:01 -06:00
Jacobwasbeast
e2a5e69f4c Implement IHid fix from alula/qlaunch
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
2025-02-07 04:24:37 -06:00
Jacobwasbeast
7f27b791f8 Refactor Share Buffer Implementation to Follow Code Style Guidelines 2025-02-07 04:16:27 -06:00
Jacobwasbeast
3ca8618f5f Implement changes from gdkchan/buffer-sharing-rebased
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
2025-02-06 03:48:54 -06:00
127 changed files with 2567 additions and 3379 deletions

View File

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

View File

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

View File

@@ -39,12 +39,12 @@
<p align="center">
Click below to join the Discord:
<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">
</a>
<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>
## Usage

View File

@@ -332,7 +332,6 @@
0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21
01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26
01004DA012976000,"Area 86",,playable,2020-12-16 16:45:52
01008d8006a6a000,"Arena of Valor",crash,boots,2025-02-03 22:19:34
0100691013C46000,"ARIA CHRONICLE",,playable,2022-11-16 13:50:55
0100D4A00B284000,"ARK: Survival Evolved",gpu;nvdec;online-broken;UE4;ldn-untested,ingame,2024-04-16 00:53:56
0100C56012C96000,"Arkanoid vs. Space Invaders",services,ingame,2021-01-21 12:50:30
@@ -427,7 +426,6 @@
0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07
0100CD801CE5E000,"Balatro",,ingame,2024-04-21 02:01:53
010010A00DA48000,"Baldur's Gate and Baldur's Gate II: Enhanced Editions",32-bit,playable,2022-09-12 23:52:15
0100fd1014726000,"Baldur's Gate: Dark Alliance",ldn-untested,ingame,2025-02-03 22:21:00
0100BC400FB64000,"Balthazar's Dream",,playable,2022-09-13 00:13:22
01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39
010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37
@@ -530,7 +528,6 @@
01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43
0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26
0100CFA00CC74000,"Blades of Time",deadlock;online,boots,2022-07-17 19:19:58
01003d700dd8a000,"Blades",,boots,2025-02-03 22:22:00
01006CC01182C000,"Blair Witch",nvdec;UE4,playable,2022-10-01 14:06:16
010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13
0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31
@@ -958,7 +955,7 @@
010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28
01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12
010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,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
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
@@ -1249,7 +1246,7 @@
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
0100E1F013674000,"FUSER™",nvdec;UE4;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
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
@@ -1657,7 +1654,7 @@
0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03
0100A7700B46C000,"Legendary Fishing",online,playable,2021-04-14 15:08:46
0100739018020000,"LEGO® 2K Drive",gpu;ldn-works,ingame,2024-04-09 02:05:12
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
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
@@ -1916,7 +1913,6 @@
010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17
0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04
0100211005E94000,"Mulaka",,playable,2021-01-28 18:07:20
01008e2013fb4000,"Multi Quiz",ldn-untested,ingame,2025-02-03 22:26:00
010038B00B9AE000,"Mummy Pinball",,playable,2022-08-05 16:08:11
01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29
010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08
@@ -2032,7 +2028,6 @@
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
0100C9A00ECE6000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
0100e0601c632000,"Nintendo 64™ Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
@@ -2063,7 +2058,7 @@
010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07
010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23
010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22
,"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
010049F00EC30000,"Nyan Cat: Lost in Space",online,playable,2021-06-12 13:22:03
01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14
@@ -2471,7 +2466,7 @@
0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38
0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01
01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18
,"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
01006EC00F2CC000,"RUINER",UE4,playable,2022-10-03 14:11:33
010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28
@@ -2480,7 +2475,6 @@
010081C0191D8000,"Rune Factory 3 Special",,playable,2023-10-15 08:32:49
010051D00E3A4000,"Rune Factory 4 Special",32-bit;crash;nvdec,ingame,2023-05-06 08:49:17
010014D01216E000,"Rune Factory 5 (JP)",gpu,ingame,2021-06-01 12:00:36
010071E0145F8000,"Rustler",,playable,2025-02-10 20:17:12
0100E21013908000,"RWBY: Grimm Eclipse - Definitive Edition",online-broken,playable,2022-11-03 10:44:01
010012C0060F0000,"RXN -Raijin-",nvdec,playable,2021-01-10 16:05:43
0100B8B012ECA000,"S.N.I.P.E.R. - Hunter Scope",,playable,2021-04-19 15:58:09
@@ -2538,7 +2532,7 @@
0100C3E00B700000,"SEGA AGES Space Harrier",,playable,2021-01-11 12:57:40
010054400D2E6000,"SEGA AGES Virtua Racing",online-broken,playable,2023-01-29 17:08:39
01001E700AC60000,"SEGA AGES Wonder Boy: Monster Land",online,playable,2021-05-05 16:28:25
0100B3C014BDA000,"SEGA Genesis™ Nintendo Switch Online",crash;regression,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
01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31
0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10
@@ -2674,10 +2668,10 @@
01004F401BEBE000,"Song of Nunu: A League of Legends Story",,ingame,2024-07-12 18:53:44
0100E5400BF94000,"Songbird Symphony",,playable,2021-02-27 02:44:04
010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02
,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
0000000000000000,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
0000000000000000,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
0000000000000000,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
0000000000000000,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26
01001270012B6000,"SONIC FORCES™",,playable,2024-07-28 13:11:21
01004AD014BF0000,"Sonic Frontiers",gpu;deadlock;amd-vendor-bug;intel-vendor-bug,ingame,2024-09-05 09:18:53
@@ -2694,7 +2688,7 @@
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37
010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10
,"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
010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56
010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39
@@ -2839,7 +2833,7 @@
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
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
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
@@ -2970,7 +2964,6 @@
0100C38004DCC000,"The Flame In The Flood: Complete Edition",gpu;nvdec;UE4,ingame,2022-08-22 16:23:49
010007700D4AC000,"The Forbidden Arts",,playable,2021-01-26 16:26:24
010030700CBBC000,"The friends of Ringo Ishikawa",,playable,2022-08-22 16:33:17
0100b620139d8000,"The Game of Life 2",ldn-untested,ingame,2025-02-03 22:30:00
01006350148DA000,"The Gardener and the Wild Vines",gpu,ingame,2024-04-29 16:32:10
0100B13007A6A000,"The Gardens Between",,playable,2021-01-29 16:16:53
010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29
@@ -2988,8 +2981,6 @@
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
01006fe0096ac000,"The Jackbox Party Pack 5",slow;online-working,ingame,2025-02-14 05:32:00
01005a400db52000,"The Jackbox Party Pack 6",slow;online-working,ingame,2025-02-14 05:26:00
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
@@ -3168,7 +3159,6 @@
010055E00CA68000,"Trine 4: The Nightmare Prince",gpu,nothing,2025-01-07 05:47:46
0100D9000A930000,"Trine Enchanted Edition",ldn-untested;nvdec,playable,2021-06-03 11:28:15
01002D7010A54000,"Trinity Trigger",crash,ingame,2023-03-03 03:09:09
010020700a5e0000,"TRIVIAL PURSUIT Live!",ldn-untested,ingame,2025-02-03 22:35:00
0100868013FFC000,"TRIVIAL PURSUIT Live! 2",,boots,2022-12-19 00:04:33
0100F78002040000,"Troll and I™",gpu;nvdec,ingame,2021-06-04 16:58:50
0100145011008000,"Trollhunters: Defenders of Arcadia",gpu;nvdec,ingame,2020-11-30 13:27:09
@@ -3218,7 +3208,6 @@
0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27
0100A3E011CB0000,"Unlock the King 2",,playable,2021-06-15 20:43:55
01005AA00372A000,"UNO® for Nintendo Switch",nvdec;ldn-untested,playable,2022-07-28 14:49:47
0100b6e012ebe000,"UNO",ldn-untested,ingame,2025-02-03 22:40:00
0100E5D00CC0C000,"Unravel Two",nvdec,playable,2024-05-23 15:45:05
010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31
0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49
@@ -3383,7 +3372,6 @@
0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51
010012F00B6F2000,"Yomawari: The Long Night Collection",,playable,2022-09-03 14:36:59
0100CC600ABB2000,"Yonder: The Cloud Catcher Chronicles (Retail Only)",,playable,2021-01-28 14:06:25
0100534009ff2000,"Yonder: The Cloud Catcher Chronicles",,playable,2025-02-03 22:19:13
0100BE50042F6000,"Yono and the Celestial Elephants",,playable,2021-01-28 18:23:58
0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45
010022F00DA66000,"Yooka-Laylee and the Impossible Lair",,playable,2021-03-05 17:32:21
1 title_id game_name labels status last_updated
332 0100E680149DC000 Arcaea playable 2023-03-16 19:31:21
333 01003C2010C78000 Archaica: The Path Of Light crash nothing 2020-10-16 13:22:26
334 01004DA012976000 Area 86 playable 2020-12-16 16:45:52
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
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
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
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
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

View File

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

View File

@@ -2,8 +2,6 @@ using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Memory;
using ARMeilleure.Native;
using Humanizer;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Generic;
@@ -20,8 +18,9 @@ namespace ARMeilleure.Translation.Cache
private static readonly int _pageMask = _pageSize - 1;
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 CacheMemoryAllocator _cacheAllocator;
@@ -31,9 +30,6 @@ namespace ARMeilleure.Translation.Cache
private static readonly Lock _lock = new();
private static bool _initialized;
private static readonly List<ReservedRegion> _jitRegions = new();
private static int _activeRegionIndex = 0;
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
@@ -52,9 +48,7 @@ namespace ARMeilleure.Translation.Cache
return;
}
ReservedRegion firstRegion = new(allocator, CacheSize);
_jitRegions.Add(firstRegion);
_activeRegionIndex = 0;
_jitRegion = new ReservedRegion(allocator, CacheSize);
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
{
@@ -65,9 +59,7 @@ namespace ARMeilleure.Translation.Cache
if (OperatingSystem.IsWindows())
{
JitUnwindWindows.InstallFunctionTableHandler(
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
);
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize));
}
_initialized = true;
@@ -83,8 +75,8 @@ namespace ARMeilleure.Translation.Cache
Debug.Assert(_initialized);
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)
{
@@ -98,9 +90,9 @@ namespace ARMeilleure.Translation.Cache
}
else
{
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
ReprotectAsWritable(funcOffset, 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)
{
@@ -124,83 +116,52 @@ namespace ARMeilleure.Translation.Cache
{
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() ||
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
{
continue;
}
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
{
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
}
return;
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
}
}
}
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
private static void ReprotectAsWritable(int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_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 regionStart = offset & ~_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)
{
codeSize = AlignCodeSize(codeSize);
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset < 0)
{
int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset >= 0)
{
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
_activeRegionIndex = i;
return allocOffset;
}
throw new OutOfMemoryException("JIT Cache exhausted.");
}
int exhaustedRegion = _activeRegionIndex;
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
_jitRegions.Add(newRegion);
_activeRegionIndex = _jitRegions.Count - 1;
int newRegionNumber = _activeRegionIndex;
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
if (allocOffsetNew < 0)
{
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
}
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
return allocOffsetNew;
return allocOffset;
}
private static int AlignCodeSize(int codeSize)
{
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
@@ -224,21 +185,18 @@ namespace ARMeilleure.Translation.Cache
{
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)
{
index = ~index - 1;
}
if (index >= 0)
{
entry = _cacheEntries[index];
entryIndex = index;
return true;
}
if (index >= 0)
{
entry = _cacheEntries[index];
entryIndex = index;
return true;
}
}

View File

@@ -144,15 +144,17 @@ namespace ARMeilleure.Translation.PTC
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)
continue;
if (!funcs.Contains(ptr))
funcs.Add(ptr);
if (profiledFunc.Value.Blacklist)
{
if (!funcs.Contains(profiledFunc.Key))
{
funcs.Add(profiledFunc.Key);
}
}
}
return funcs;

View File

@@ -164,16 +164,15 @@ namespace Ryujinx.Common
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
//NSO Membership games
"0100ccf019c8c000", // F-ZERO 99
"0100c62011050000", // GB - Nintendo Switch Online
"010012f017576000", // GBA - Nintendo Switch Online
"0100c9a00ece6000", // N64 - Nintendo Switch Online
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
"0100d870045b6000", // NES - Nintendo Switch Online
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100ccf019c8c000", // F-ZERO 99
"0100ad9012510000", // PAC-MAN 99
"010040600c5ce000", // Tetris 99
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100277011f1a000", // Super Mario Bros. 35
//Misc Nintendo 1st party games
@@ -219,7 +218,6 @@ namespace Ryujinx.Common
//Misc Games
"010056e00853a000", // A Hat in Time
"0100fd1014726000", // Baldurs Gate: Dark Alliance
"01008c2019598000", // Bluey: The Video Game
"0100c6800b934000", // Brawlhalla
"0100dbf01000a000", // Burnout Paradise Remastered
"0100744001588000", // Cars 3: Driven to Win

View File

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

View File

@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{
private const int CodeAlignment = 4; // Bytes.
private const int SharedCacheSize = 2047 * 1024 * 1024;
private const int LocalCacheSize = 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
// and allow the guest to take the fast path.

View File

@@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
// Make sure all pending uniform buffer data is written to memory.
_3dEngine.FlushUboDirty();
uint qmdAddress = _state.State.SendPcasA;
ComputeQmd qmd = _channel.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
@@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
ulong shaderGpuVa = ((ulong)_state.State.SetProgramRegionAAddressUpper << 32) | _state.State.SetProgramRegionB;
shaderGpuVa += (uint)qmd.ProgramOffset;
ShaderCache shaderCache = memoryManager.GetBackingMemory(shaderGpuVa).ShaderCache;
int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize;
@@ -142,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
sharedMemorySize,
_channel.BufferManager.HasUnalignedStorageBuffers);
CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
CachedShaderProgram cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
@@ -156,10 +158,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
{
BufferDescriptor sb = info.SBuffers[index];
ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
(PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress);
uint size;
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
@@ -187,7 +189,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
sharedMemorySize,
_channel.BufferManager.HasUnalignedStorageBuffers);
cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
}

View File

@@ -215,7 +215,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
_channel.TextureManager.RefreshModifiedTextures();
_3dEngine.CreatePendingSyncs();
_3dEngine.FlushUboDirty();
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcGpuVa);
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstGpuVa);
if (copy2D)
{
// Buffer to texture copy.
@@ -293,7 +296,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
if (completeSource && completeDest && !srcLinear && isIdentityRemap)
{
Image.Texture source = memoryManager.Physical.TextureCache.FindTexture(
Image.Texture source = srcPhysical.TextureCache.FindTexture(
memoryManager,
srcGpuVa,
srcBpp,
@@ -309,7 +312,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
{
source.SynchronizeMemory();
Image.Texture target = memoryManager.Physical.TextureCache.FindOrCreateTexture(
Image.Texture target = dstPhysical.TextureCache.FindOrCreateTexture(
memoryManager,
source.Info.FormatInfo,
dstGpuVa,
@@ -339,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap)
{
Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
Image.Texture target = dstPhysical.TextureCache.FindTexture(
memoryManager,
dstGpuVa,
dstBpp,
@@ -462,6 +465,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
}
else
{
BufferCache bufferCache = dstPhysical.BufferCache;
if (remap &&
_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA &&
_state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA &&
@@ -472,7 +476,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
_state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four)
{
// Fast path for clears when remap is enabled.
memoryManager.Physical.BufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
bufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
}
else
{
@@ -492,7 +496,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
}
else
{
memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
BufferCache.CopyBuffer(_context,memoryManager, srcGpuVa, dstGpuVa, size);
}
}
}

View File

@@ -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.
if (!_isLinear)
{
Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
Image.Texture target = memoryManager.GetBackingMemory(_dstGpuVa).TextureCache.FindTexture(
memoryManager,
_dstGpuVa,
1,

View File

@@ -384,7 +384,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
ulong indirectBufferGpuVa = count.GpuVa;
BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
BufferCache bufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
@@ -394,6 +394,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
_processor.ThreedClass.DrawIndirect(
topology,
bufferCache,
null,
new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
default,
1,
@@ -491,22 +493,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
}
BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
BufferCache indirectBufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
BufferCache parameterBufferCache = _processor.MemoryManager.GetBackingMemory(parameterBufferGpuVa).BufferCache;
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
MultiRange indirectBufferRange = indirectBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
MultiRange parameterBufferRange = parameterBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
_processor.ThreedClass.DrawIndirect(
topology,
indirectBufferCache,
parameterBufferCache,
indirectBufferRange,
parameterBufferRange,
maxDrawCount,
stride,
indexCount,
Threed.IndirectDrawType.DrawIndexedIndirectCount);
IndirectDrawType.DrawIndexedIndirectCount);
}
/// <summary>

View File

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

View File

@@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
continue;
}
@@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
continue;
}
int vbStride = vertexBuffer.UnpackStride();
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
ulong oldVbSize = vbSize;
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
int componentSize = format.GetScalarSize();
@@ -196,11 +200,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
_context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) });
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true);
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) });
_vacContext.VertexInfoBufferUpdater.Commit();
@@ -228,7 +232,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
_context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) });
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
@@ -246,11 +250,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
BufferRange vertexBuffer = _vacContext.GetGeometryVertexDataBufferRange(_geometryVertexDataOffset, _geometryVertexDataSize, write: true);
BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true);
_context.Renderer.Pipeline.SetStorageBuffers([
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[]
{
new BufferAssignment(vertexDataBinding, vertexDataRange),
new BufferAssignment(geometryVbBinding, vertexBuffer),
new BufferAssignment(geometryIbBinding, indexBuffer)
]);
new BufferAssignment(geometryIbBinding, indexBuffer),
});
_context.Renderer.Pipeline.DispatchCompute(
BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize),
@@ -294,7 +299,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
_context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt);
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexBuffer)]);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexBuffer) });
_context.Renderer.Pipeline.SetPrimitiveRestart(true, -1);
_context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive));
@@ -309,7 +314,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false);
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) });
_context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0);
}
}
@@ -340,6 +345,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
return maxOutputVertices / verticesPerPrimitive;
}
/// <summary>
/// Binds a dummy buffer as vertex buffer into a buffer texture.
/// </summary>
/// <param name="reservations">Shader resource binding reservations</param>
/// <param name="index">Buffer texture index</param>
/// <param name="format">Buffer texture format</param>
private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format)
{
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
bufferTexture.SetStorage(_vacContext.GetDummyBufferRange());
_context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null);
}
/// <summary>
/// Binds a vertex buffer into a buffer texture.
/// </summary>
@@ -352,7 +371,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{
MemoryManager memoryManager = _channel.MemoryManager;
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer);
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer);
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
bufferTexture.SetStorage(range);
@@ -394,7 +413,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
MemoryManager memoryManager = _channel.MemoryManager;
ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1);
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(
memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign),
BufferStage.IndexBuffer);
misalignedOffset = (int)misalign >> shift;

View File

@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// State associated with direct uniform buffer updates.
// This state is used to attempt to batch together consecutive updates.
private ulong _ubBeginGpuAddress = 0;
private ulong _ubBeginCpuAddress = 0;
private ulong _ubFollowUpAddress = 0;
private ulong _ubByteCount = 0;
@@ -113,12 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (_ubFollowUpAddress != 0)
{
MemoryManager memoryManager = _channel.MemoryManager;
PhysicalMemory physicalMemory = memoryManager.GetBackingMemory(_ubBeginGpuAddress);
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
if (physicalMemory.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
{
memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
physicalMemory.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
}
_ubFollowUpAddress = 0;

View File

@@ -641,6 +641,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public void DrawIndirect(
ThreedClass engine,
PrimitiveTopology topology,
BufferCache indirectBufferCache,
BufferCache parameterBufferCache,
MultiRange indirectBufferRange,
MultiRange parameterBufferRange,
int maxDrawCount,
@@ -662,8 +664,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return;
}
PhysicalMemory memory = _channel.MemoryManager.Physical;
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
@@ -684,8 +684,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (hasCount)
{
BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
BufferRange parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
BufferRange parameterBuffer = parameterBufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
if (indexed)
{
@@ -698,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
else
{
BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
if (indexed)
{

View File

@@ -381,10 +381,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
BufferDescriptor sb = info.SBuffers[index];
ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
(PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress);
uint size;
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
@@ -505,7 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
rtNoAlphaMask |= 1u << index;
}
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
TextureCache colorTextureCache = memoryManager.GetBackingMemory(colorState.Address.Pack()).TextureCache;
Image.Texture color = colorTextureCache.FindOrCreateTexture(
memoryManager,
colorState,
_vtgWritesRtLayer || layered,
@@ -513,7 +515,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
samplesInX,
samplesInY,
sizeHint);
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
if (color != null)
@@ -543,8 +545,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
RtDepthStencilState dsState = _state.State.RtDepthStencilState;
Size3D dsSize = _state.State.RtDepthStencilSize;
depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
TextureCache dsTextureCache = memoryManager.GetBackingMemory(dsState.Address.Pack()).TextureCache;
depthStencil = dsTextureCache.FindOrCreateTexture(
memoryManager,
dsState,
dsSize,
@@ -1409,8 +1412,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
private void UpdateShaderState()
{
ShaderCache shaderCache = _channel.MemoryManager.Physical.ShaderCache;
_vtgWritesRtLayer = false;
ShaderAddresses addresses = new();
@@ -1433,6 +1434,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
? _state.State.TexturePoolState.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(
ref _state.State,
ref _pipeline,

View File

@@ -5,6 +5,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory.Range;
using System;
@@ -804,6 +805,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Performs a indirect draw, with parameters from a GPU buffer.
/// </summary>
/// <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="parameterBufferRange">Memory range of the buffer with the draw count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
@@ -812,6 +815,8 @@ 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>
public void DrawIndirect(
PrimitiveTopology topology,
BufferCache indirectBufferCache,
BufferCache parameterBufferCache,
MultiRange indirectBufferRange,
MultiRange parameterBufferRange,
int maxDrawCount,
@@ -819,7 +824,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int indexCount,
IndirectDrawType drawType)
{
_drawManager.DrawIndirect(this, topology, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType);
_drawManager.DrawIndirect(this, topology, indirectBufferCache, parameterBufferCache, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType);
}
/// <summary>

View File

@@ -233,6 +233,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
TwodTexture dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
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 srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
@@ -305,7 +308,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
// are the same, as we can't blit between different depth formats.
bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format;
Image.Texture srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
Image.Texture srcTexture = srcTextureCache.FindOrCreateTexture(
memoryManager,
srcCopyTexture,
offset,
@@ -326,7 +329,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
return;
}
memoryManager.Physical.TextureCache.Lift(srcTexture);
srcTextureCache.Lift(srcTexture);
// When the source texture that was found has a depth format,
// we must enforce the target texture also has a depth format,
@@ -342,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
}
Image.Texture dstTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
Image.Texture dstTexture = dstTextureCache.FindOrCreateTexture(
memoryManager,
dstCopyTexture,
0,

View File

@@ -58,22 +58,24 @@ namespace Ryujinx.Graphics.Gpu
public void BindMemory(MemoryManager memoryManager)
{
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
if (oldMemoryManager == memoryManager)
{
return;
}
memoryManager.Physical.IncrementReferenceCount();
memoryManager.AttachToChannel(BufferManager.Rebind);
if (oldMemoryManager != null)
{
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
oldMemoryManager.DetachFromChannel(BufferManager.Rebind);
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
}
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
TextureManager.ReloadPools();
memoryManager.Physical.BufferCache.QueuePrune();
memoryManager.QueuePrune();
}
/// <summary>
@@ -86,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu
TextureManager.ReloadPools();
MemoryManager memoryManager = Volatile.Read(ref _memoryManager);
memoryManager?.Physical.BufferCache.QueuePrune();
memoryManager?.QueuePrune();
}
/// <summary>
@@ -141,8 +143,7 @@ namespace Ryujinx.Graphics.Gpu
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
if (oldMemoryManager != null)
{
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
oldMemoryManager.DetachFromChannel(BufferManager.Rebind);
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
}
}

View File

@@ -6,6 +6,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -172,7 +173,7 @@ namespace Ryujinx.Graphics.Gpu
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
}
return new MemoryManager(physicalMemory, cpuMemorySize);
return new MemoryManager(this, physicalMemory, cpuMemorySize);
}
/// <summary>
@@ -197,7 +198,7 @@ namespace Ryujinx.Graphics.Gpu
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
/// <param name="cpuMemory">Virtual memory owned by the process</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
public void RegisterProcess(ulong pid, Cpu.IVirtualMemoryManagerTracked cpuMemory)
public void RegisterProcess(ulong pid, IVirtualMemoryManagerTracked cpuMemory)
{
PhysicalMemory physicalMemory = new(this, cpuMemory);
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))

View File

@@ -1,3 +1,4 @@
using Ryujinx.Graphics.Gpu.Memory;
using System;
using System.Collections.Generic;
@@ -64,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="maximumId">Maximum ID of the texture pool</param>
/// <param name="bindingsArrayCache">Cache of texture array bindings</param>
/// <returns>The found or newly created texture pool</returns>
public T FindOrCreate(GpuChannel channel, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
public T FindOrCreate(GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
{
// Remove old entries from the cache, if possible.
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
@@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
// If not found, create a new one.
pool = CreatePool(_context, channel, address, maximumId);
pool = CreatePool(_context, channel, physicalMemory, address, maximumId);
pool.CacheNode = _pools.AddLast(pool);
pool.CacheTimestamp = _currentTimestamp;
@@ -112,9 +113,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="context">GPU context 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="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId);
protected abstract T CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId);
public void Dispose()
{

View File

@@ -1,3 +1,5 @@
using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image
{
/// <summary>
@@ -20,11 +22,12 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <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="physicalMemory">GPU backing memory of the pool</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>
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId)
{
return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId);
return new SamplerPool(context, physicalMemory, address, maximumId);
}
}
}

View File

@@ -660,6 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
BufferCache bufferCache = null;
for (int index = 0; index < length; index++)
{
@@ -673,7 +674,7 @@ namespace Ryujinx.Graphics.Gpu.Image
else
{
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
if (texture != null)
{
entry.Textures[texture] = texture.InvalidatedSequence;
@@ -702,11 +703,10 @@ namespace Ryujinx.Graphics.Gpu.Image
// to ensure we're not using a old buffer that was already deleted.
if (isImage)
{
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
}
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); }
else
{
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
}
}
else if (isImage)
@@ -797,11 +797,11 @@ namespace Ryujinx.Graphics.Gpu.Image
return;
}
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range));
if (separateSamplerBuffer)
{
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range));
}
else
{
@@ -828,11 +828,10 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range));
if (separateSamplerBuffer)
{
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range));
}
else
{
@@ -901,16 +900,18 @@ namespace Ryujinx.Graphics.Gpu.Image
if (hostTexture != null && texture.Target == Target.TextureBuffer)
{
BufferCache bufferCache = textureBufferBounds.BufferCache;
// 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
// to ensure we're not using a old buffer that was already deleted.
if (isImage)
{
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
}
else
{
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
}
}
else if (isImage)

View File

@@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
cachedTextureBufferIndex = textureBufferIndex;
if (samplerBufferIndex == textureBufferIndex)
@@ -410,7 +410,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
cachedSamplerBufferIndex = samplerBufferIndex;
}
}
@@ -524,7 +524,8 @@ namespace Ryujinx.Graphics.Gpu.Image
// 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
// to ensure we're not using a old buffer that was already deleted.
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, false);
// Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null;
@@ -659,7 +660,8 @@ namespace Ryujinx.Graphics.Gpu.Image
// 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.
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, true);
// Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null;
@@ -715,9 +717,10 @@ namespace Ryujinx.Graphics.Gpu.Image
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
int textureId = TextureHandle.UnpackTextureId(packedId);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolGpuVa);
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache);
TextureDescriptor descriptor;
@@ -751,12 +754,12 @@ namespace Ryujinx.Graphics.Gpu.Image
{
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
ulong textureBufferAddress = _isCompute
(PhysicalMemory texturePhysicalMemory, ulong textureBufferAddress) = _isCompute
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
int handle = textureBufferAddress != MemoryManager.PteUnmapped
? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
? texturePhysicalMemory.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
: 0;
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
@@ -771,12 +774,12 @@ namespace Ryujinx.Graphics.Gpu.Image
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
{
ulong samplerBufferAddress = _isCompute
(PhysicalMemory samplerPhysicalMemory, ulong samplerBufferAddress) = _isCompute
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped
? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
? samplerPhysicalMemory.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
: 0;
}
else
@@ -813,7 +816,8 @@ namespace Ryujinx.Graphics.Gpu.Image
if (poolAddress != MemoryManager.PteUnmapped)
{
texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_texturePoolGpuVa);
texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
_texturePool = texturePool;
}
}
@@ -824,7 +828,8 @@ namespace Ryujinx.Graphics.Gpu.Image
if (poolAddress != MemoryManager.PteUnmapped)
{
samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_samplerPoolGpuVa);
samplerPool = _samplerPoolCache.FindOrCreate(_channel, physical, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
_samplerPool = samplerPool;
}
}

View File

@@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader;
using System;
@@ -385,8 +386,9 @@ namespace Ryujinx.Graphics.Gpu.Image
public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
{
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolAddress);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache);
return texturePool;
}

View File

@@ -160,9 +160,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <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="physicalMemory">Backing memory of the pool</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>
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
public TexturePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId)
{
_channel = channel;
_aliasLists = new Dictionary<Texture, TextureAliasList>();
@@ -193,7 +194,9 @@ namespace Ryujinx.Graphics.Gpu.Image
}
TextureInfo info = GetInfo(descriptor, out int layerSize);
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
MemoryManager memoryManager = _channel.MemoryManager;
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 (texture == null)
@@ -421,7 +424,8 @@ namespace Ryujinx.Graphics.Gpu.Image
continue;
}
MultiRange range = _channel.MemoryManager.Physical.TextureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
TextureCache textureCache = _channel.MemoryManager.GetBackingMemory(address).TextureCache;
MultiRange range = textureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
// If the texture is not mapped at all, delete its reference.
@@ -446,7 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!range.Equals(texture.Range))
{
// Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range))
if (!textureCache.UpdateMapping(texture, range))
{
// Texture could not be remapped due to a collision, just delete it.
if (Interlocked.Exchange(ref Items[request.ID], null) != null)
@@ -481,6 +485,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="size">Size of the range being invalidated</param>
protected override void InvalidateRangeImpl(ulong address, ulong size)
{
MemoryManager memoryManager = _channel.MemoryManager;
ProcessDereferenceQueue();
ulong endAddress = address + size;
@@ -505,7 +510,8 @@ namespace Ryujinx.Graphics.Gpu.Image
if (texture.HasOneReference())
{
_channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache;
textureCache.AddShortCache(texture, ref cachedDescriptor);
}
if (Interlocked.Exchange(ref Items[id], null) != null)

View File

@@ -1,3 +1,5 @@
using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image
{
/// <summary>
@@ -20,11 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <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="physicalMemory">Backing memory of the pool</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>
protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
protected override TexturePool CreatePool(
GpuContext context,
GpuChannel channel,
PhysicalMemory physicalMemory,
ulong address,
int maximumId)
{
return new TexturePool(context, channel, address, maximumId);
return new TexturePool(context, channel, physicalMemory, address, maximumId);
}
}
}

View File

@@ -9,6 +9,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
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>
/// Physical memory ranges where the buffer is mapped.
/// </summary>
@@ -29,8 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="range">Physical memory ranges where the buffer is mapped</param>
/// <param name="flags">Buffer usage flags</param>
public BufferBounds(MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
public BufferBounds(PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
{
Physical = physical;
Range = range;
Flags = flags;
}

View File

@@ -735,18 +735,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <remarks>
/// This does a GPU side copy.
/// </remarks>
/// <param name="context">GPU context</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="dstVa">GPU virtual address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param>
public void CopyBuffer(MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
public static void CopyBuffer(GpuContext context, MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
{
MultiRange srcRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, srcVa, size, BufferStage.Copy);
MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size, BufferStage.Copy);
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcVa);
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstVa);
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)
{
CopyBufferSingleRange(memoryManager, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size);
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size);
}
else
{
@@ -777,7 +781,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong dstSize = dstSubRange.Size - dstOffset;
ulong copySize = Math.Min(srcSize, dstSize);
CopyBufferSingleRange(memoryManager, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
srcOffset += copySize;
dstOffset += copySize;
@@ -793,18 +797,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// This does a GPU side copy.
/// </remarks>
/// <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="dstAddress">Physical address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param>
private void CopyBufferSingleRange(MemoryManager memoryManager, ulong srcAddress, ulong dstAddress, ulong size)
private static void CopyBufferSingleRange(
GpuContext context,
PhysicalMemory srcPhysical,
PhysicalMemory dstPhysical,
ulong srcAddress,
ulong dstAddress,
ulong size)
{
Buffer srcBuffer = GetBuffer(srcAddress, size, BufferStage.Copy);
Buffer dstBuffer = GetBuffer(dstAddress, size, BufferStage.Copy);
Buffer srcBuffer = srcPhysical.BufferCache.GetBuffer(srcAddress, size, BufferStage.Copy);
Buffer dstBuffer = dstPhysical.BufferCache.GetBuffer(dstAddress, size, BufferStage.Copy);
int srcOffset = (int)(srcAddress - srcBuffer.Address);
int dstOffset = (int)(dstAddress - dstBuffer.Address);
_context.Renderer.Pipeline.CopyBuffer(
context.Renderer.Pipeline.CopyBuffer(
srcBuffer.Handle,
dstBuffer.Handle,
srcOffset,
@@ -820,7 +832,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.
dstBuffer.ClearModified(dstAddress, size);
memoryManager.Physical.WriteTrackedResource(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
dstPhysical.WriteTrackedResource(dstAddress, srcPhysical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
}
dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size);
@@ -849,7 +861,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
memoryManager.Physical.FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
memoryManager.GetBackingMemory(gpuVa).FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size);
}

View File

@@ -66,18 +66,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
Buffers = new BufferBounds[count];
Unaligned = new bool[count];
Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL)));
Buffers.AsSpan().Fill(new BufferBounds(null, new MultiRange(MemoryManager.PteUnmapped, 0UL)));
}
/// <summary>
/// Sets the region of a buffer at a given slot.
/// </summary>
/// <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="flags">Buffer usage flags</param>
public void SetBounds(int index, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
public void SetBounds(int index, PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
{
Buffers[index] = new BufferBounds(range, flags);
Buffers[index] = new BufferBounds(physical, range, flags);
}
/// <summary>
@@ -156,8 +157,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="type">Type of each index buffer element</param>
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer);
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache;
MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer);
_indexBuffer.BufferCache = bufferCache;
_indexBuffer.Range = range;
_indexBuffer.Type = type;
@@ -186,11 +189,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <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)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer);
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache;
MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer);
_vertexBuffers[index].Range = range;
_vertexBuffers[index].Stride = stride;
_vertexBuffers[index].Divisor = divisor;
ref VertexBuffer vb = ref _vertexBuffers[index];
vb.BufferCache = bufferCache;
vb.Range = range;
vb.Stride = stride;
vb.Divisor = divisor;
_vertexBuffersDirty = true;
@@ -213,9 +220,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the transform feedback buffer</param>
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback);
_transformFeedbackBuffers[index] = new BufferBounds(range);
_transformFeedbackBuffers[index] = new BufferBounds(physical, range);
_transformFeedbackBuffersDirty = true;
}
@@ -258,11 +266,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
_cpStorageBuffers.SetBounds(index, range, flags);
_cpStorageBuffers.SetBounds(index, physical, range, flags);
}
/// <summary>
@@ -282,16 +291,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(buffers, index, gpuVa);
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
if (!buffers.Buffers[index].Range.Equals(range))
{
_gpStorageBuffersDirty = true;
}
buffers.SetBounds(index, range, flags);
buffers.SetBounds(index, physical, range, flags);
}
/// <summary>
@@ -303,9 +313,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute);
_cpUniformBuffers.SetBounds(index, range);
_cpUniformBuffers.SetBounds(index, physical, range);
}
/// <summary>
@@ -318,9 +329,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage));
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage));
_gpUniformBuffers[stage].SetBounds(index, range);
_gpUniformBuffers[stage].SetBounds(index, physical, range);
_gpUniformBuffersDirty = true;
}
@@ -416,9 +428,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <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>
public ulong GetComputeUniformBufferAddress(int index)
public (PhysicalMemory, ulong) GetComputeUniformBufferAddress(int index)
{
return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address;
ref BufferBounds buffer = ref _cpUniformBuffers.Buffers[index];
return (buffer.Physical, buffer.Range.GetSubRange(0).Address);
}
/// <summary>
@@ -437,9 +450,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">Index of the shader stage</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>
public ulong GetGraphicsUniformBufferAddress(int stage, int index)
public (PhysicalMemory, ulong) GetGraphicsUniformBufferAddress(int stage, int index)
{
return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address;
ref BufferBounds buffer = ref _gpUniformBuffers[stage].Buffers[index];
return (buffer.Physical, buffer.Range.GetSubRange(0).Address);
}
/// <summary>
@@ -478,12 +492,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public void CommitComputeBindings()
{
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
BindBuffers(_cpStorageBuffers, isStorage: true);
BindBuffers(_cpUniformBuffers, isStorage: false);
BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
CommitBufferTextureBindings(bufferCache);
CommitBufferTextureBindings();
// Force rebind after doing compute work.
Rebind();
@@ -495,14 +507,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Commit any queued buffer texture bindings.
/// </summary>
/// <param name="bufferCache">Buffer cache</param>
private void CommitBufferTextureBindings(BufferCache bufferCache)
private void CommitBufferTextureBindings()
{
if (_bufferTextures.Count > 0)
{
foreach (BufferTextureBinding binding in _bufferTextures)
{
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore);
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore);
binding.Texture.SetStorage(range);
// The texture must be rebound to use the new storage if it was updated.
@@ -526,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
foreach (BufferTextureArrayBinding<ITextureArray> binding in _bufferTextureArrays)
{
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None);
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None);
binding.Texture.SetStorage(range);
textureArray[0] = binding.Texture;
@@ -536,7 +548,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
foreach (BufferTextureArrayBinding<IImageArray> binding in _bufferImageArrays)
{
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
binding.Texture.SetStorage(range);
textureArray[0] = binding.Texture;
@@ -555,8 +567,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="indexed">True if the index buffer is in use</param>
public void CommitGraphicsBindings(bool indexed)
{
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
if (indexed)
{
if (_indexBufferDirty || _rebind)
@@ -565,14 +575,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (!_indexBuffer.Range.IsUnmapped)
{
BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer);
BufferRange buffer = _indexBuffer.BufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer);
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
}
}
else if (!_indexBuffer.Range.IsUnmapped)
{
bufferCache.SynchronizeBufferRange(_indexBuffer.Range);
_indexBuffer.BufferCache.SynchronizeBufferRange(_indexBuffer.Range);
}
}
else if (_rebind)
@@ -597,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
BufferRange buffer = bufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer);
BufferRange buffer = vb.BufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer);
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
}
@@ -615,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
bufferCache.SynchronizeBufferRange(vb.Range);
vb.BufferCache.SynchronizeBufferRange(vb.Range);
}
}
@@ -637,7 +647,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
tfbs[index] = bufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true);
tfbs[index] = tfb.BufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true);
}
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@@ -684,7 +694,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true));
buffers[index] = new BufferAssignment(index, tfb.BufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true));
}
}
@@ -702,7 +712,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
bufferCache.SynchronizeBufferRange(tfb.Range);
tfb.BufferCache.SynchronizeBufferRange(tfb.Range);
}
}
@@ -710,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_gpStorageBuffersDirty = false;
BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
BindBuffers(_gpStorageBuffers, isStorage: true);
}
else
{
@@ -721,14 +731,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_gpUniformBuffersDirty = false;
BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
BindBuffers(_gpUniformBuffers, isStorage: false);
}
else
{
UpdateBuffers(_gpUniformBuffers);
}
CommitBufferTextureBindings(bufferCache);
CommitBufferTextureBindings();
_rebind = false;
@@ -742,7 +752,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
{
int rangesCount = 0;
@@ -763,8 +773,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
: bufferCache.GetBufferRange(bounds.Range, bufferStage);
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
: bounds.BufferCache.GetBufferRange(bounds.Range, bufferStage);
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
}
@@ -780,11 +790,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Bind respective buffer bindings on the host API.
/// </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="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
private void BindBuffers(BuffersPerStage buffers, bool isStorage)
{
int rangesCount = 0;
@@ -800,8 +809,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
: bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
: bounds.BufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
}
@@ -854,7 +863,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Range);
bounds.BufferCache.SynchronizeBufferRange(bounds.Range);
}
}
}
@@ -871,13 +880,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void SetBufferTextureStorage(
ShaderStage stage,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
bool isImage)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage));
_bufferTextures.Add(new BufferTextureBinding(stage, texture, bufferCache, range, bindingInfo, isImage));
}
/// <summary>
@@ -894,13 +904,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
ShaderStage stage,
ITextureArray array,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
int index)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index));
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, bufferCache, range, bindingInfo, index));
}
/// <summary>
@@ -917,13 +928,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
ShaderStage stage,
IImageArray array,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
int index)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index));
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, bufferCache, range, bindingInfo, index));
}
/// <summary>

View File

@@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public ITexture Texture { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache { get; }
/// <summary>
/// Physical ranges of memory where the buffer texture data is located.
/// </summary>
@@ -39,18 +44,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="array">Array</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="bindingInfo">Binding info</param>
/// <param name="index">Index of the binding on the array</param>
public BufferTextureArrayBinding(
T array,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
int index)
{
Array = array;
Texture = texture;
BufferCache = bufferCache;
Range = range;
BindingInfo = bindingInfo;
Index = index;

View File

@@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public ITexture Texture { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache { get; }
/// <summary>
/// Physical ranges of memory where the buffer texture data is located.
/// </summary>
@@ -40,18 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="stage">Shader stage accessing the 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="bindingInfo">Binding info</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
public BufferTextureBinding(
ShaderStage stage,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
bool isImage)
{
Stage = stage;
Texture = texture;
BufferCache = bufferCache;
Range = range;
BindingInfo = bindingInfo;
IsImage = isImage;

View File

@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
struct IndexBuffer
{
public BufferCache BufferCache;
public MultiRange Range;
public IndexType Type;
}

View File

@@ -1,4 +1,5 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using System;
@@ -35,10 +36,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
/// <summary>
/// Physical memory where the virtual memory is mapped into.
/// </summary>
internal PhysicalMemory Physical { get; }
private readonly GpuContext _context;
private readonly List<PhysicalMemory> _physicalMemoryList;
private readonly Dictionary<PhysicalMemory, byte> _physicalMemoryMap;
/// <summary>
/// Virtual range cache.
@@ -53,19 +53,65 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Creates a new instance of the GPU memory manager.
/// </summary>
/// <param name="context">GPU context</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>
internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize)
internal MemoryManager(GpuContext context, PhysicalMemory physicalMemory, ulong cpuMemorySize)
{
Physical = physicalMemory;
_context = context;
_physicalMemoryList = new List<PhysicalMemory>()
{
physicalMemory
};
_physicalMemoryMap = new Dictionary<PhysicalMemory, byte>
{
{ physicalMemory, 0 }
};
VirtualRangeCache = new VirtualRangeCache(this);
CounterCache = new CounterCache();
_pageTable = new ulong[PtLvl0Size][];
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += physicalMemory.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += physicalMemory.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
Physical.TextureCache.Initialize(cpuMemorySize);
physicalMemory.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>
@@ -81,15 +127,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (IsContiguous(va, size))
{
ulong address = Translate(va);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
if (tracked)
{
return Physical.ReadTracked<T>(address);
return physicalMemory.ReadTracked<T>(address);
}
else
{
return Physical.Read<T>(address);
return physicalMemory.Read<T>(address);
}
}
else
@@ -113,7 +159,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (IsContiguous(va, size))
{
return Physical.GetSpan(Translate(va), size, tracked);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetSpan(address, size, tracked);
}
else
{
@@ -138,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
bool isContiguous = true;
int mappedSize;
if (ValidateAddress(va) && GetPte(va) != PteUnmapped && Physical.IsMapped(Translate(va)))
if (ValidateAddress(va) && IsMappedOnGpuAndPhysical(va))
{
ulong endVa = va + (ulong)size;
ulong endVaAligned = (endVa + PageMask) & ~PageMask;
@@ -151,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong nextVa = currentVa + PageSize;
ulong nextPa = Translate(nextVa);
if (!ValidateAddress(nextVa) || GetPte(nextVa) == PteUnmapped || !Physical.IsMapped(nextPa))
if (!ValidateAddress(nextVa) || !IsMappedOnGpuAndPhysical(nextVa))
{
break;
}
@@ -180,7 +228,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (isContiguous)
{
return Physical.GetSpan(Translate(va), mappedSize, tracked);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetSpan(address, mappedSize, tracked);
}
else
{
@@ -192,6 +242,23 @@ 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>
/// Reads data from a possibly non-contiguous region of GPU mapped memory.
/// </summary>
@@ -209,22 +276,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0)
{
ulong pa = Translate(va);
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
Physical.GetSpan(pa, size, tracked).CopyTo(data[..size]);
physicalMemory.GetSpan(pa, size, tracked).CopyTo(data[..size]);
offset += size;
}
for (; offset < data.Length; offset += size)
{
ulong pa = Translate(va + (ulong)offset);
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
physicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
}
}
@@ -239,15 +306,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (IsContiguous(va, size))
{
return Physical.GetWritableRegion(Translate(va), size, tracked);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetWritableRegion(address, size, tracked);
}
else
{
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
Memory<byte> memory = new byte[size];
ReadImpl(va, memoryOwner.Span, tracked);
GetSpan(va, size).CopyTo(memory.Span);
return new WritableRegion(this, va, memoryOwner, tracked);
return new WritableRegion(this, va, memory, tracked);
}
}
@@ -269,7 +338,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void Write(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.Write);
WriteImpl(va, data, (physical, va, data) => physical.Write(va, data));
}
/// <summary>
@@ -279,7 +348,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.WriteTrackedResource);
WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data));
}
/// <summary>
@@ -289,10 +358,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.WriteUntracked);
WriteImpl(va, data, (physical, va, data) => physical.WriteUntracked(va, data));
}
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
private delegate void WriteCallback(PhysicalMemory physicalMemory, ulong address, ReadOnlySpan<byte> data);
/// <summary>
/// Writes data to possibly non-contiguous GPU mapped memory.
@@ -304,7 +373,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (IsContiguous(va, data.Length))
{
writeCallback(Translate(va), data);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
writeCallback(physicalMemory, address, data);
}
else
{
@@ -312,22 +383,67 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0)
{
ulong pa = Translate(va);
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
writeCallback(pa, data[..size]);
writeCallback(physicalMemory, pa, data[..size]);
offset += size;
}
for (; offset < data.Length; offset += size)
{
ulong pa = Translate(va + (ulong)offset);
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
writeCallback(pa, data.Slice(offset, size));
writeCallback(physicalMemory, 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));
}
}
}
}
@@ -359,15 +475,51 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of 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)
{
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)
{
UnmapEventArgs e = new(va, size);
MemoryUnmapped?.Invoke(this, e);
byte pIndex = physicalMemory != null ? GetOrAddPhysicalMemory(physicalMemory) : (byte)0;
for (ulong offset = 0; offset < size; offset += PageSize)
{
SetPte(va + offset, PackPte(pa + offset, kind));
SetPte(va + offset, PackPte(pa + offset, pIndex, kind));
}
RunRemapActions(e);
@@ -418,12 +570,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int page = 0; page < pages - 1; page++)
{
if (!ValidateAddress(va + PageSize) || GetPte(va + PageSize) == PteUnmapped)
ulong nextPte = GetPte(va + PageSize);
if (!ValidateAddress(va + PageSize) || nextPte == PteUnmapped)
{
return false;
}
if (Translate(va) + PageSize != Translate(va + PageSize))
if (GetPte(va) + PageSize != nextPte)
{
return false;
}
@@ -457,7 +611,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
int pages = (int)((endVaRounded - va) / PageSize);
List<MemoryRange> regions = [];
List<MemoryRange> regions = new();
for (int page = 0; page < pages - 1; page++)
{
@@ -535,6 +689,49 @@ namespace Ryujinx.Graphics.Gpu.Memory
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>
/// Validates a GPU virtual address.
/// </summary>
@@ -636,6 +833,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
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>
/// 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.
@@ -659,6 +878,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
return UnpackKindFromPte(pte);
}
public bool IsForeignMapping(ulong va)
{
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return false;
}
return UnpackPIndexFromPte(pte) != 0;
}
/// <summary>
/// Gets the Page Table entry for a given GPU virtual address.
/// </summary>
@@ -704,11 +935,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a page table entry from a physical address and kind.
/// </summary>
/// <param name="pa">Physical address</param>
/// <param name="pIndex">Index of the physical memory on the list</param>
/// <param name="kind">Kind</param>
/// <returns>Page table entry</returns>
private static ulong PackPte(ulong pa, PteKind kind)
private static ulong PackPte(ulong pa, byte pIndex, PteKind kind)
{
return pa | ((ulong)kind << 56);
return pa | ((ulong)pIndex << 48) | ((ulong)kind << 56);
}
/// <summary>
@@ -721,6 +953,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
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>
/// Unpacks physical address from a page table entry.
/// </summary>
@@ -728,7 +970,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>Physical address</returns>
private static ulong UnpackPaFromPte(ulong pte)
{
return pte & 0xffffffffffffffUL;
return pte & 0xffffffffffffUL;
}
}
}

View File

@@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
struct VertexBuffer
{
public BufferCache BufferCache;
public MultiRange Range;
public int Stride;
public int Divisor;

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using System;
@@ -66,11 +67,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <inheritdoc/>
public uint ConstantBuffer1Read(int offset)
{
ulong baseAddress = _compute
(PhysicalMemory physical, ulong baseAddress) = _compute
? _channel.BufferManager.GetComputeUniformBufferAddress(1)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1);
return _channel.MemoryManager.Physical.Read<uint>(baseAddress + (ulong)offset);
return physical.Read<uint>(baseAddress + (ulong)offset);
}
/// <inheritdoc/>

View File

@@ -733,15 +733,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
byte[] codeB,
bool asCompute)
{
ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
(PhysicalMemory physical, ulong cb1DataAddress) = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
MemoryManager memoryManager = channel.MemoryManager;
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
byte[] cb1DataA = ReadArray(memoryManager, cb1DataAddress, vertexA.Cb1DataSize);
byte[] cb1DataB = ReadArray(memoryManager, cb1DataAddress, currentStage.Cb1DataSize);
byte[] cb1DataA = ReadArray(physical, cb1DataAddress, vertexA.Cb1DataSize);
byte[] cb1DataB = ReadArray(physical, cb1DataAddress, currentStage.Cb1DataSize);
ShaderDumpPaths pathsA = default;
ShaderDumpPaths pathsB = default;
@@ -775,11 +775,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
MemoryManager memoryManager = channel.MemoryManager;
ulong cb1DataAddress = context.Stage == ShaderStage.Compute
(PhysicalMemory physical, ulong cb1DataAddress) = context.Stage == ShaderStage.Compute
? channel.BufferManager.GetComputeUniformBufferAddress(1)
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
byte[] cb1Data = ReadArray(memoryManager, cb1DataAddress, context.Cb1DataSize);
byte[] cb1Data = ReadArray(physical, cb1DataAddress, context.Cb1DataSize);
code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
@@ -793,18 +793,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0.
/// </summary>
/// <param name="memoryManager">Memory manager with the physical memory to read from</param>
/// <param name="physicalMemory">Physical memory to read the data from, might be null</param>
/// <param name="address">Physical address of the region to read</param>
/// <param name="size">Size in bytes of the data</param>
/// <returns>An array with the data at the specified memory location</returns>
private static byte[] ReadArray(MemoryManager memoryManager, ulong address, int size)
private static byte[] ReadArray(PhysicalMemory physicalMemory, ulong address, int size)
{
if (address == MemoryManager.PteUnmapped || size == 0)
if (address == MemoryManager.PteUnmapped || size == 0 || physicalMemory == null)
{
return [];
}
return memoryManager.Physical.GetSpan(address, size).ToArray();
return physicalMemory.GetSpan(address, size).ToArray();
}
/// <summary>

View File

@@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
cachedTextureBufferIndex = textureBufferIndex;
if (samplerBufferIndex == textureBufferIndex)
@@ -710,7 +710,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
cachedSamplerBufferIndex = samplerBufferIndex;
}

View File

@@ -9,11 +9,18 @@ using System.Threading;
namespace Ryujinx.Graphics.Gpu
{
using Texture = Image.Texture;
public record TextureData(int Width, int Height, byte[] Data);
/// <summary>
/// GPU image presentation window.
/// </summary>
public class Window
{
private const int CaptureTextureWidth = 1280;
private const int CaptureTextureHeight = 720;
private readonly GpuContext _context;
/// <summary>
@@ -85,7 +92,21 @@ 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 PresentedTexture _lastPresentedTexture;
private ITexture _captureTexture;
private int _framesAvailable;
@@ -188,6 +209,51 @@ namespace Ryujinx.Graphics.Gpu
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>
/// Presents a texture on the queue.
/// If the queue is empty, then no texture is presented.
@@ -205,6 +271,10 @@ namespace Ryujinx.Graphics.Gpu
pt.Cache.Tick();
EnsureCaptureTexture();
Volatile.Write(ref _lastPresentedTexture, new PresentedTexture(texture, pt.Crop));
texture.SynchronizeMemory();
ImageCrop crop = new(
@@ -244,6 +314,96 @@ 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>
/// Indicate that a frame on the queue is ready to be acquired.
/// </summary>

View File

@@ -1,4 +1,5 @@
using Silk.NET.Vulkan;
using System.Text.RegularExpressions;
namespace Ryujinx.Graphics.Vulkan
{

View File

@@ -15,6 +15,7 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
namespace Ryujinx.HLE.HOS.Applets.Error
{
@@ -158,15 +159,13 @@ namespace Ryujinx.HLE.HOS.Applets.Error
string[] buttons = GetButtonsText(module, description, "DlgBtn");
(uint Module, uint Description) errorCodeTuple = (module, uint.Parse(description.ToString("0000")));
bool showDetails = _horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Code: {module}-{description:0000}", "\n" + message, buttons, errorCodeTuple);
bool showDetails = _horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Code: {module}-{description:0000}", "\n" + message, buttons);
if (showDetails)
{
message = GetMessageText(module, description, "FlvMsg");
buttons = GetButtonsText(module, description, "FlvBtn");
_horizon.Device.UIHandler.DisplayErrorAppletDialog($"Details: {module}-{description:0000}", "\n" + message, buttons, errorCodeTuple);
_horizon.Device.UIHandler.DisplayErrorAppletDialog($"Details: {module}-{description:0000}", "\n" + message, buttons);
}
}

View File

@@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS
private readonly ICpuContext _cpuContext;
private T _memoryManager;
public IVirtualMemoryManager AddressSpace => _memoryManager;
public IVirtualMemoryManagerTracked AddressSpace => _memoryManager;
public ulong AddressSpaceSize { get; }

View File

@@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS
internal ServerBase TimeServer { get; private set; }
internal ServerBase ViServer { get; private set; }
internal ServerBase ViServerM { get; private set; }
internal ServerBase ViServerS { get; private set; }
internal ViServer ViServerS { get; private set; }
internal ServerBase LdnServer { get; private set; }
internal KSharedMemory HidSharedMem { get; private set; }
@@ -254,7 +254,7 @@ namespace Ryujinx.HLE.HOS
TimeServer = new ServerBase(KernelContext, "TimeServer");
ViServer = new ServerBase(KernelContext, "ViServerU");
ViServerM = new ServerBase(KernelContext, "ViServerM");
ViServerS = new ServerBase(KernelContext, "ViServerS");
ViServerS = new ViServer(KernelContext, "ViServerS");
LdnServer = new ServerBase(KernelContext, "LdnServer");
StartNewServices();

View File

@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
interface IProcessContext : IDisposable
{
IVirtualMemoryManager AddressSpace { get; }
IVirtualMemoryManagerTracked AddressSpace { get; }
ulong AddressSpaceSize { get; }

View File

@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private IProcessContextFactory _contextFactory;
public IProcessContext Context { get; private set; }
public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
public IVirtualMemoryManagerTracked CpuMemory => Context.AddressSpace;
public HleProcessDebugger Debugger { get; private set; }

View File

@@ -6,11 +6,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
class ProcessContext : IProcessContext
{
public IVirtualMemoryManager AddressSpace { get; }
public IVirtualMemoryManagerTracked AddressSpace { get; }
public ulong AddressSpaceSize { get; }
public ProcessContext(IVirtualMemoryManager asManager, ulong addressSpaceSize)
public ProcessContext(IVirtualMemoryManagerTracked asManager, ulong addressSpaceSize)
{
AddressSpace = asManager;
AddressSpaceSize = addressSpaceSize;

View File

@@ -102,5 +102,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandCmif(22)]
// AcquireLastApplicationCaptureSharedBuffer() -> (b8, u32)
public ResultCode AcquireLastApplicationCaptureSharedBuffer(ServiceCtx context)
{
context.ResponseData.Write(1);
context.ResponseData.Write(context.Device.System.ViServerS.GetApplicationLastPresentedFrameHandle(context.Device.Gpu));
return ResultCode.Success;
}
[CommandCmif(26)]
// AcquireCallerAppletCaptureSharedBuffer() -> (b8, u32)
public ResultCode AcquireCallerAppletCaptureSharedBuffer(ServiceCtx context)
{
// TODO: How does the handling for applets differ from the one for applications?
context.ResponseData.Write(1);
context.ResponseData.Write(context.Device.System.ViServerS.GetApplicationLastPresentedFrameHandle(context.Device.Gpu));
return ResultCode.Success;
}
[CommandCmif(27)]
public ResultCode ReleaseCallerAppletCaptureSharedBuffer(ServiceCtx context)
{
context.ResponseData.Write(2);
return ResultCode.Success;
}
}
}

View File

@@ -225,7 +225,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
public ResultCode CreateManagedDisplayLayer(ServiceCtx context)
{
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, _pid);
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
context.ResponseData.Write(layerId);
@@ -236,18 +235,27 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// IsSystemBufferSharingEnabled()
public ResultCode IsSystemBufferSharingEnabled(ServiceCtx context)
{
// NOTE: Service checks a private field and return an error if the SystemBufferSharing is disabled.
return ResultCode.NotImplemented;
// TODO: Implement this once we have a way to check if we're not an AppletId.Application
return ResultCode.Success;
}
[CommandCmif(42)] // 4.0.0+
// GetSystemSharedLayerHandle() -> (nn::vi::fbshare::SharedBufferHandle, nn::vi::fbshare::SharedLayerHandle)
public ResultCode GetSystemSharedLayerHandle(ServiceCtx context)
{
context.ResponseData.Write((ulong)context.Device.System.ViServerS.GetSharedBufferNvMapId());
context.ResponseData.Write(context.Device.System.ViServerS.GetSharedLayerId());
return ResultCode.Success;
}
[CommandCmif(44)] // 10.0.0+
// CreateManagedDisplaySeparableLayer() -> (u64, u64)
public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context)
{
context.Device.System.SurfaceFlinger.CreateLayer(out long displayLayerId, _pid);
context.Device.System.SurfaceFlinger.CreateLayer(out long recordingLayerId, _pid);
context.Device.System.SurfaceFlinger.SetRenderLayer(displayLayerId);
//context.Device.System.SurfaceFlinger.SetRenderLayer(displayLayerId);
context.ResponseData.Write(displayLayerId);
context.ResponseData.Write(recordingLayerId);

View File

@@ -35,6 +35,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new();
public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new();
internal PlayerIndex LastActiveNpad { get; set; }
public NpadDevices(Switch device, bool active = true) : base(device, active)
{
@@ -384,6 +386,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return;
}
LastActiveNpad = state.PlayerId;
ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
NpadCommonState newState = new()
@@ -639,5 +643,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return rumbleQueue;
}
public NpadIdType GetLastActiveNpadId()
{
return LastActiveNpad switch {
PlayerIndex.Player1 => NpadIdType.Player1,
PlayerIndex.Player2 => NpadIdType.Player2,
PlayerIndex.Player3 => NpadIdType.Player3,
PlayerIndex.Player4 => NpadIdType.Player4,
PlayerIndex.Player5 => NpadIdType.Player5,
PlayerIndex.Player6 => NpadIdType.Player6,
PlayerIndex.Player7 => NpadIdType.Player7,
PlayerIndex.Player8 => NpadIdType.Player8,
_ => NpadIdType.Handheld,
};
}
}
}

View File

@@ -24,15 +24,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// GetLastActiveNpad(u32) -> u8, u8
public ResultCode GetLastActiveNpad(ServiceCtx context)
{
// TODO: RequestData seems to have garbage data, reading an extra uint seems to fix the issue.
context.RequestData.ReadUInt32();
context.ResponseData.Write((byte)context.Device.Hid.Npads.GetLastActiveNpadId());
ResultCode resultCode = GetAppletFooterUiTypeImpl(context, out AppletFooterUiType appletFooterUiType);
context.ResponseData.Write((byte)appletFooterUiType);
context.ResponseData.Write((byte)0);
return resultCode;
return ResultCode.Success;
}
[CommandCmif(307)]

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
@@ -105,6 +106,26 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
return ResultCode.Success;
}
[CommandCmif(6)]
// SetRequirementPreset(u32)
public ResultCode SetRequirementPreset(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
return ResultCode.Success;
}
[CommandCmif(9)]
// SetNetworkProfileId(nn::util::Uuid)
public ResultCode SetNetworkProfileId(ServiceCtx context)
{
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { uuid });
return ResultCode.Success;
}
[CommandCmif(11)]
// SetConnectionConfirmationOption(i8)
public ResultCode SetConnectionConfirmationOption(ServiceCtx context)
@@ -142,5 +163,38 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
return ResultCode.Success;
}
[CommandCmif(23)]
// SetKeptInSleep(bool)
public ResultCode SetKeptInSleep(ServiceCtx context)
{
bool keptInSleep = context.RequestData.ReadBoolean();
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { keptInSleep });
return ResultCode.Success;
}
[CommandCmif(24)]
// RegisterSocketDescriptor(s32)
public ResultCode RegisterSocketDescriptor(ServiceCtx context)
{
int socketDescriptor = context.RequestData.ReadInt32();
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { socketDescriptor });
return ResultCode.Success;
}
[CommandCmif(25)]
// UnregisterSocketDescriptor(s32)
public ResultCode UnregisterSocketDescriptor(ServiceCtx context)
{
int socketDescriptor = context.RequestData.ReadInt32();
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { socketDescriptor });
return ResultCode.Success;
}
}
}

View File

@@ -245,8 +245,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
}
}
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments.NvMapHandle);
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(arguments.NvMapHandle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments.NvMapHandle:x8}!");
@@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
{
if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
{
_asContext.Gmm.Map(physicalAddress, arguments.Offset, size, (PteKind)arguments.Kind);
Map(physicalAddress, arguments.Offset, size, (PteKind)arguments.Kind, map.OwnerPid);
}
else
{
@@ -301,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
}
_asContext.Gmm.Map(physicalAddress, va, size, (PteKind)arguments.Kind);
Map(physicalAddress, va, size, (PteKind)arguments.Kind, map.OwnerPid);
arguments.Offset = va;
}
@@ -380,8 +380,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
ulong mapOffs = (ulong)argument.MapOffset << 16;
PteKind kind = (PteKind)argument.Kind;
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, nvmapHandle);
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(nvmapHandle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{nvmapHandle:x8}!");
@@ -389,13 +389,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
return NvInternalResult.InvalidInput;
}
gmm.Map(mapOffs + map.Address, gpuVa, size, kind);
Map(mapOffs + map.Address, gpuVa, size, kind, map.OwnerPid);
}
}
return NvInternalResult.Success;
}
private void Map(ulong pa, ulong va, ulong size, PteKind kind, ulong ownerPid)
{
if (Owner == ownerPid)
{
_asContext.Gmm.Map(pa, va, size, kind);
}
else
{
_asContext.Gmm.MapForeign(pa, va, size, kind, ownerPid);
}
}
public override void Close() { }
}
}

View File

@@ -167,8 +167,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
foreach (CommandBuffer commandBuffer in commandBuffers)
{
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBuffer.Mem);
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBuffer.Mem);
ReadOnlySpan<byte> data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
_host1xContext.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data), _contextId);
@@ -237,8 +237,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
{
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBufferEntry.MapHandle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
@@ -279,8 +279,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
{
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBufferEntry.MapHandle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");

View File

@@ -71,8 +71,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
uint size = BitUtils.AlignUp(arguments.Size, (uint)MemoryManager.PageSize);
arguments.Handle = CreateHandleFromMap(new NvMapHandle(size));
arguments.Handle = CreateHandleFromMap(new NvMapHandle(size) { OwnerPid = Owner });
Logger.Debug?.Print(LogClass.ServiceNv, $"Created map {arguments.Handle} with size 0x{size:x8}!");
return NvInternalResult.Success;
@@ -80,8 +80,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
private NvInternalResult FromId(ref NvMapFromId arguments)
{
NvMapHandle map = GetMapFromHandle(Owner, arguments.Id);
NvMapHandle map = GetMapFromHandle(arguments.Id);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
@@ -98,8 +98,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
private NvInternalResult Alloc(ref NvMapAlloc arguments)
{
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
NvMapHandle map = GetMapFromHandle(arguments.Handle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
@@ -152,8 +152,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
private NvInternalResult Free(ref NvMapFree arguments)
{
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
NvMapHandle map = GetMapFromHandle(arguments.Handle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
@@ -179,8 +179,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
private NvInternalResult Param(ref NvMapParam arguments)
{
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
NvMapHandle map = GetMapFromHandle(arguments.Handle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
@@ -217,8 +217,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
private NvInternalResult GetId(ref NvMapGetId arguments)
{
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
NvMapHandle map = GetMapFromHandle(arguments.Handle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
@@ -237,25 +237,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
// _maps.TryRemove(GetOwner(), out _);
}
private int CreateHandleFromMap(NvMapHandle map)
public static int CreateMap(ulong pid, ulong address, uint size)
{
return CreateHandleFromMap(new NvMapHandle(size) { Address = address, OwnerPid = pid });
}
private static int CreateHandleFromMap(NvMapHandle map)
{
return _maps.Add(map);
}
private static bool DeleteMapWithHandle(ulong pid, int handle)
private static bool DeleteMapWithHandle(int handle)
{
return _maps.Delete(handle) != null;
}
public static void IncrementMapRefCount(ulong pid, int handle)
{
GetMapFromHandle(pid, handle)?.IncrementRefCount();
GetMapFromHandle(handle)?.IncrementRefCount();
}
public static bool DecrementMapRefCount(ulong pid, int handle)
{
NvMapHandle map = GetMapFromHandle(pid, handle);
NvMapHandle map = GetMapFromHandle(handle);
if (map == null)
{
return false;
@@ -263,8 +268,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
if (map.DecrementRefCount() <= 0)
{
DeleteMapWithHandle(pid, handle);
DeleteMapWithHandle(handle);
Logger.Debug?.Print(LogClass.ServiceNv, $"Deleted map {handle}!");
return true;
@@ -275,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
}
}
public static NvMapHandle GetMapFromHandle(ulong pid, int handle)
public static NvMapHandle GetMapFromHandle(int handle)
{
return _maps.Get(handle);
}

View File

@@ -14,7 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
public ulong Address;
public bool Allocated;
public ulong DmaMapAddress;
public ulong OwnerPid;
private long _dupes;
public NvMapHandle()

View File

@@ -172,6 +172,15 @@ namespace Ryujinx.HLE.HOS.Services
{
ServerLoop();
}
protected virtual ulong CalculateRequiredHeapSize()
{
return 0UL;
}
protected virtual void CustomInit(KernelContext context, ulong pid, ulong heapAddress)
{
}
private void ServerLoop()
{
@@ -196,10 +205,14 @@ namespace Ryujinx.HLE.HOS.Services
Result result = _selfProcess.HandleTable.GenerateHandle(_wakeEvent.ReadableEvent, out _wakeHandle);
InitDone.Set();
ulong heapSize = CalculateRequiredHeapSize() + PointerBufferSize;
ulong messagePtr = _selfThread.TlsAddress;
_context.Syscall.SetHeapSize(out ulong heapAddr, 0x200000);
_context.Syscall.SetHeapSize(out ulong heapAddr, BitUtils.AlignUp(heapSize, 0x200000UL));
CustomInit(_context, _selfProcess.Pid, heapAddr + PointerBufferSize);
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));

View File

@@ -150,7 +150,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ BsdSocketOption.SoLinger, SocketOptionName.Linger },
{ BsdSocketOption.SoOobInline, SocketOptionName.OutOfBandInline },
{ BsdSocketOption.SoReusePort, SocketOptionName.ReuseAddress },
{ BsdSocketOption.SoNoSigpipe, SocketOptionName.DontLinger },
{ BsdSocketOption.SoSndBuf, SocketOptionName.SendBuffer },
{ BsdSocketOption.SoRcvBuf, SocketOptionName.ReceiveBuffer },
{ BsdSocketOption.SoSndLoWat, SocketOptionName.SendLowWater },

View File

@@ -4,6 +4,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.Common.PreciseSleep;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using System;
using System.Collections.Generic;
@@ -38,8 +39,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private readonly Lock _lock = new();
public long RenderLayerId { get; private set; }
private class Layer
{
public int ProducerBinderId;
@@ -60,7 +59,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
_device = device;
_layers = new Dictionary<long, Layer>();
RenderLayerId = 0;
_composerThread = new Thread(HandleComposition)
{
@@ -239,34 +237,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private void CloseLayer(long layerId, Layer layer)
{
// If the layer was removed and the current in use, we need to change the current layer in use.
if (RenderLayerId == layerId)
{
// If no layer is availaible, reset to default value.
if (_layers.Count == 0)
{
SetRenderLayer(0);
}
else
{
SetRenderLayer(_layers.Last().Key);
}
}
if (layer.State == LayerState.ManagedOpened)
{
layer.State = LayerState.ManagedClosed;
}
}
public void SetRenderLayer(long layerId)
{
lock (_lock)
{
RenderLayerId = layerId;
}
}
private Layer GetLayerByIdLocked(long layerId)
{
foreach (KeyValuePair<long, Layer> pair in _layers)
@@ -360,41 +336,55 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
lock (_lock)
{
// TODO: support multilayers (& multidisplay ?)
if (RenderLayerId == 0)
foreach (var (layerId, layer) in _layers)
{
return;
}
Layer layer = GetLayerByIdLocked(RenderLayerId);
Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
if (acquireStatus == Status.Success)
{
if (_device.VSyncMode == VSyncMode.Unbounded)
if (layer.State == LayerState.NotInitialized || layer.State == LayerState.ManagedClosed)
continue;
if (_device.System.KernelContext.Processes.TryGetValue(layer.Owner, out var process))
{
if (_swapInterval != 0)
if (process.State == ProcessState.Exiting || process.State == ProcessState.Exited)
{
UpdateSwapInterval(0);
_vSyncMode = _device.VSyncMode;
HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
if (_layers.Remove(layerId))
{
CloseLayer(layerId, layer);
}
continue;
}
}
else if (_device.VSyncMode != _vSyncMode)
Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
if (acquireStatus == Status.Success)
{
UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval);
_vSyncMode = _device.VSyncMode;
}
else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval)
{
UpdateSwapInterval(item.SwapInterval);
}
if (_device.VSyncMode == VSyncMode.Unbounded)
{
if (_swapInterval != 0)
{
UpdateSwapInterval(0);
_vSyncMode = _device.VSyncMode;
}
}
else if (_device.VSyncMode != _vSyncMode)
{
UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval);
_vSyncMode = _device.VSyncMode;
}
else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval)
{
UpdateSwapInterval(item.SwapInterval);
}
PostFrameBuffer(layer, item);
}
else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation)
{
throw new InvalidOperationException();
PostFrameBuffer(layer, item);
}
else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation)
{
Logger.Warning?.Print(LogClass.SurfaceFlinger, $"Failed to acquire buffer for layer {layerId} (status: {acquireStatus})");
continue;
}
}
}
}
@@ -413,8 +403,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
ulong bufferOffset = (ulong)item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset;
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(layer.Owner, nvMapHandle);
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(nvMapHandle);
ulong frameBufferAddress = map.Address + bufferOffset;
Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat);

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
@@ -36,6 +37,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public int PlanesCount;
[FieldOffset(0x34)]
public NvGraphicBufferSurfaceArray Surfaces;
public Array3<NvGraphicBufferSurface> Surfaces;
}
}

View File

@@ -40,7 +40,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId);
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid);
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
context.ResponseData.Write(layerId);

View File

@@ -1,4 +1,11 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types.Fbshare;
using Ryujinx.Horizon.Common;
using System;
namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
{
@@ -7,6 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
#pragma warning disable IDE0052 // Remove unread private member
private readonly IApplicationDisplayService _applicationDisplayService;
#pragma warning restore IDE0052
private KEvent _sharedFramebufferAcquirableEvent;
private int _sharedFramebufferAcquirableEventHandle;
public ISystemDisplayService(IApplicationDisplayService applicationDisplayService)
{
@@ -57,5 +67,138 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
return ResultCode.Success;
}
[CommandCmif(8225)] // 4.0.0+
// GetSharedBufferMemoryHandleId()
public ResultCode GetSharedBufferMemoryHandleId(ServiceCtx context)
{
context.ResponseData.Write((ulong)context.Device.System.ViServerS.GetSharedBufferNvMapId());
context.ResponseData.Write(context.Device.System.ViServerS.GetSharedBufferSize());
(ulong mapAddress, ulong mapSize) = context.Request.GetBufferType0x22();
context.Memory.Write(mapAddress, context.Device.System.ViServerS.GetSharedBufferMap());
return ResultCode.Success;
}
[CommandCmif(8250)] // 4.0.0+
// OpenSharedLayer(nn::vi::fbshare::SharedLayerHandle, nn::applet::AppletResourceUserId, pid)
public ResultCode OpenSharedLayer(ServiceCtx context)
{
long sharedLayerHandle = context.RequestData.ReadInt64();
long appletResourceUserId = context.RequestData.ReadInt64();
context.Device.System.ViServerS.OpenSharedLayer(sharedLayerHandle);
return ResultCode.Success;
}
[CommandCmif(8251)] // 4.0.0+
// CloseSharedLayer(nn::vi::fbshare::SharedLayerHandle)
public ResultCode CloseSharedLayer(ServiceCtx context)
{
long sharedLayerHandle = context.RequestData.ReadInt64();
context.Device.System.ViServerS.CloseSharedLayer(sharedLayerHandle);
return ResultCode.Success;
}
[CommandCmif(8252)] // 4.0.0+
// ConnectSharedLayer(nn::vi::fbshare::SharedLayerHandle)
public ResultCode ConnectSharedLayer(ServiceCtx context)
{
long sharedLayerHandle = context.RequestData.ReadInt64();
context.Device.System.ViServerS.ConnectSharedLayer(sharedLayerHandle);
return ResultCode.Success;
}
[CommandCmif(8253)] // 4.0.0+
// DisconnectSharedLayer(nn::vi::fbshare::SharedLayerHandle)
public ResultCode DisconnectSharedLayer(ServiceCtx context)
{
long sharedLayerHandle = context.RequestData.ReadInt64();
context.Device.System.ViServerS.DisconnectSharedLayer(sharedLayerHandle);
return ResultCode.Success;
}
[CommandCmif(8254)] // 4.0.0+
// AcquireSharedFrameBuffer(nn::vi::fbshare::SharedLayerHandle) -> (nn::vi::native::NativeSync, nn::vi::fbshare::SharedLayerTextureIndexList, u64)
public ResultCode AcquireSharedFrameBuffer(ServiceCtx context)
{
long sharedLayerHandle = context.RequestData.ReadInt64();
int slot = context.Device.System.ViServerS.DequeueFrameBuffer(sharedLayerHandle, out AndroidFence fence);
var indexList = new SharedLayerTextureIndexList();
for (int i = 0; i < indexList.Indices.Length; i++)
{
indexList.Indices[i] = context.Device.System.ViServerS.GetFrameBufferMapIndex(i);
}
context.ResponseData.WriteStruct(fence);
context.ResponseData.WriteStruct(indexList);
context.ResponseData.Write(0); // Padding
context.ResponseData.Write((ulong)slot);
return ResultCode.Success;
}
[CommandCmif(8255)] // 4.0.0+
// PresentSharedFrameBuffer(nn::vi::native::NativeSync, nn::vi::CropRegion, u32, u32, nn::vi::fbshare::SharedLayerHandle, u64)
public ResultCode PresentSharedFrameBuffer(ServiceCtx context)
{
AndroidFence nativeSync = context.RequestData.ReadStruct<AndroidFence>();
Rect cropRegion = context.RequestData.ReadStruct<Rect>();
NativeWindowTransform transform = (NativeWindowTransform)context.RequestData.ReadUInt32();
int swapInterval = context.RequestData.ReadInt32();
int padding = context.RequestData.ReadInt32();
long sharedLayerHandle = context.RequestData.ReadInt64();
ulong slot = context.RequestData.ReadUInt64();
context.Device.System.ViServerS.QueueFrameBuffer(sharedLayerHandle, (int)slot, cropRegion, transform, swapInterval, nativeSync);
return ResultCode.Success;
}
[CommandCmif(8256)] // 4.0.0+
// GetSharedFrameBufferAcquirableEvent(nn::vi::fbshare::SharedLayerHandle) -> handle<copy>
public ResultCode GetSharedFrameBufferAcquirableEvent(ServiceCtx context)
{
if (_sharedFramebufferAcquirableEventHandle == 0)
{
_sharedFramebufferAcquirableEvent = new KEvent(context.Device.System.KernelContext);
_sharedFramebufferAcquirableEvent.WritableEvent.Signal();
if (context.Process.HandleTable.GenerateHandle(_sharedFramebufferAcquirableEvent.ReadableEvent, out _sharedFramebufferAcquirableEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_sharedFramebufferAcquirableEventHandle);
return ResultCode.Success;
}
[CommandCmif(8258)] // 5.0.0+
// CancelSharedFrameBuffer(nn::vi::fbshare::SharedLayerHandle, u64)
public ResultCode CancelSharedFrameBuffer(ServiceCtx context)
{
long sharedLayerHandle = context.RequestData.ReadInt64();
ulong slot = context.RequestData.ReadUInt64();
context.Device.System.ViServerS.CancelFrameBuffer(sharedLayerHandle, (int)slot);
return ResultCode.Success;
}
}
}

View File

@@ -0,0 +1,11 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types.Fbshare
{
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x4)]
struct SharedLayerTextureIndexList
{
public Array4<int> Indices;
}
}

View File

@@ -248,8 +248,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
return result;
}
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
using Parcel parcel = new(0x28, 0x4);
parcel.WriteObject(producer, "dispdrv\0");
@@ -285,9 +283,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
// TODO: support multi display.
IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, 0, LayerState.Stray);
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
using Parcel parcel = new(0x28, 0x4);
parcel.WriteObject(producer, "dispdrv\0");

View File

@@ -0,0 +1,19 @@
using Ryujinx.Common.Memory;
namespace Ryujinx.HLE.HOS.Services.Vi.Types
{
struct SharedBufferMap
{
public struct Entry
{
public ulong Offset;
public ulong Size;
public uint Width;
public uint Height;
}
public int Count;
public int Padding;
public Array16<Entry> SharedBuffers;
}
}

View File

@@ -0,0 +1,235 @@
using Ryujinx.Common;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
using Ryujinx.HLE.HOS.Services.Vi.Types;
using Ryujinx.Memory;
using System.Runtime.CompilerServices;
namespace Ryujinx.HLE.HOS.Services
{
class ViServer : ServerBase
{
private const int TotalFramebuffers = 16;
private SurfaceFlinger.SurfaceFlinger _surfaceFlinger;
private readonly uint _fbWidth;
private readonly uint _fbHeight;
private readonly PixelFormat _fbFormat;
private readonly int _fbUsage;
private readonly uint _fbCount;
private uint _fbSlotsRequested;
private ulong _pid;
private ulong _fbsBaseAddress;
private int _bufferNvMapId;
private SharedBufferMap _bufferMap;
private long _sharedLayerId;
public ViServer(KernelContext context, string name) : base(context, name)
{
_fbWidth = 1280;
_fbHeight = 720;
_fbFormat = PixelFormat.Rgba8888;
_fbUsage = 0x100 | 0x200 | 0x800;
_fbCount = 4;
}
protected override ulong CalculateRequiredHeapSize()
{
return GetSharedBufferSize();
}
protected override void CustomInit(KernelContext context, ulong pid, ulong heapAddress)
{
_pid = pid;
_fbsBaseAddress = heapAddress;
context.Device.Gpu.RegisterProcess(pid, KernelStatic.GetCurrentProcess().CpuMemory);
ulong bufferSize = CalculateFramebufferSize();
ulong totalSize = bufferSize * TotalFramebuffers;
KernelStatic.GetCurrentProcess().CpuMemory.Fill(heapAddress, totalSize, 0xff);
int mapId = NvMapDeviceFile.CreateMap(pid, heapAddress, (uint)totalSize);
_bufferNvMapId = mapId;
_bufferMap.Count = TotalFramebuffers;
ulong offset = 0;
for (int i = 0; i < TotalFramebuffers; i++)
{
_bufferMap.SharedBuffers[i].Offset = offset;
_bufferMap.SharedBuffers[i].Size = bufferSize;
_bufferMap.SharedBuffers[i].Width = _fbWidth;
_bufferMap.SharedBuffers[i].Height = _fbHeight;
offset += bufferSize;
}
_surfaceFlinger = context.Device.System.SurfaceFlinger;
_surfaceFlinger.CreateLayer(out _sharedLayerId, pid);
}
public void OpenSharedLayer(long layerId)
{
_surfaceFlinger.OpenLayer(_pid, layerId, out _);
}
public void CloseSharedLayer(long layerId)
{
_surfaceFlinger.CloseLayer(layerId);
}
public void ConnectSharedLayer(long layerId)
{
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
producer.Connect(null, NativeWindowApi.NVN, false, out IGraphicBufferProducer.QueueBufferOutput output);
GraphicBuffer graphicBuffer = new GraphicBuffer();
int gobHeightLog2 = 4;
int blockHeight = 8 * (1 << gobHeightLog2);
uint widthAlignedBytes = BitUtils.AlignUp(_fbWidth * 4, 64u);
uint widthAligned = widthAlignedBytes / 4;
uint heightAligned = BitUtils.AlignUp(_fbHeight, (uint)blockHeight);
uint totalSize = widthAlignedBytes * heightAligned;
graphicBuffer.Header.Magic = 0x47424652;
graphicBuffer.Header.Width = (int)_fbWidth;
graphicBuffer.Header.Height = (int)_fbHeight;
graphicBuffer.Header.Stride = (int)widthAligned;
graphicBuffer.Header.Format = _fbFormat;
graphicBuffer.Header.Usage = _fbUsage;
graphicBuffer.Header.IntsCount = (Unsafe.SizeOf<GraphicBuffer>() - Unsafe.SizeOf<GraphicBufferHeader>()) / sizeof(int);
graphicBuffer.Buffer.NvMapId = _bufferNvMapId;
graphicBuffer.Buffer.Magic = unchecked((int)0xDAFFCAFF);
graphicBuffer.Buffer.Pid = 42;
graphicBuffer.Buffer.Usage = _fbUsage;
graphicBuffer.Buffer.PixelFormat = (int)_fbFormat;
graphicBuffer.Buffer.ExternalPixelFormat = (int)_fbFormat;
graphicBuffer.Buffer.Stride = (int)widthAligned;
graphicBuffer.Buffer.FrameBufferSize = (int)totalSize;
graphicBuffer.Buffer.PlanesCount = 1;
graphicBuffer.Buffer.Surfaces[0].Width = _fbWidth;
graphicBuffer.Buffer.Surfaces[0].Height = _fbHeight;
graphicBuffer.Buffer.Surfaces[0].ColorFormat = ColorFormat.A8B8G8R8;
graphicBuffer.Buffer.Surfaces[0].Layout = 3; // Block linear
graphicBuffer.Buffer.Surfaces[0].Pitch = (int)widthAlignedBytes;
graphicBuffer.Buffer.Surfaces[0].Kind = 0xfe; // Generic 16Bx2
graphicBuffer.Buffer.Surfaces[0].BlockHeightLog2 = gobHeightLog2;
graphicBuffer.Buffer.Surfaces[0].Size = (int)totalSize;
for (int slot = 0; slot < _fbCount; slot++)
{
graphicBuffer.Buffer.Surfaces[0].Offset = slot * (int)totalSize;
producer.SetPreallocatedBuffer(slot, new AndroidStrongPointer<GraphicBuffer>(graphicBuffer));
}
_fbSlotsRequested = 0;
}
public void DisconnectSharedLayer(long layerId)
{
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
producer.Disconnect(NativeWindowApi.NVN);
}
public int DequeueFrameBuffer(long layerId, out AndroidFence fence)
{
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
Status status = producer.DequeueBuffer(out int slot, out fence, false, _fbWidth, _fbHeight, _fbFormat, (uint)_fbUsage);
if (status == Status.Success)
{
if ((_fbSlotsRequested & (1u << slot)) == 0)
{
status = producer.RequestBuffer(slot, out _);
if (status != Status.Success)
{
producer.CancelBuffer(slot, ref fence);
}
_fbSlotsRequested |= 1u << slot;
}
}
return slot;
}
public void QueueFrameBuffer(long layerId, int slot, Rect crop, NativeWindowTransform transform, int swapInterval, AndroidFence fence)
{
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
IGraphicBufferProducer.QueueBufferInput input = new();
input.Crop = crop;
input.Transform = transform;
input.SwapInterval = swapInterval;
input.Fence = fence;
Status status = producer.QueueBuffer(slot, ref input, out _);
}
public void CancelFrameBuffer(long layerId, int slot)
{
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
AndroidFence fence = default;
producer.CancelBuffer(slot, ref fence);
}
public int GetFrameBufferMapIndex(int index)
{
return (uint)index < _fbCount ? index : 0;
}
public int GetSharedBufferNvMapId()
{
return _bufferNvMapId;
}
public ulong GetSharedBufferSize()
{
return CalculateFramebufferSize() * TotalFramebuffers;
}
public long GetSharedLayerId()
{
return _sharedLayerId;
}
private ulong CalculateFramebufferSize()
{
// Each GOB dimension is 512 bytes x 8 lines.
// Assume 16 GOBs, for a total of 16 x 8 = 128 lines.
return BitUtils.AlignUp(_fbWidth * 4, 512u) * BitUtils.AlignUp(_fbHeight, 128u);
}
public SharedBufferMap GetSharedBufferMap()
{
return _bufferMap;
}
public int GetApplicationLastPresentedFrameHandle(GpuContext gpuContext)
{
TextureData texture = gpuContext.Window.GetLastPresentedData();
IVirtualMemoryManagerTracked selfAs = KernelStatic.GetProcessByPid(_pid).CpuMemory;
int fbIndex = (int)_fbCount; // Place it after all our frame buffers.
selfAs.Write(_fbsBaseAddress + _bufferMap.SharedBuffers[fbIndex].Offset, texture.Data);
return fbIndex;
}
}
}

View File

@@ -45,12 +45,10 @@ namespace Ryujinx.HLE.UI
/// <param name="value">The value associated to the <paramref name="kind"/>.</param>
void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value);
/// <summary>
/// Displays a Message Dialog box specific to Error Applet and blocks until it is closed.
/// </summary>
/// <returns>False when OK is pressed, True when another button (Details) is pressed.</returns>
// ReSharper disable once UnusedParameter.Global
bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null);
bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText);
/// <summary>
/// Creates a handler to process keyboard inputs into text strings.

View File

@@ -1,5 +1,5 @@
using MsgPack;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Prepo.Types;
using Ryujinx.Memory;
using System;
using System.Threading;
@@ -8,7 +8,7 @@ namespace Ryujinx.Horizon
{
public static class HorizonStatic
{
internal static void HandlePlayReport(PlayReport report) =>
internal static void HandlePlayReport(MessagePackObject report) =>
new Thread(() => PlayReport?.Invoke(report))
{
Name = "HLE.PlayReportEvent",
@@ -16,7 +16,7 @@ namespace Ryujinx.Horizon
Priority = ThreadPriority.AboveNormal
}.Start();
public static event Action<PlayReport> PlayReport;
public static event Action<MessagePackObject> PlayReport;
[field: ThreadStatic]
public static HorizonOptions Options { get; private set; }

View File

@@ -1,3 +1,4 @@
using Gommon;
using MsgPack;
using MsgPack.Serialization;
using Ryujinx.Common.Logging;
@@ -11,12 +12,19 @@ using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;
using System.Text;
using System.Threading;
using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId;
namespace Ryujinx.Horizon.Prepo.Ipc
{
partial class PrepoService : IPrepoService
{
enum PlayReportKind
{
Normal,
System,
}
private readonly ArpApi _arp;
private readonly PrepoServicePermissionLevel _permissionLevel;
private ulong _systemSessionId;
@@ -188,17 +196,10 @@ namespace Ryujinx.Horizon.Prepo.Ipc
{
return PrepoResult.InvalidBufferSize;
}
StringBuilder builder = new();
MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
PlayReport playReport = new()
{
Kind = playReportKind,
Room = gameRoom,
ReportData = deserializedReport
};
builder.AppendLine();
builder.AppendLine("PlayReport log:");
builder.AppendLine($" Kind: {playReportKind}");
@@ -208,12 +209,10 @@ namespace Ryujinx.Horizon.Prepo.Ipc
if (pid != 0)
{
builder.AppendLine($" Pid: {pid}");
playReport.Pid = pid;
}
else
{
builder.AppendLine($" ApplicationId: {applicationId}");
playReport.AppId = applicationId;
}
Result result = _arp.GetApplicationInstanceId(out ulong applicationInstanceId, pid);
@@ -224,20 +223,17 @@ namespace Ryujinx.Horizon.Prepo.Ipc
_arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty applicationLaunchProperty, applicationInstanceId).AbortOnFailure();
playReport.Version = applicationLaunchProperty.Version;
builder.AppendLine($" ApplicationVersion: {applicationLaunchProperty.Version}");
if (!userId.IsNull)
{
builder.AppendLine($" UserId: {userId}");
playReport.UserId = userId;
}
builder.AppendLine($" Room: {gameRoom}");
builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
HorizonStatic.HandlePlayReport(playReport);
HorizonStatic.HandlePlayReport(deserializedReport);
Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString());

View File

@@ -1,24 +0,0 @@
using MsgPack;
using Ryujinx.Horizon.Sdk.Account;
using Ryujinx.Horizon.Sdk.Ncm;
namespace Ryujinx.Horizon.Prepo.Types
{
public struct PlayReport
{
public PlayReportKind Kind { get; init; }
public string Room { get; init; }
public MessagePackObject ReportData { get; init; }
public ApplicationId? AppId;
public ulong? Pid;
public uint Version;
public Uid? UserId;
}
public enum PlayReportKind
{
Normal,
System,
}
}

View File

@@ -185,15 +185,6 @@ namespace Ryujinx.Input.HLE
}
}
public bool InputUpdatesBlocked
{
get
{
lock (_lock)
return _blockInputUpdates;
}
}
public void BlockInputUpdates()
{
lock (_lock)

View File

@@ -1,4 +1,5 @@
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -9,7 +10,7 @@ namespace Ryujinx.Memory
/// Represents a address space manager.
/// Supports virtual memory region mapping, address translation and read/write access to mapped regions.
/// </summary>
public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManager
public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManagerTracked
{
/// <inheritdoc/>
public bool UsesPrivateAllocations => false;
@@ -21,6 +22,8 @@ namespace Ryujinx.Memory
private readonly MemoryBlock _backingMemory;
private readonly PageTable<nuint> _pageTable;
private readonly MemoryTracking _tracking;
private bool _writeTracked;
protected override ulong AddressSpaceSize { get; }
@@ -44,6 +47,7 @@ namespace Ryujinx.Memory
AddressSpaceSize = asSize;
_backingMemory = backingMemory;
_pageTable = new PageTable<nuint>();
_tracking = new MemoryTracking(this, 0x1000);
}
/// <inheritdoc/>
@@ -227,7 +231,7 @@ namespace Ryujinx.Memory
/// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest = false)
{
throw new NotImplementedException();
_writeTracked = true;
}
protected unsafe override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
@@ -240,5 +244,29 @@ namespace Ryujinx.Memory
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
=> GetHostAddress(va);
/// <inheritdoc/>
public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{
if (_writeTracked)
{
_tracking.VirtualMemoryEvent(va, size, write, precise);
}
}
/// <inheritdoc/>
public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None)
{
return _tracking.BeginTracking(address, size, id, flags);
}
/// <inheritdoc/>
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags = RegionFlags.None)
{
return _tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
}
/// <inheritdoc/>
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{
return _tracking.BeginSmartGranularTracking(address, size, granularity, id);
}
}
}

View File

@@ -3,7 +3,7 @@ using Ryujinx.Memory.Tracking;
using System;
using System.Collections.Generic;
namespace Ryujinx.Cpu
namespace Ryujinx.Memory
{
public interface IVirtualMemoryManagerTracked : IVirtualMemoryManager
{

View File

@@ -517,7 +517,7 @@ namespace Ryujinx.Ava
Device?.System.ChangeDockedModeState(e.NewValue);
}
public void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
{
Device?.SetVolume(e.NewValue);
@@ -1041,7 +1041,6 @@ namespace Ryujinx.Ava
if (_viewModel.StartGamesInFullscreen)
{
_viewModel.WindowState = WindowState.FullScreen;
_viewModel.Window.TitleBar.ExtendsContentIntoTitleBar = true;
}
if (_viewModel.WindowState is WindowState.FullScreen || _viewModel.StartGamesWithoutUI)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
using System;
namespace Ryujinx.Ava.Common
{
public static class ThemeManager
{
public static event Action ThemeChanged;
public static void OnThemeChanged()
{
ThemeChanged?.Invoke();
}
}
}

View File

@@ -1,5 +1,6 @@
using DiscordRPC;
using Gommon;
using MsgPack;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Configuration;
@@ -9,7 +10,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE;
using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.Horizon;
using Ryujinx.Horizon.Prepo.Types;
using System.Text;
namespace Ryujinx.Ava
@@ -37,9 +37,6 @@ namespace Ryujinx.Ava
private static RichPresence _discordPresencePlaying;
private static ApplicationMetadata _currentApp;
public static bool HasAssetImage(string titleId) => TitleIDs.DiscordGameAssetKeys.ContainsIgnoreCase(titleId);
public static bool HasAnalyzer(string titleId) => PlayReports.Analyzer.TitleIds.ContainsIgnoreCase(titleId);
public static void Initialize()
{
_discordPresenceMain = new RichPresence
@@ -56,7 +53,6 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
HorizonStatic.PlayReport += HandlePlayReport;
PlayReports.Initialize();
}
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
@@ -124,22 +120,20 @@ namespace Ryujinx.Ava
_currentApp = null;
}
private static void HandlePlayReport(PlayReport playReport)
private static void HandlePlayReport(MessagePackObject playReport)
{
if (_discordClient is null) return;
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
if (_discordPresencePlaying is null) return;
FormattedValue formattedValue =
Analyzer.FormattedValue formattedValue =
PlayReports.Analyzer.Format(TitleIDs.CurrentApplication.Value, _currentApp, playReport);
if (!formattedValue.Handled) return;
_discordPresencePlaying.Details = TruncateToByteLength(
formattedValue.Reset
? $"Playing {_currentApp.Title}"
: formattedValue.FormattedString
);
_discordPresencePlaying.Details = formattedValue.Reset
? $"Playing {_currentApp.Title}"
: formattedValue.FormattedString;
if (_discordClient.CurrentPresence.Details.Equals(_discordPresencePlaying.Details))
return; //don't trigger an update if the set presence Details are identical to current

View File

@@ -148,7 +148,7 @@ namespace Ryujinx.Headless
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
if (NeedsOverride(nameof(IgnoreControllerApplet)))
IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet;
IgnoreControllerApplet = configurationState.System.IgnoreApplet;
return;
@@ -387,7 +387,7 @@ namespace Ryujinx.Headless
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
public string GraphicsShadersDumpPath { get; set; }
[Option("graphics-backend", Required = false, Default = GraphicsBackend.Vulkan, HelpText = "Change Graphics Backend to use.")]
[Option("graphics-backend", Required = false, Default = GraphicsBackend.OpenGl, HelpText = "Change Graphics Backend to use.")]
public GraphicsBackend GraphicsBackend { get; set; }
[Option("preferred-gpu-vendor", Required = false, Default = "", HelpText = "When using the Vulkan backend, prefer using the GPU from the specified vendor.")]

View File

@@ -513,7 +513,7 @@ namespace Ryujinx.Headless
Exit();
}
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null)
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText)
{
SDL_MessageBoxData data = new()
{
@@ -521,7 +521,7 @@ namespace Ryujinx.Headless
message = message,
buttons = new SDL_MessageBoxButtonData[buttonsText.Length],
numbuttons = buttonsText.Length,
window = WindowHandle
window = WindowHandle,
};
for (int i = 0; i < buttonsText.Length; i++)

View File

@@ -22,8 +22,6 @@ namespace Ryujinx.Ava
{
public class RyujinxApp : Application
{
public static event Action ThemeChanged;
internal static string FormatTitle(LocaleKeys? windowTitleKey = null, bool includeVersion = true)
=> windowTitleKey is null
? $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)}"
@@ -114,7 +112,7 @@ namespace Ryujinx.Ava
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
}
ThemeChanged?.Invoke();
ThemeManager.OnThemeChanged();
RequestedThemeVariant = baseStyle switch
{

View File

@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Applet
bool okPressed = false;
if (ConfigurationState.Instance.System.IgnoreControllerApplet)
if (ConfigurationState.Instance.System.IgnoreApplet)
return false;
Dispatcher.UIThread.InvokeAsync(async () =>
@@ -75,32 +75,31 @@ namespace Ryujinx.Ava.UI.Applet
bool opened = false;
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
title,
message,
string.Empty,
LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel],
string.Empty,
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
(int)Symbol.Important,
deferEvent,
async window =>
{
if (opened)
{
return;
}
title,
message,
string.Empty,
LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel],
string.Empty,
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
(int)Symbol.Important,
deferEvent,
async window =>
{
if (opened)
{
return;
}
opened = true;
opened = true;
_parent.SettingsWindow =
new SettingsWindow(_parent.VirtualFileSystem, _parent.ContentManager);
_parent.SettingsWindow = new SettingsWindow(_parent.VirtualFileSystem, _parent.ContentManager);
await _parent.SettingsWindow.ShowDialog(window);
await _parent.SettingsWindow.ShowDialog(window);
_parent.SettingsWindow = null;
_parent.SettingsWindow = null;
opened = false;
});
opened = false;
});
if (response == UserResult.Ok)
{
@@ -111,9 +110,7 @@ namespace Ryujinx.Ava.UI.Applet
}
catch (Exception ex)
{
await ContentDialogHelper.CreateErrorDialog(
LocaleManager.Instance.UpdateAndGetDynamicValue(
LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex));
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex));
dialogCloseEvent.Set();
}
@@ -137,9 +134,7 @@ namespace Ryujinx.Ava.UI.Applet
try
{
_parent.ViewModel.AppHost.NpadManager.BlockInputUpdates();
(UserResult result, string userInput) =
await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard],
args);
(UserResult result, string userInput) = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
if (result == UserResult.Ok)
{
@@ -151,9 +146,7 @@ namespace Ryujinx.Ava.UI.Applet
{
error = true;
await ContentDialogHelper.CreateErrorDialog(
LocaleManager.Instance.UpdateAndGetDynamicValue(
LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
}
finally
{
@@ -184,8 +177,7 @@ namespace Ryujinx.Ava.UI.Applet
args.InitialText = "Ryujinx";
args.StringLengthMin = 1;
args.StringLengthMax = 25;
(UserResult result, string userInput) =
await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.CabinetDialog], args);
(UserResult result, string userInput) = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.CabinetDialog], args);
if (result == UserResult.Ok)
{
inputText = userInput;
@@ -209,13 +201,11 @@ namespace Ryujinx.Ava.UI.Applet
Dispatcher.UIThread.InvokeAsync(async () =>
{
dialogCloseEvent.Set();
await ContentDialogHelper.CreateInfoDialog(
LocaleManager.Instance[LocaleKeys.CabinetScanDialog],
string.Empty,
LocaleManager.Instance[LocaleKeys.InputDialogOk],
string.Empty,
LocaleManager.Instance[LocaleKeys.CabinetTitle]
);
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.CabinetScanDialog],
string.Empty,
LocaleManager.Instance[LocaleKeys.InputDialogOk],
string.Empty,
LocaleManager.Instance[LocaleKeys.CabinetTitle]);
});
dialogCloseEvent.WaitOne();
}
@@ -227,8 +217,7 @@ namespace Ryujinx.Ava.UI.Applet
_parent.ViewModel.AppHost?.Stop();
}
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons,
(uint Module, uint Description)? errorCode = null)
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
{
ManualResetEvent dialogCloseEvent = new(false);
@@ -240,7 +229,9 @@ namespace Ryujinx.Ava.UI.Applet
{
ErrorAppletWindow msgDialog = new(_parent, buttons, message)
{
Title = title, WindowStartupLocation = WindowStartupLocation.CenterScreen, Width = 400
Title = title,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
Width = 400
};
object response = await msgDialog.Run();
@@ -258,9 +249,7 @@ namespace Ryujinx.Ava.UI.Applet
{
dialogCloseEvent.Set();
await ContentDialogHelper.CreateErrorDialog(
LocaleManager.Instance.UpdateAndGetDynamicValue(
LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex));
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex));
}
});
@@ -270,36 +259,38 @@ namespace Ryujinx.Ava.UI.Applet
}
public IDynamicTextInputHandler CreateDynamicTextInputHandler() => new AvaloniaDynamicTextInputHandler(_parent);
public UserProfile ShowPlayerSelectDialog()
{
UserId selected = UserId.Null;
byte[] defaultGuestImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/GuestUserImage.jpg");
UserProfile guest = new(new UserId("00000000000000000000000000000080"), "Guest", defaultGuestImage);
ManualResetEvent dialogCloseEvent = new(false);
Dispatcher.UIThread.InvokeAsync(async () =>
{
ObservableCollection<BaseModel> profiles = [];
NavigationDialogHost nav = new();
_parent.AccountManager.GetAllUsers()
.OrderBy(x => x.Name)
.ForEach(profile => profiles.Add(new Models.UserProfile(profile, nav)));
profiles.Add(new Models.UserProfile(guest, nav));
ProfileSelectorDialogViewModel viewModel = new()
UserSelectorDialogViewModel viewModel = new()
{
Profiles = profiles, SelectedUserId = _parent.AccountManager.LastOpenedUser.UserId
Profiles = profiles,
SelectedUserId = _parent.AccountManager.LastOpenedUser.UserId
};
(selected, _) = await ProfileSelectorDialog.ShowInputDialog(viewModel);
UserSelectorDialog content = new(viewModel);
(selected, _) = await UserSelectorDialog.ShowInputDialog(content);
dialogCloseEvent.Set();
});
dialogCloseEvent.WaitOne();
UserProfile profile = _parent.AccountManager.LastOpenedUser;
if (selected == guest.UserId)
{
@@ -320,7 +311,6 @@ namespace Ryujinx.Ava.UI.Applet
}
}
}
return profile;
}
}

View File

@@ -1,5 +1,5 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Applet.ProfileSelectorDialog"
x:Class="Ryujinx.Ava.UI.Applet.UserSelectorDialog"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -12,9 +12,9 @@
d:DesignWidth="800"
mc:Ignorable="d"
Focusable="True"
x:DataType="viewModels:ProfileSelectorDialogViewModel">
x:DataType="viewModels:UserSelectorDialogViewModel">
<Design.DataContext>
<viewModels:ProfileSelectorDialogViewModel />
<viewModels:UserSelectorDialogViewModel />
</Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">

View File

@@ -16,15 +16,15 @@ using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
namespace Ryujinx.Ava.UI.Applet
{
public partial class ProfileSelectorDialog : UserControl
public partial class UserSelectorDialog : UserControl, INotifyPropertyChanged
{
public ProfileSelectorDialogViewModel ViewModel { get; set; }
public UserSelectorDialogViewModel ViewModel { get; set; }
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
public UserSelectorDialog(UserSelectorDialogViewModel viewModel)
{
DataContext = ViewModel = viewModel;
InitializeComponent();
ViewModel = viewModel;
DataContext = ViewModel;
}
private void Grid_PointerEntered(object sender, PointerEventArgs e)
@@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Applet
if (ViewModel.Profiles[selectedIndex] is UserProfile userProfile)
{
ViewModel.SelectedUserId = userProfile.UserId;
Logger.Info?.Print(LogClass.UI, $"Selected: {userProfile.UserId}", "ProfileSelector");
Logger.Info?.Print(LogClass.UI, $"Selected user: {userProfile.UserId}");
ObservableCollection<BaseModel> newProfiles = [];
@@ -79,7 +79,7 @@ namespace Ryujinx.Ava.UI.Applet
}
}
public static async Task<(UserId Id, bool Result)> ShowInputDialog(ProfileSelectorDialogViewModel viewModel)
public static async Task<(UserId Id, bool Result)> ShowInputDialog(UserSelectorDialog content)
{
ContentDialog contentDialog = new()
{
@@ -87,25 +87,22 @@ namespace Ryujinx.Ava.UI.Applet
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
SecondaryButtonText = string.Empty,
CloseButtonText = LocaleManager.Instance[LocaleKeys.Cancel],
Content = new ProfileSelectorDialog(viewModel),
Content = content,
Padding = new Thickness(0)
};
UserId result = UserId.Null;
bool input = false;
contentDialog.Closed += Handler;
await ContentDialogHelper.ShowAsync(contentDialog);
return (result, input);
void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
{
if (eventArgs.Result == ContentDialogResult.Primary)
{
result = viewModel.SelectedUserId;
input = true;
if (contentDialog.Content is UserSelectorDialog view)
{
result = view.ViewModel.SelectedUserId;
input = true;
}
}
else
{
@@ -113,6 +110,12 @@ namespace Ryujinx.Ava.UI.Applet
input = false;
}
}
contentDialog.Closed += Handler;
await ContentDialogHelper.ShowAsync(contentDialog);
return (result, input);
}
}
}

View File

@@ -4,7 +4,6 @@
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:ext="using:Ryujinx.Ava.Common.Markup"
xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Controls.ApplicationDataView"
@@ -55,21 +54,9 @@
Tag="{Binding AppData.IdString}"
Text="{Binding AppData.LocalizedStatus}"
Foreground="{Binding AppData.PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
ToolTip.Tip="{Binding AppData.LocalizedStatusTooltip}"
TextAlignment="Start"
TextWrapping="Wrap">
<ToolTip.Tip>
<StackPanel Orientation="Vertical">
<TextBlock
Text="{Binding AppData.LocalizedStatusTooltip}" />
<Separator
Margin="0, 10, 0, 10"
IsVisible="{Binding AppData.HasCompatibilityLabels}" />
<TextBlock
IsVisible="{Binding AppData.HasCompatibilityLabels}"
Text="{Binding AppData.FormattedCompatibilityLabels}" />
</StackPanel>
</ToolTip.Tip>
</TextBlock>
TextWrapping="Wrap" />
<Button.Styles>
<Style Selector="Button">
<Setter Property="MinWidth"
@@ -98,55 +85,6 @@
</StackPanel>
</Grid>
<Separator Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
<StackPanel Orientation="Vertical" Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<ui:SymbolIcon Foreground="ForestGreen" Symbol="Checkmark" IsVisible="{Binding AppData.HasRichPresenceAsset}"/>
<TextBlock
Foreground="ForestGreen"
HorizontalAlignment="Stretch"
IsVisible="{Binding AppData.HasRichPresenceAsset}"
Text="{ext:Locale GameInfoRpcImage}"
TextAlignment="Start"
TextWrapping="Wrap" >
</TextBlock>
<ui:SymbolIcon Foreground="Red" Symbol="Cancel" IsVisible="{Binding !AppData.HasRichPresenceAsset}"/>
<TextBlock
Foreground="Red"
HorizontalAlignment="Stretch"
IsVisible="{Binding !AppData.HasRichPresenceAsset}"
Text="{ext:Locale GameInfoRpcImage}"
TextAlignment="Start"
TextWrapping="Wrap" >
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5" ToolTip.Tip="{Binding DynamicRichPresenceDescription}">
<ui:SymbolIcon
Foreground="ForestGreen"
Symbol="Checkmark"
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
<TextBlock
Foreground="ForestGreen"
HorizontalAlignment="Stretch"
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"
Text="{ext:Locale GameInfoRpcDynamic}"
TextAlignment="Start"
TextWrapping="Wrap">
</TextBlock>
<ui:SymbolIcon
Foreground="Red"
Symbol="Cancel"
IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
<TextBlock
Foreground="Red"
HorizontalAlignment="Stretch"
IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"
Text="{ext:Locale GameInfoRpcDynamic}"
TextAlignment="Start"
TextWrapping="Wrap" >
</TextBlock>
</StackPanel>
</StackPanel>
<Separator Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
<TextBlock
HorizontalAlignment="Stretch"
IsVisible="{Binding AppData.HasLdnGames}"
@@ -154,35 +92,22 @@
TextAlignment="Start"
TextWrapping="Wrap" />
<Separator IsVisible="{Binding AppData.HasLdnGames}" Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
<StackPanel Orientation="Vertical" Spacing="5">
<Grid
ColumnDefinitions="Auto,*,Auto">
<TextBlock
Grid.Column="0"
Text="{ext:Locale GameListHeaderLastPlayed}"
VerticalAlignment="Top"
TextAlignment="Start"
TextWrapping="NoWrap" />
<TextBlock
Grid.Column="2"
Text="{Binding AppData.LastPlayedString}"
TextAlignment="End"
TextWrapping="Wrap" />
</Grid>
<Grid
ColumnDefinitions="Auto,*,Auto"
IsVisible="{Binding AppData.HasPlayedPreviously}">
<TextBlock
Grid.Column="0"
Text="{ext:Locale GameListHeaderTimePlayed}"
VerticalAlignment="Top"
TextAlignment="Start"
TextWrapping="NoWrap" />
<TextBlock Grid.Column="2"
Text="{Binding AppData.TimePlayedString}"
TextAlignment="End"
TextWrapping="Wrap" />
</Grid>
<StackPanel
HorizontalAlignment="Left"
VerticalAlignment="Top"
Orientation="Vertical"
Spacing="5">
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding FormattedLastPlayed}"
TextAlignment="Start"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding FormattedPlayTime}"
IsVisible="{Binding AppData.HasPlayedPreviously}"
TextAlignment="Start"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
</StackPanel>

View File

@@ -1,9 +1,12 @@
using Avalonia.Controls;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Styling;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;

View File

@@ -93,19 +93,8 @@
IsVisible="{Binding HasPlayabilityInfo}"
Background="{DynamicResource AppListBackgroundColor}"
Margin="-1, 0, 0, 0"
Padding="0">
<ToolTip.Tip>
<StackPanel Orientation="Vertical">
<TextBlock
Text="{Binding LocalizedStatusTooltip}" />
<Separator
Margin="0, 10, 0, 10"
IsVisible="{Binding HasCompatibilityLabels}" />
<TextBlock
IsVisible="{Binding HasCompatibilityLabels}"
Text="{Binding FormattedCompatibilityLabels}" />
</StackPanel>
</ToolTip.Tip>
Padding="0"
ToolTip.Tip="{Binding LocalizedStatusTooltip}">
<TextBlock
Margin="1.5"
Tag="{Binding IdString}"

View File

@@ -7,6 +7,7 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Compat;
using System;
using System.Globalization;
using System.Linq;
namespace Ryujinx.Ava.UI.Controls

View File

@@ -18,7 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers
LocaleKeys.CompatibilityListNothing or
LocaleKeys.CompatibilityListBoots or
LocaleKeys.CompatibilityListMenus => Brushes.Red,
LocaleKeys.CompatibilityListIngame => Brushes.DarkOrange,
LocaleKeys.CompatibilityListIngame => Brushes.Yellow,
_ => Brushes.ForestGreen
};

View File

@@ -1,7 +1,6 @@
using Avalonia.Logging;
using Avalonia.Utilities;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Logging;
using System;
using System.Text;
@@ -15,19 +14,13 @@ namespace Ryujinx.Ava.UI.Helpers
internal class LoggerAdapter : ILogSink
{
private static bool _avaloniaLogsEnabled = ConfigurationState.Instance.Logger.EnableAvaloniaLog;
public static void Register()
{
AvaLogger.Sink = new LoggerAdapter();
ConfigurationState.Instance.Logger.EnableAvaloniaLog.Event
+= (_, e) => _avaloniaLogsEnabled = e.NewValue;
}
private static RyuLogger.Log? GetLog(AvaLogLevel level, string area)
{
if (!_avaloniaLogsEnabled) return null;
return level switch
{
AvaLogLevel.Verbose => RyuLogger.Debug,

View File

@@ -2,7 +2,6 @@ using Avalonia.Media.Imaging;
using Avalonia.Styling;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using Gommon;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.Configuration;
@@ -25,35 +24,30 @@ namespace Ryujinx.Ava.UI.ViewModels
Version = RyujinxApp.FullAppName + "\n" + Program.Version;
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value);
RyujinxApp.ThemeChanged += Ryujinx_ThemeChanged;
ThemeManager.ThemeChanged += ThemeManager_ThemeChanged;
}
private void Ryujinx_ThemeChanged()
private void ThemeManager_ThemeChanged()
{
Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value));
}
private const string LogoPathFormat = "resm:Ryujinx.Assets.UIImages.Logo_{0}_{1}.png?assembly=Ryujinx";
private void UpdateLogoTheme(string theme)
{
bool isDarkTheme = theme == "Dark" || (theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark);
string themeName = isDarkTheme ? "Dark" : "Light";
GithubLogo = LoadBitmap(LogoPathFormat.Format("GitHub", themeName));
DiscordLogo = LoadBitmap(LogoPathFormat.Format("Discord", themeName));
string basePath = "resm:Ryujinx.Assets.UIImages.";
string themeSuffix = isDarkTheme ? "Dark.png" : "Light.png";
GithubLogo = LoadBitmap($"{basePath}Logo_GitHub_{themeSuffix}?assembly=Ryujinx");
DiscordLogo = LoadBitmap($"{basePath}Logo_Discord_{themeSuffix}?assembly=Ryujinx");
}
private static Bitmap LoadBitmap(string uri) => new(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
public void Dispose()
{
RyujinxApp.ThemeChanged -= Ryujinx_ThemeChanged;
GithubLogo.Dispose();
DiscordLogo.Dispose();
ThemeManager.ThemeChanged -= ThemeManager_ThemeChanged;
GC.SuppressFinalize(this);
}
}

View File

@@ -264,7 +264,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}");
// Neither local or remote files are valid JSON, close window.
await ShowInfoDialog();
ShowInfoDialog();
Close();
}
else if (!remoteIsValid)
@@ -273,7 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
// Only the local file is valid, the local one should be used
// but the user should be warned.
await ShowInfoDialog();
ShowInfoDialog();
}
}
@@ -525,7 +525,7 @@ namespace Ryujinx.Ava.UI.ViewModels
AmiiboImage = bitmap;
}
private static async Task ShowInfoDialog()
private static async void ShowInfoDialog()
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],

View File

@@ -1,7 +1,6 @@
using Gommon;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.PlayReport;
namespace Ryujinx.Ava.UI.ViewModels
{
@@ -11,14 +10,12 @@ namespace Ryujinx.Ava.UI.ViewModels
public ApplicationDataViewModel(ApplicationData appData) => AppData = appData;
public string DynamicRichPresenceDescription =>
AppData.HasDynamicRichPresenceSupport
? AppData.RichPresenceSpec.Value.Description
: GameSpec.DefaultDescription;
public string FormattedVersion => LocaleManager.Instance[LocaleKeys.GameListHeaderVersion].Format(AppData.Version);
public string FormattedDeveloper => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper].Format(AppData.Developer);
public string FormattedFileExtension => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension].Format(AppData.FileExtension);
public string FormattedLastPlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderLastPlayed].Format(AppData.LastPlayedString);
public string FormattedPlayTime => LocaleManager.Instance[LocaleKeys.GameListHeaderTimePlayed].Format(AppData.TimePlayedString);
public string FormattedFileSize => LocaleManager.Instance[LocaleKeys.GameListHeaderFileSize].Format(AppData.FileSizeString);
public string FormattedLdnInfo =>

View File

@@ -7,7 +7,6 @@ using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DynamicData;
using DynamicData.Binding;
using FluentAvalonia.UI.Controls;
@@ -105,13 +104,6 @@ namespace Ryujinx.Ava.UI.ViewModels
[ObservableProperty] private bool _isSubMenuOpen;
[ObservableProperty] private ApplicationContextMenu _listAppContextMenu;
[ObservableProperty] private ApplicationContextMenu _gridAppContextMenu;
[ObservableProperty] private bool _updateAvailable;
public static AsyncRelayCommand UpdateCommand { get; } = Commands.Create(async () =>
{
if (Updater.CanUpdate(true))
await Updater.BeginUpdateAsync(true);
});
private bool _showLoadProgress;
private bool _isGameRunning;
@@ -1155,10 +1147,10 @@ namespace Ryujinx.Ava.UI.ViewModels
List<string> dirs = result.Select(it => it.Path.LocalPath).ToList();
int numAdded = onDirsSelected(dirs, out int numRemoved);
string msg = string.Join("\n",
string msg = String.Join("\r\n", new string[] {
string.Format(LocaleManager.Instance[localeMessageRemovedKey], numRemoved),
string.Format(LocaleManager.Instance[localeMessageAddedKey], numAdded)
);
});
await Dispatcher.UIThread.InvokeAsync(async () =>
{
@@ -1347,25 +1339,6 @@ namespace Ryujinx.Ava.UI.ViewModels
OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
}
public void OpenScreenshotsFolder()
{
string screenshotsDir = Path.Combine(AppDataManager.BaseDirPath, "screenshots");
try
{
if (!Directory.Exists(screenshotsDir))
Directory.CreateDirectory(screenshotsDir);
}
catch (Exception ex)
{
Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {screenshotsDir}. Error : {ex.GetType().Name}", "Screenshot");
return;
}
OpenHelper.OpenFolder(screenshotsDir);
}
public void OpenLogsFolder()
{
string logPath = AppDataManager.GetOrCreateLogsDir();

View File

@@ -13,7 +13,6 @@ using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Ava.Utilities.Configuration.UI;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.GraphicsDriver;
@@ -49,7 +48,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _graphicsBackendMultithreadingIndex;
private float _volume;
[ObservableProperty] private bool _isVulkanAvailable = true;
[ObservableProperty] private bool _gameListNeedsRefresh;
[ObservableProperty] private bool _gameDirectoryChanged;
[ObservableProperty] private bool _autoloadDirectoryChanged;
private readonly List<string> _gpuIds = [];
private int _graphicsBackendIndex;
private int _scalingFilter;
@@ -121,14 +121,9 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool RememberWindowState { get; set; }
public bool ShowTitleBar { get; set; }
public int HideCursor { get; set; }
public int UpdateCheckerType { get; set; }
public bool EnableDockedMode { get; set; }
public bool EnableKeyboard { get; set; }
public bool EnableMouse { get; set; }
public bool DisableInputWhenOutOfFocus { get; set; }
public int FocusLostActionType { get; set; }
public VSyncMode VSyncMode
{
get => _vSyncMode;
@@ -209,7 +204,6 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool EnableTrace { get; set; }
public bool EnableGuest { get; set; }
public bool EnableFsAccessLog { get; set; }
public bool EnableAvaloniaLog { get; set; }
public bool EnableDebug { get; set; }
public bool IsOpenAlEnabled { get; set; }
public bool IsSoundIoEnabled { get; set; }
@@ -481,8 +475,6 @@ namespace Ryujinx.Ava.UI.ViewModels
RememberWindowState = config.RememberWindowState;
ShowTitleBar = config.ShowTitleBar;
HideCursor = (int)config.HideCursor.Value;
UpdateCheckerType = (int)config.UpdateCheckerType.Value;
FocusLostActionType = (int)config.FocusLostActionType.Value;
GameDirectories.Clear();
GameDirectories.AddRange(config.UI.GameDirs.Value);
@@ -502,7 +494,6 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableDockedMode = config.System.EnableDockedMode;
EnableKeyboard = config.Hid.EnableKeyboard;
EnableMouse = config.Hid.EnableMouse;
DisableInputWhenOutOfFocus = config.Hid.DisableInputWhenOutOfFocus;
// Keyboard Hotkeys
KeyboardHotkey = new HotkeyConfig(config.Hid.Hotkeys.Value);
@@ -526,7 +517,7 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
DramSize = config.System.DramSize;
IgnoreMissingServices = config.System.IgnoreMissingServices;
IgnoreApplet = config.System.IgnoreControllerApplet;
IgnoreApplet = config.System.IgnoreApplet;
// CPU
EnablePptc = config.System.EnablePtc;
@@ -569,7 +560,6 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableGuest = config.Logger.EnableGuest;
EnableDebug = config.Logger.EnableDebug;
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
EnableAvaloniaLog = config.Logger.EnableAvaloniaLog;
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
@@ -590,10 +580,16 @@ namespace Ryujinx.Ava.UI.ViewModels
config.RememberWindowState.Value = RememberWindowState;
config.ShowTitleBar.Value = ShowTitleBar;
config.HideCursor.Value = (HideCursorMode)HideCursor;
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
config.UI.GameDirs.Value = [..GameDirectories];
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
if (GameDirectoryChanged)
{
config.UI.GameDirs.Value = [..GameDirectories];
}
if (AutoloadDirectoryChanged)
{
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
}
config.UI.BaseStyle.Value = BaseStyleIndex switch
{
@@ -607,18 +603,14 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.EnableDockedMode.Value = EnableDockedMode;
config.Hid.EnableKeyboard.Value = EnableKeyboard;
config.Hid.EnableMouse.Value = EnableMouse;
config.Hid.DisableInputWhenOutOfFocus.Value = DisableInputWhenOutOfFocus;
// Keyboard Hotkeys
config.Hid.Hotkeys.Value = KeyboardHotkey.GetConfig();
// System
config.System.Region.Value = (Region)Region;
if (config.System.Language.Value != (Language)Language)
GameListNeedsRefresh = true;
config.System.Language.Value = (Language)Language;
if (_validTzRegions.Contains(TimeZone))
{
config.System.TimeZone.Value = TimeZone;
@@ -629,7 +621,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
config.System.DramSize.Value = DramSize;
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
config.System.IgnoreControllerApplet.Value = IgnoreApplet;
config.System.IgnoreApplet.Value = IgnoreApplet;
// CPU
config.System.EnablePtc.Value = EnablePptc;
@@ -687,7 +679,6 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Logger.EnableGuest.Value = EnableGuest;
config.Logger.EnableDebug.Value = EnableDebug;
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
config.Logger.EnableAvaloniaLog.Value = EnableAvaloniaLog;
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
@@ -709,7 +700,8 @@ namespace Ryujinx.Ava.UI.ViewModels
SaveSettingsEvent?.Invoke();
GameListNeedsRefresh = false;
GameDirectoryChanged = false;
AutoloadDirectoryChanged = false;
}
private static void RevertIfNotSaved()

View File

@@ -4,7 +4,7 @@ using System.Collections.ObjectModel;
namespace Ryujinx.Ava.UI.ViewModels
{
public partial class ProfileSelectorDialogViewModel : BaseModel
public partial class UserSelectorDialogViewModel : BaseModel
{
[ObservableProperty] private UserId _selectedUserId;

View File

@@ -66,10 +66,6 @@
Command="{Binding OpenRyujinxFolder}"
Header="{ext:Locale MenuBarFileOpenEmuFolder}"
ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" />
<MenuItem
Command="{Binding OpenScreenshotsFolder}"
Header="{ext:Locale MenuBarFileOpenScreenshotsFolder}"
ToolTip.Tip="{ext:Locale OpenScreenshotFolderTooltip}"/>
<MenuItem
Command="{Binding OpenLogsFolder}"
Header="{ext:Locale MenuBarFileOpenLogsFolder}"

View File

@@ -51,8 +51,12 @@ namespace Ryujinx.Ava.UI.Views.Main
XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show);
AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show);
CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityList.Show());
UpdateMenuItem.Command = MainWindowViewModel.UpdateCommand;
UpdateMenuItem.Command = Commands.Create(async () =>
{
if (Updater.CanUpdate(true))
await Updater.BeginUpdateAsync(true);
});
FaqMenuItem.Command =
SetupGuideMenuItem.Command =

View File

@@ -23,7 +23,7 @@
Background="{DynamicResource ThemeContentBackgroundColor}"
DockPanel.Dock="Bottom"
IsVisible="{Binding ShowMenuAndStatusBar}"
ColumnDefinitions="Auto,Auto,*,Auto,Auto,Auto">
ColumnDefinitions="Auto,Auto,*,Auto,Auto">
<StackPanel
Grid.Column="0"
Margin="5"
@@ -280,30 +280,8 @@
Text="{Binding GpuNameText}"
TextAlignment="Start" />
</StackPanel>
<StackPanel
Grid.Column="4"
Margin="0,0,5,0"
Orientation="Horizontal">
<StackPanel.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="EnableNonGameRunningControls" />
<Binding Path="UpdateAvailable" />
</MultiBinding>
</StackPanel.IsVisible>
<Button Margin="0, 0, 5, -2"
Command="{Binding UpdateCommand}"
Background="{DynamicResource SystemAccentColor}">
<TextBlock
Margin="-5"
Foreground="{StaticResource SystemColorButtonTextColor}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{ext:Locale UpdaterBackgroundStatusBarButtonText}" />
</Button>
<controls:MiniVerticalSeparator Margin="5,0,0,0"/>
</StackPanel>
<StackPanel
Grid.Column="5"
Grid.Column="4"
Margin="0,0,5,0"
VerticalAlignment="Center"
IsVisible="{Binding ShowFirmwareStatus}"

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