Compare commits
126 Commits
Canary-1.2
...
fdba73bb58
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdba73bb58 | ||
|
|
52b0b45d34 | ||
|
|
12f0dbcc70 | ||
|
|
719be560ec | ||
|
|
18238736be | ||
|
|
5d9e4ad7a4 | ||
|
|
adba775f0c | ||
|
|
2ffaeb2803 | ||
|
|
b16b844760 | ||
|
|
bc07bc482d | ||
|
|
61975ca44d | ||
|
|
66054dd225 | ||
|
|
b1f61e5143 | ||
|
|
0d7d0e8092 | ||
|
|
aa2178dbe5 | ||
|
|
f92d09711b | ||
|
|
45ee8cd0e8 | ||
|
|
395bbd144a | ||
|
|
744d813b87 | ||
|
|
7d59ada798 | ||
|
|
a4b5304935 | ||
|
|
0965ee905d | ||
|
|
855161b23b | ||
|
|
0282b216fd | ||
|
|
6b55d158b7 | ||
|
|
91f73a4891 | ||
|
|
883d4d863a | ||
|
|
ca5de909a1 | ||
|
|
5172567b08 | ||
|
|
6fe4cee7c0 | ||
|
|
8623452abc | ||
|
|
17e8ae1d9a | ||
|
|
7591b07fce | ||
|
|
89b4389ed2 | ||
|
|
d9ee729199 | ||
|
|
f2ade5f1f8 | ||
|
|
ba0cd13cff | ||
|
|
501b199e24 | ||
|
|
8aecccadb8 | ||
|
|
e23d610f49 | ||
|
|
f6822f7358 | ||
|
|
d3f84a1305 | ||
|
|
06d34a5992 | ||
|
|
e8e1dc6619 | ||
|
|
c5603d4c36 | ||
|
|
05b56730d6 | ||
|
|
43f7b000ca | ||
|
|
ad89cf39b6 | ||
|
|
96c33a0b92 | ||
|
|
e0db55df46 | ||
|
|
30fef8e96e | ||
|
|
9cb5f5689b | ||
|
|
a205ec374b | ||
|
|
aab9b58542 | ||
|
|
daa648dc40 | ||
|
|
1024aa8757 | ||
|
|
13388e972a | ||
|
|
1eb78872d8 | ||
|
|
fe9fe2a10f | ||
|
|
6ab899f621 | ||
|
|
faacec9801 | ||
|
|
55fdb3f6b2 | ||
|
|
1129ab0e8c | ||
|
|
b6b391b2cf | ||
|
|
f3cf03495d | ||
|
|
7bce8206d5 | ||
|
|
efa0cc7554 | ||
|
|
1c0813d09d | ||
|
|
8bec09d7ff | ||
|
|
e4b4e94b56 | ||
|
|
764c9e9d4e | ||
|
|
05e991db87 | ||
|
|
2cd876b1cb | ||
|
|
93a298523f | ||
|
|
253cbb2810 | ||
|
|
9c226dcc7a | ||
|
|
30a534edcd | ||
|
|
1d88771d1b | ||
|
|
4e8157688e | ||
|
|
5085af0050 | ||
|
|
2c8edaf89e | ||
|
|
965fb9dd5f | ||
|
|
e2a5e69f4c | ||
|
|
7f27b791f8 | ||
|
|
aa8ba8b503 | ||
|
|
a4211fec33 | ||
|
|
54b233dd78 | ||
|
|
3ca8618f5f | ||
|
|
d1da937fce | ||
|
|
4a8f98126f | ||
|
|
e55629a908 | ||
|
|
c638a7daf8 | ||
|
|
5e5e180fea | ||
|
|
131fe71205 | ||
|
|
6af388c623 | ||
|
|
45cec4e7cf | ||
|
|
479b38f035 | ||
|
|
3ecc7819cc | ||
|
|
4b1d94ccd8 | ||
|
|
4ae9f1c0d2 | ||
|
|
717851985e | ||
|
|
bd08a111a8 | ||
|
|
1972a47f39 | ||
|
|
222ceb818b | ||
|
|
b0fcc5bee1 | ||
|
|
820e8f7375 | ||
|
|
e8a7d5b0b7 | ||
|
|
fafb99c702 | ||
|
|
df9e6e4812 | ||
|
|
566f3d079a | ||
|
|
d7707d4176 | ||
|
|
7a9b62884a | ||
|
|
de9faf183a | ||
|
|
0bf7c5dfa2 | ||
|
|
11bc32d98e | ||
|
|
063430ea16 | ||
|
|
65f08caaa3 | ||
|
|
f225b18c05 | ||
|
|
d8549f687b | ||
|
|
5ab50680b4 | ||
|
|
a0edc5c2b0 | ||
|
|
158ea7b4d6 | ||
|
|
8bc3de8303 | ||
|
|
c812106611 | ||
|
|
11e4d8f970 | ||
|
|
774edb7b29 |
4
.github/workflows/canary.yml
vendored
4
.github/workflows/canary.yml
vendored
@@ -29,7 +29,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
tag:
|
tag:
|
||||||
name: Create tag
|
name: Create tag
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
@@ -202,7 +202,7 @@ jobs:
|
|||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
name: Release MacOS universal
|
name: Release MacOS universal
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -18,7 +18,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
tag:
|
tag:
|
||||||
name: Create tag
|
name: Create tag
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
@@ -183,7 +183,7 @@ jobs:
|
|||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
name: Release MacOS universal
|
name: Release MacOS universal
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
@@ -39,12 +39,12 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
Click below to join the Discord:
|
Click below to join the Discord:
|
||||||
<br>
|
<br>
|
||||||
<a href="https://discord.gg/dHPrkBkkyA">
|
<a href="https://discord.gg/PEuzjrFXUA">
|
||||||
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
|
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<img src="https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/docs/shell.png">
|
<img src="https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/docs/shell.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
@@ -332,6 +332,7 @@
|
|||||||
0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21
|
0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21
|
||||||
01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26
|
01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26
|
||||||
01004DA012976000,"Area 86",,playable,2020-12-16 16:45:52
|
01004DA012976000,"Area 86",,playable,2020-12-16 16:45:52
|
||||||
|
01008d8006a6a000,"Arena of Valor",crash,boots,2025-02-03 22:19:34
|
||||||
0100691013C46000,"ARIA CHRONICLE",,playable,2022-11-16 13:50:55
|
0100691013C46000,"ARIA CHRONICLE",,playable,2022-11-16 13:50:55
|
||||||
0100D4A00B284000,"ARK: Survival Evolved",gpu;nvdec;online-broken;UE4;ldn-untested,ingame,2024-04-16 00:53:56
|
0100D4A00B284000,"ARK: Survival Evolved",gpu;nvdec;online-broken;UE4;ldn-untested,ingame,2024-04-16 00:53:56
|
||||||
0100C56012C96000,"Arkanoid vs. Space Invaders",services,ingame,2021-01-21 12:50:30
|
0100C56012C96000,"Arkanoid vs. Space Invaders",services,ingame,2021-01-21 12:50:30
|
||||||
@@ -426,6 +427,7 @@
|
|||||||
0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07
|
0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07
|
||||||
0100CD801CE5E000,"Balatro",,ingame,2024-04-21 02:01:53
|
0100CD801CE5E000,"Balatro",,ingame,2024-04-21 02:01:53
|
||||||
010010A00DA48000,"Baldur's Gate and Baldur's Gate II: Enhanced Editions",32-bit,playable,2022-09-12 23:52:15
|
010010A00DA48000,"Baldur's Gate and Baldur's Gate II: Enhanced Editions",32-bit,playable,2022-09-12 23:52:15
|
||||||
|
0100fd1014726000,"Baldur's Gate: Dark Alliance",ldn-untested,ingame,2025-02-03 22:21:00
|
||||||
0100BC400FB64000,"Balthazar's Dream",,playable,2022-09-13 00:13:22
|
0100BC400FB64000,"Balthazar's Dream",,playable,2022-09-13 00:13:22
|
||||||
01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39
|
01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39
|
||||||
010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37
|
010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37
|
||||||
@@ -528,6 +530,7 @@
|
|||||||
01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43
|
01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43
|
||||||
0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26
|
0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26
|
||||||
0100CFA00CC74000,"Blades of Time",deadlock;online,boots,2022-07-17 19:19:58
|
0100CFA00CC74000,"Blades of Time",deadlock;online,boots,2022-07-17 19:19:58
|
||||||
|
01003d700dd8a000,"Blades",,boots,2025-02-03 22:22:00
|
||||||
01006CC01182C000,"Blair Witch",nvdec;UE4,playable,2022-10-01 14:06:16
|
01006CC01182C000,"Blair Witch",nvdec;UE4,playable,2022-10-01 14:06:16
|
||||||
010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13
|
010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13
|
||||||
0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31
|
0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31
|
||||||
@@ -955,7 +958,7 @@
|
|||||||
010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28
|
010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28
|
||||||
01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12
|
01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12
|
||||||
010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24
|
010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24
|
||||||
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,menus,2023-08-13 17:20:03
|
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,ingame,2025-02-03 22:12:30
|
||||||
01001770115C8000,"Dodo Peak",nvdec;UE4,playable,2022-10-04 16:13:05
|
01001770115C8000,"Dodo Peak",nvdec;UE4,playable,2022-10-04 16:13:05
|
||||||
010077B0100DA000,"Dogurai",,playable,2020-10-04 02:40:16
|
010077B0100DA000,"Dogurai",,playable,2020-10-04 02:40:16
|
||||||
010048100D51A000,"Dokapon Up! Mugen no Roulette",gpu;Needs Update,menus,2022-12-08 19:39:10
|
010048100D51A000,"Dokapon Up! Mugen no Roulette",gpu;Needs Update,menus,2022-12-08 19:39:10
|
||||||
@@ -966,6 +969,7 @@
|
|||||||
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34
|
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34
|
||||||
010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
|
010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
|
||||||
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10
|
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10
|
||||||
|
01009D901BC56000,"Donkey Kong Country™: Returns HD",gpu,ingame,2025-02-16 13:44:12
|
||||||
0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
|
0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
|
||||||
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
|
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
|
||||||
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
|
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
|
||||||
@@ -1155,7 +1159,7 @@
|
|||||||
010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22
|
010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22
|
||||||
0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47
|
0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47
|
||||||
0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39
|
0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39
|
||||||
01000EA014150000,"FINAL FANTASY",crash,nothing,2024-09-05 20:55:30
|
01000EA014150000,"FINAL FANTASY",,playable,2025-02-16 21:27:30
|
||||||
01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04
|
01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04
|
||||||
01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00
|
01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00
|
||||||
0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55
|
0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55
|
||||||
@@ -1246,7 +1250,7 @@
|
|||||||
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
|
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
|
||||||
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
|
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
|
||||||
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
|
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
|
||||||
0100E1F013674000,"FUSER™",nvdec;UE4,playable,2022-10-17 20:58:32
|
0100E1F013674000,"FUSER™",nvdec;UE4;slow;gpu,ingame,2025-02-12 16:03:00
|
||||||
0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02
|
0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02
|
||||||
01003C300B274000,"Futari de! Nyanko Daisensou",,playable,2024-01-05 22:26:52
|
01003C300B274000,"Futari de! Nyanko Daisensou",,playable,2024-01-05 22:26:52
|
||||||
010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53
|
010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53
|
||||||
@@ -1654,7 +1658,7 @@
|
|||||||
0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03
|
0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03
|
||||||
0100A7700B46C000,"Legendary Fishing",online,playable,2021-04-14 15:08:46
|
0100A7700B46C000,"Legendary Fishing",online,playable,2021-04-14 15:08:46
|
||||||
0100739018020000,"LEGO® 2K Drive",gpu;ldn-works,ingame,2024-04-09 02:05:12
|
0100739018020000,"LEGO® 2K Drive",gpu;ldn-works,ingame,2024-04-09 02:05:12
|
||||||
01003A30012C0000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
|
010085500130a000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
|
||||||
010070D009FEC000,"LEGO® DC Super-Villains",,playable,2021-05-27 18:10:37
|
010070D009FEC000,"LEGO® DC Super-Villains",,playable,2021-05-27 18:10:37
|
||||||
010052A00B5D2000,"LEGO® Harry Potter™ Collection",crash,ingame,2024-01-31 10:28:07
|
010052A00B5D2000,"LEGO® Harry Potter™ Collection",crash,ingame,2024-01-31 10:28:07
|
||||||
010073C01AF34000,"LEGO® Horizon Adventures™",vulkan-backend-bug;opengl-backend-bug;UE4,ingame,2025-01-07 04:24:56
|
010073C01AF34000,"LEGO® Horizon Adventures™",vulkan-backend-bug;opengl-backend-bug;UE4,ingame,2025-01-07 04:24:56
|
||||||
@@ -1913,6 +1917,7 @@
|
|||||||
010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17
|
010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17
|
||||||
0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04
|
0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04
|
||||||
0100211005E94000,"Mulaka",,playable,2021-01-28 18:07:20
|
0100211005E94000,"Mulaka",,playable,2021-01-28 18:07:20
|
||||||
|
01008e2013fb4000,"Multi Quiz",ldn-untested,ingame,2025-02-03 22:26:00
|
||||||
010038B00B9AE000,"Mummy Pinball",,playable,2022-08-05 16:08:11
|
010038B00B9AE000,"Mummy Pinball",,playable,2022-08-05 16:08:11
|
||||||
01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29
|
01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29
|
||||||
010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08
|
010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08
|
||||||
@@ -2028,6 +2033,7 @@
|
|||||||
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
|
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
|
||||||
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
|
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
|
||||||
0100C9A00ECE6000,"Nintendo 64™ – Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
|
0100C9A00ECE6000,"Nintendo 64™ – Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
|
||||||
|
0100e0601c632000,"Nintendo 64™ – Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00
|
||||||
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
|
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
|
||||||
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
|
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
|
||||||
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
|
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
|
||||||
@@ -2058,7 +2064,7 @@
|
|||||||
010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07
|
010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07
|
||||||
010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23
|
010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23
|
||||||
010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22
|
010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22
|
||||||
0000000000000000,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
|
,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
|
||||||
,"nxquake2",services;crash;homebrew,nothing,2022-08-04 23:14:04
|
,"nxquake2",services;crash;homebrew,nothing,2022-08-04 23:14:04
|
||||||
010049F00EC30000,"Nyan Cat: Lost in Space",online,playable,2021-06-12 13:22:03
|
010049F00EC30000,"Nyan Cat: Lost in Space",online,playable,2021-06-12 13:22:03
|
||||||
01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14
|
01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14
|
||||||
@@ -2466,7 +2472,7 @@
|
|||||||
0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38
|
0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38
|
||||||
0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01
|
0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01
|
||||||
01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18
|
01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18
|
||||||
0000000000000000,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
|
,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
|
||||||
010009B00D33C000,"Rugby Challenge 4",slow;online-broken;UE4,playable,2022-10-06 12:45:53
|
010009B00D33C000,"Rugby Challenge 4",slow;online-broken;UE4,playable,2022-10-06 12:45:53
|
||||||
01006EC00F2CC000,"RUINER",UE4,playable,2022-10-03 14:11:33
|
01006EC00F2CC000,"RUINER",UE4,playable,2022-10-03 14:11:33
|
||||||
010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28
|
010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28
|
||||||
@@ -2475,6 +2481,7 @@
|
|||||||
010081C0191D8000,"Rune Factory 3 Special",,playable,2023-10-15 08:32:49
|
010081C0191D8000,"Rune Factory 3 Special",,playable,2023-10-15 08:32:49
|
||||||
010051D00E3A4000,"Rune Factory 4 Special",32-bit;crash;nvdec,ingame,2023-05-06 08:49:17
|
010051D00E3A4000,"Rune Factory 4 Special",32-bit;crash;nvdec,ingame,2023-05-06 08:49:17
|
||||||
010014D01216E000,"Rune Factory 5 (JP)",gpu,ingame,2021-06-01 12:00:36
|
010014D01216E000,"Rune Factory 5 (JP)",gpu,ingame,2021-06-01 12:00:36
|
||||||
|
010071E0145F8000,"Rustler",,playable,2025-02-10 20:17:12
|
||||||
0100E21013908000,"RWBY: Grimm Eclipse - Definitive Edition",online-broken,playable,2022-11-03 10:44:01
|
0100E21013908000,"RWBY: Grimm Eclipse - Definitive Edition",online-broken,playable,2022-11-03 10:44:01
|
||||||
010012C0060F0000,"RXN -Raijin-",nvdec,playable,2021-01-10 16:05:43
|
010012C0060F0000,"RXN -Raijin-",nvdec,playable,2021-01-10 16:05:43
|
||||||
0100B8B012ECA000,"S.N.I.P.E.R. - Hunter Scope",,playable,2021-04-19 15:58:09
|
0100B8B012ECA000,"S.N.I.P.E.R. - Hunter Scope",,playable,2021-04-19 15:58:09
|
||||||
@@ -2532,7 +2539,7 @@
|
|||||||
0100C3E00B700000,"SEGA AGES Space Harrier",,playable,2021-01-11 12:57:40
|
0100C3E00B700000,"SEGA AGES Space Harrier",,playable,2021-01-11 12:57:40
|
||||||
010054400D2E6000,"SEGA AGES Virtua Racing",online-broken,playable,2023-01-29 17:08:39
|
010054400D2E6000,"SEGA AGES Virtua Racing",online-broken,playable,2023-01-29 17:08:39
|
||||||
01001E700AC60000,"SEGA AGES Wonder Boy: Monster Land",online,playable,2021-05-05 16:28:25
|
01001E700AC60000,"SEGA AGES Wonder Boy: Monster Land",online,playable,2021-05-05 16:28:25
|
||||||
0100B3C014BDA000,"SEGA Genesis™ – Nintendo Switch Online",crash;regression,nothing,2022-04-11 07:27:21
|
0100B3C014BDA000,"SEGA Genesis™ – Nintendo Switch Online",crash;regression,ingame,2025-02-03 22:13:30
|
||||||
0100F7300B24E000,"SEGA Mega Drive Classics",online,playable,2021-01-05 11:08:00
|
0100F7300B24E000,"SEGA Mega Drive Classics",online,playable,2021-01-05 11:08:00
|
||||||
01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31
|
01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31
|
||||||
0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10
|
0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10
|
||||||
@@ -2668,10 +2675,10 @@
|
|||||||
01004F401BEBE000,"Song of Nunu: A League of Legends Story",,ingame,2024-07-12 18:53:44
|
01004F401BEBE000,"Song of Nunu: A League of Legends Story",,ingame,2024-07-12 18:53:44
|
||||||
0100E5400BF94000,"Songbird Symphony",,playable,2021-02-27 02:44:04
|
0100E5400BF94000,"Songbird Symphony",,playable,2021-02-27 02:44:04
|
||||||
010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02
|
010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02
|
||||||
0000000000000000,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
|
,"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
|
,"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
|
,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
|
||||||
0000000000000000,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
|
,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
|
||||||
010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26
|
010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26
|
||||||
01001270012B6000,"SONIC FORCES™",,playable,2024-07-28 13:11:21
|
01001270012B6000,"SONIC FORCES™",,playable,2024-07-28 13:11:21
|
||||||
01004AD014BF0000,"Sonic Frontiers",gpu;deadlock;amd-vendor-bug;intel-vendor-bug,ingame,2024-09-05 09:18:53
|
01004AD014BF0000,"Sonic Frontiers",gpu;deadlock;amd-vendor-bug;intel-vendor-bug,ingame,2024-09-05 09:18:53
|
||||||
@@ -2688,7 +2695,7 @@
|
|||||||
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
|
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
|
||||||
010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37
|
010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37
|
||||||
010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10
|
010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10
|
||||||
0000000000000000,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
|
,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
|
||||||
0100D9B0041CE000,"Spacecats with Lasers",,playable,2022-08-15 17:22:44
|
0100D9B0041CE000,"Spacecats with Lasers",,playable,2022-08-15 17:22:44
|
||||||
010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56
|
010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56
|
||||||
010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39
|
010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39
|
||||||
@@ -2832,8 +2839,9 @@
|
|||||||
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
|
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
|
||||||
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
|
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
|
||||||
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
|
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
|
||||||
|
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
|
||||||
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
|
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
|
||||||
0000000000000000,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
|
,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
|
||||||
010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16
|
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 + Bowser’s Fury",ldn-works,playable,2024-07-31 10:45:37
|
010028600EBDA000,"Super Mario™ 3D World + Bowser’s Fury",ldn-works,playable,2024-07-31 10:45:37
|
||||||
01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
|
01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
|
||||||
@@ -2964,6 +2972,7 @@
|
|||||||
0100C38004DCC000,"The Flame In The Flood: Complete Edition",gpu;nvdec;UE4,ingame,2022-08-22 16:23:49
|
0100C38004DCC000,"The Flame In The Flood: Complete Edition",gpu;nvdec;UE4,ingame,2022-08-22 16:23:49
|
||||||
010007700D4AC000,"The Forbidden Arts",,playable,2021-01-26 16:26:24
|
010007700D4AC000,"The Forbidden Arts",,playable,2021-01-26 16:26:24
|
||||||
010030700CBBC000,"The friends of Ringo Ishikawa",,playable,2022-08-22 16:33:17
|
010030700CBBC000,"The friends of Ringo Ishikawa",,playable,2022-08-22 16:33:17
|
||||||
|
0100b620139d8000,"The Game of Life 2",ldn-untested,ingame,2025-02-03 22:30:00
|
||||||
01006350148DA000,"The Gardener and the Wild Vines",gpu,ingame,2024-04-29 16:32:10
|
01006350148DA000,"The Gardener and the Wild Vines",gpu,ingame,2024-04-29 16:32:10
|
||||||
0100B13007A6A000,"The Gardens Between",,playable,2021-01-29 16:16:53
|
0100B13007A6A000,"The Gardens Between",,playable,2021-01-29 16:16:53
|
||||||
010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29
|
010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29
|
||||||
@@ -2981,6 +2990,8 @@
|
|||||||
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
|
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
|
||||||
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
|
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
|
||||||
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
|
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
|
||||||
|
01006fe0096ac000,"The Jackbox Party Pack 5",slow;online-working,ingame,2025-02-14 05:32:00
|
||||||
|
01005a400db52000,"The Jackbox Party Pack 6",slow;online-working,ingame,2025-02-14 05:26:00
|
||||||
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
|
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
|
||||||
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
|
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
|
||||||
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
|
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
|
||||||
@@ -3159,6 +3170,7 @@
|
|||||||
010055E00CA68000,"Trine 4: The Nightmare Prince",gpu,nothing,2025-01-07 05:47:46
|
010055E00CA68000,"Trine 4: The Nightmare Prince",gpu,nothing,2025-01-07 05:47:46
|
||||||
0100D9000A930000,"Trine Enchanted Edition",ldn-untested;nvdec,playable,2021-06-03 11:28:15
|
0100D9000A930000,"Trine Enchanted Edition",ldn-untested;nvdec,playable,2021-06-03 11:28:15
|
||||||
01002D7010A54000,"Trinity Trigger",crash,ingame,2023-03-03 03:09:09
|
01002D7010A54000,"Trinity Trigger",crash,ingame,2023-03-03 03:09:09
|
||||||
|
010020700a5e0000,"TRIVIAL PURSUIT Live!",ldn-untested,ingame,2025-02-03 22:35:00
|
||||||
0100868013FFC000,"TRIVIAL PURSUIT Live! 2",,boots,2022-12-19 00:04:33
|
0100868013FFC000,"TRIVIAL PURSUIT Live! 2",,boots,2022-12-19 00:04:33
|
||||||
0100F78002040000,"Troll and I™",gpu;nvdec,ingame,2021-06-04 16:58:50
|
0100F78002040000,"Troll and I™",gpu;nvdec,ingame,2021-06-04 16:58:50
|
||||||
0100145011008000,"Trollhunters: Defenders of Arcadia",gpu;nvdec,ingame,2020-11-30 13:27:09
|
0100145011008000,"Trollhunters: Defenders of Arcadia",gpu;nvdec,ingame,2020-11-30 13:27:09
|
||||||
@@ -3208,6 +3220,7 @@
|
|||||||
0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27
|
0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27
|
||||||
0100A3E011CB0000,"Unlock the King 2",,playable,2021-06-15 20:43:55
|
0100A3E011CB0000,"Unlock the King 2",,playable,2021-06-15 20:43:55
|
||||||
01005AA00372A000,"UNO® for Nintendo Switch",nvdec;ldn-untested,playable,2022-07-28 14:49:47
|
01005AA00372A000,"UNO® for Nintendo Switch",nvdec;ldn-untested,playable,2022-07-28 14:49:47
|
||||||
|
0100b6e012ebe000,"UNO",ldn-untested,ingame,2025-02-03 22:40:00
|
||||||
0100E5D00CC0C000,"Unravel Two",nvdec,playable,2024-05-23 15:45:05
|
0100E5D00CC0C000,"Unravel Two",nvdec,playable,2024-05-23 15:45:05
|
||||||
010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31
|
010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31
|
||||||
0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49
|
0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49
|
||||||
@@ -3372,6 +3385,7 @@
|
|||||||
0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51
|
0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51
|
||||||
010012F00B6F2000,"Yomawari: The Long Night Collection",,playable,2022-09-03 14:36:59
|
010012F00B6F2000,"Yomawari: The Long Night Collection",,playable,2022-09-03 14:36:59
|
||||||
0100CC600ABB2000,"Yonder: The Cloud Catcher Chronicles (Retail Only)",,playable,2021-01-28 14:06:25
|
0100CC600ABB2000,"Yonder: The Cloud Catcher Chronicles (Retail Only)",,playable,2021-01-28 14:06:25
|
||||||
|
0100534009ff2000,"Yonder: The Cloud Catcher Chronicles",,playable,2025-02-03 22:19:13
|
||||||
0100BE50042F6000,"Yono and the Celestial Elephants",,playable,2021-01-28 18:23:58
|
0100BE50042F6000,"Yono and the Celestial Elephants",,playable,2021-01-28 18:23:58
|
||||||
0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45
|
0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45
|
||||||
010022F00DA66000,"Yooka-Laylee and the Impossible Lair",,playable,2021-03-05 17:32:21
|
010022F00DA66000,"Yooka-Laylee and the Impossible Lair",,playable,2021-03-05 17:32:21
|
||||||
|
|||||||
|
@@ -7,6 +7,7 @@ namespace ARMeilleure.Memory
|
|||||||
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
||||||
|
|
||||||
public IJitMemoryBlock Block { get; }
|
public IJitMemoryBlock Block { get; }
|
||||||
|
public IJitMemoryAllocator Allocator { get; }
|
||||||
|
|
||||||
public nint Pointer => Block.Pointer;
|
public nint Pointer => Block.Pointer;
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ namespace ARMeilleure.Memory
|
|||||||
granularity = DefaultGranularity;
|
granularity = DefaultGranularity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Allocator = allocator;
|
||||||
Block = allocator.Reserve(maxSize);
|
Block = allocator.Reserve(maxSize);
|
||||||
_maxSize = maxSize;
|
_maxSize = maxSize;
|
||||||
_sizeGranularity = granularity;
|
_sizeGranularity = granularity;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ using ARMeilleure.CodeGen;
|
|||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Native;
|
using ARMeilleure.Native;
|
||||||
|
using Humanizer;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -18,9 +20,8 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int CacheSize = 2047 * 1024 * 1024;
|
private const int CacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
private static ReservedRegion _jitRegion;
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
private static CacheMemoryAllocator _cacheAllocator;
|
||||||
@@ -30,6 +31,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
private static readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
|
||||||
|
private static readonly List<ReservedRegion> _jitRegions = new();
|
||||||
|
private static int _activeRegionIndex = 0;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
@@ -48,7 +52,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
ReservedRegion firstRegion = new(allocator, CacheSize);
|
||||||
|
_jitRegions.Add(firstRegion);
|
||||||
|
_activeRegionIndex = 0;
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
@@ -59,7 +65,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize));
|
JitUnwindWindows.InstallFunctionTableHandler(
|
||||||
|
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
@@ -75,8 +83,8 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -90,9 +98,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -116,52 +124,83 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
foreach (ReservedRegion region in _jitRegions)
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
|
{
|
||||||
|
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
||||||
|
|
||||||
if (allocOffset < 0)
|
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
|
if (allocOffset >= 0)
|
||||||
|
{
|
||||||
|
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
_activeRegionIndex = i;
|
||||||
|
return allocOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
|
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
return allocOffset;
|
int newRegionNumber = _activeRegionIndex;
|
||||||
|
|
||||||
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
|
|
||||||
|
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
|
}
|
||||||
|
|
||||||
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
|
return allocOffsetNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
{
|
{
|
||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
@@ -185,18 +224,21 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
foreach (ReservedRegion _ in _jitRegions)
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
{
|
||||||
index = ~index - 1;
|
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
entry = _cacheEntries[index];
|
index = ~index - 1;
|
||||||
entryIndex = index;
|
}
|
||||||
return true;
|
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
entry = _cacheEntries[index];
|
||||||
|
entryIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,17 +144,15 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public List<ulong> GetBlacklistedFunctions()
|
public List<ulong> GetBlacklistedFunctions()
|
||||||
{
|
{
|
||||||
List<ulong> funcs = new List<ulong>();
|
List<ulong> funcs = [];
|
||||||
|
|
||||||
foreach (var profiledFunc in ProfiledFuncs)
|
foreach ((ulong ptr, FuncProfile funcProfile) in ProfiledFuncs)
|
||||||
{
|
{
|
||||||
if (profiledFunc.Value.Blacklist)
|
if (!funcProfile.Blacklist)
|
||||||
{
|
continue;
|
||||||
if (!funcs.Contains(profiledFunc.Key))
|
|
||||||
{
|
if (!funcs.Contains(ptr))
|
||||||
funcs.Add(profiledFunc.Key);
|
funcs.Add(ptr);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return funcs;
|
return funcs;
|
||||||
|
|||||||
@@ -47,15 +47,15 @@ namespace Ryujinx.Common
|
|||||||
"01006f8002326000", // Animal Crossings: New Horizons
|
"01006f8002326000", // Animal Crossings: New Horizons
|
||||||
"01009bf0072d4000", // Captain Toad: Treasure Tracker
|
"01009bf0072d4000", // Captain Toad: Treasure Tracker
|
||||||
"01009510001ca000", // Fast RMX
|
"01009510001ca000", // Fast RMX
|
||||||
"01005CA01580E000", // Persona 5 Royale
|
"01005CA01580E000", // Persona 5 Royal
|
||||||
|
"0100b880154fc000", // Persona 5 The Royal (Japan)
|
||||||
"010015100b514000", // Super Mario Bros. Wonder
|
"010015100b514000", // Super Mario Bros. Wonder
|
||||||
"0100000000010000", // Super Mario Odyssey
|
"0100000000010000", // Super Mario Odyssey
|
||||||
|
|
||||||
// Further testing is appreciated, I did not test the entire game:
|
// Further testing is appreciated, I did not test the entire game:
|
||||||
"01007300020fa000", // Astral Chain
|
//"010076f0049a2000", // Bayonetta
|
||||||
"010076f0049a2000", // Bayonetta
|
//"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
||||||
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
//"0100f4300bf2c000", // New Pokemon Snap
|
||||||
"0100f4300bf2c000", // New Pokemon Snap
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public static string GetDiscordGameAsset(string titleId)
|
public static string GetDiscordGameAsset(string titleId)
|
||||||
@@ -163,15 +163,16 @@ namespace Ryujinx.Common
|
|||||||
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
|
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
|
||||||
|
|
||||||
//NSO Membership games
|
//NSO Membership games
|
||||||
"0100ccf019c8c000", // F-ZERO 99
|
|
||||||
"0100c62011050000", // GB - Nintendo Switch Online
|
"0100c62011050000", // GB - Nintendo Switch Online
|
||||||
"010012f017576000", // GBA - Nintendo Switch Online
|
"010012f017576000", // GBA - Nintendo Switch Online
|
||||||
"0100c9a00ece6000", // N64 - Nintendo Switch Online
|
"0100c9a00ece6000", // N64 - Nintendo Switch Online
|
||||||
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
|
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
|
||||||
"0100d870045b6000", // NES - Nintendo Switch Online
|
"0100d870045b6000", // NES - Nintendo Switch Online
|
||||||
|
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
|
||||||
|
"01008d300c50c000", // SNES - Nintendo Switch Online
|
||||||
|
"0100ccf019c8c000", // F-ZERO 99
|
||||||
"0100ad9012510000", // PAC-MAN 99
|
"0100ad9012510000", // PAC-MAN 99
|
||||||
"010040600c5ce000", // Tetris 99
|
"010040600c5ce000", // Tetris 99
|
||||||
"01008d300c50c000", // SNES - Nintendo Switch Online
|
|
||||||
"0100277011f1a000", // Super Mario Bros. 35
|
"0100277011f1a000", // Super Mario Bros. 35
|
||||||
|
|
||||||
//Misc Nintendo 1st party games
|
//Misc Nintendo 1st party games
|
||||||
@@ -217,6 +218,7 @@ namespace Ryujinx.Common
|
|||||||
//Misc Games
|
//Misc Games
|
||||||
"010056e00853a000", // A Hat in Time
|
"010056e00853a000", // A Hat in Time
|
||||||
"0100fd1014726000", // Baldurs Gate: Dark Alliance
|
"0100fd1014726000", // Baldurs Gate: Dark Alliance
|
||||||
|
"01008c2019598000", // Bluey: The Video Game
|
||||||
"0100c6800b934000", // Brawlhalla
|
"0100c6800b934000", // Brawlhalla
|
||||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||||
"0100744001588000", // Cars 3: Driven to Win
|
"0100744001588000", // Cars 3: Driven to Win
|
||||||
@@ -227,6 +229,7 @@ namespace Ryujinx.Common
|
|||||||
"01008c8012920000", // Dying Light Platinum Edition
|
"01008c8012920000", // Dying Light Platinum Edition
|
||||||
"01001cc01b2d4000", // Goat Simulator 3
|
"01001cc01b2d4000", // Goat Simulator 3
|
||||||
"01003620068ea000", // Hand of Fate 2
|
"01003620068ea000", // Hand of Fate 2
|
||||||
|
"0100f7e00c70e000", // Hogwarts Legacy
|
||||||
"010085500130a000", // Lego City: Undercover
|
"010085500130a000", // Lego City: Undercover
|
||||||
"010073c01af34000", // LEGO Horizon Adventures
|
"010073c01af34000", // LEGO Horizon Adventures
|
||||||
"0100d71004694000", // Minecraft
|
"0100d71004694000", // Minecraft
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using Humanizer;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -15,9 +17,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int CacheSize = 2047 * 1024 * 1024;
|
private const int CacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
private static ReservedRegion _jitRegion;
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
private static CacheMemoryAllocator _cacheAllocator;
|
||||||
@@ -26,6 +27,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
private static readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
private static readonly List<ReservedRegion> _jitRegions = new();
|
||||||
|
private static int _activeRegionIndex = 0;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
@@ -45,7 +48,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
ReservedRegion firstRegion = new(allocator, CacheSize);
|
||||||
|
_jitRegions.Add(firstRegion);
|
||||||
|
_activeRegionIndex = 0;
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
@@ -65,8 +70,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -80,18 +85,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
Marshal.Copy(code.ToArray(), 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
||||||
{
|
|
||||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Add(funcOffset, code.Length);
|
Add(funcOffset, code.Length);
|
||||||
@@ -106,50 +104,80 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
foreach (ReservedRegion region in _jitRegions)
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
|
{
|
||||||
|
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
||||||
|
|
||||||
if (allocOffset < 0)
|
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
|
if (allocOffset >= 0)
|
||||||
|
{
|
||||||
|
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
_activeRegionIndex = i;
|
||||||
|
return allocOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
|
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
return allocOffset;
|
int newRegionNumber = _activeRegionIndex;
|
||||||
|
|
||||||
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
|
|
||||||
|
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
|
}
|
||||||
|
|
||||||
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
|
return allocOffsetNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
{
|
{
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int SharedCacheSize = 2047 * 1024 * 1024;
|
private const int SharedCacheSize = 2047 * 1024 * 1024;
|
||||||
private const int LocalCacheSize = 128 * 1024 * 1024;
|
private const int LocalCacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
||||||
// and allow the guest to take the fast path.
|
// and allow the guest to take the fast path.
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
|
|
||||||
shaderGpuVa += (uint)qmd.ProgramOffset;
|
shaderGpuVa += (uint)qmd.ProgramOffset;
|
||||||
|
|
||||||
|
ShaderCache shaderCache = memoryManager.GetBackingMemory(shaderGpuVa).ShaderCache;
|
||||||
|
|
||||||
int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize;
|
int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize;
|
||||||
|
|
||||||
int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize);
|
int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize);
|
||||||
@@ -142,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
sharedMemorySize,
|
sharedMemorySize,
|
||||||
_channel.BufferManager.HasUnalignedStorageBuffers);
|
_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);
|
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
||||||
|
|
||||||
@@ -156,10 +158,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
{
|
{
|
||||||
BufferDescriptor sb = info.SBuffers[index];
|
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;
|
sbDescAddress += (ulong)sb.SbCbOffset * 4;
|
||||||
|
|
||||||
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
|
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress);
|
||||||
|
|
||||||
uint size;
|
uint size;
|
||||||
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
|
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
|
||||||
@@ -187,7 +189,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
sharedMemorySize,
|
sharedMemorySize,
|
||||||
_channel.BufferManager.HasUnalignedStorageBuffers);
|
_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);
|
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,6 +216,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
_3dEngine.CreatePendingSyncs();
|
_3dEngine.CreatePendingSyncs();
|
||||||
_3dEngine.FlushUboDirty();
|
_3dEngine.FlushUboDirty();
|
||||||
|
|
||||||
|
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcGpuVa);
|
||||||
|
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstGpuVa);
|
||||||
|
|
||||||
if (copy2D)
|
if (copy2D)
|
||||||
{
|
{
|
||||||
// Buffer to texture copy.
|
// Buffer to texture copy.
|
||||||
@@ -293,7 +296,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
|
|
||||||
if (completeSource && completeDest && !srcLinear && isIdentityRemap)
|
if (completeSource && completeDest && !srcLinear && isIdentityRemap)
|
||||||
{
|
{
|
||||||
Image.Texture source = memoryManager.Physical.TextureCache.FindTexture(
|
Image.Texture source = srcPhysical.TextureCache.FindTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
srcGpuVa,
|
srcGpuVa,
|
||||||
srcBpp,
|
srcBpp,
|
||||||
@@ -309,7 +312,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
{
|
{
|
||||||
source.SynchronizeMemory();
|
source.SynchronizeMemory();
|
||||||
|
|
||||||
Image.Texture target = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
Image.Texture target = dstPhysical.TextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
source.Info.FormatInfo,
|
source.Info.FormatInfo,
|
||||||
dstGpuVa,
|
dstGpuVa,
|
||||||
@@ -339,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
|
|
||||||
if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap)
|
if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap)
|
||||||
{
|
{
|
||||||
Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
|
Image.Texture target = dstPhysical.TextureCache.FindTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
dstGpuVa,
|
dstGpuVa,
|
||||||
dstBpp,
|
dstBpp,
|
||||||
@@ -462,6 +465,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
BufferCache bufferCache = dstPhysical.BufferCache;
|
||||||
if (remap &&
|
if (remap &&
|
||||||
_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA &&
|
_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA &&
|
||||||
_state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA &&
|
_state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA &&
|
||||||
@@ -472,7 +476,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
_state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four)
|
_state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four)
|
||||||
{
|
{
|
||||||
// Fast path for clears when remap is enabled.
|
// Fast path for clears when remap is enabled.
|
||||||
memoryManager.Physical.BufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
|
bufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -492,7 +496,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
|
BufferCache.CopyBuffer(_context,memoryManager, srcGpuVa, dstGpuVa, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
|
|||||||
// Right now the copy code at the bottom assumes that it is used on both which might be incorrect.
|
// Right now the copy code at the bottom assumes that it is used on both which might be incorrect.
|
||||||
if (!_isLinear)
|
if (!_isLinear)
|
||||||
{
|
{
|
||||||
Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
|
Image.Texture target = memoryManager.GetBackingMemory(_dstGpuVa).TextureCache.FindTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
_dstGpuVa,
|
_dstGpuVa,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
|
|
||||||
ulong indirectBufferGpuVa = count.GpuVa;
|
ulong indirectBufferGpuVa = count.GpuVa;
|
||||||
|
|
||||||
BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
|
BufferCache bufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
|
||||||
|
|
||||||
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
|
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
|
||||||
|
|
||||||
@@ -394,6 +394,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
|
|
||||||
_processor.ThreedClass.DrawIndirect(
|
_processor.ThreedClass.DrawIndirect(
|
||||||
topology,
|
topology,
|
||||||
|
bufferCache,
|
||||||
|
null,
|
||||||
new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
|
new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
|
||||||
default,
|
default,
|
||||||
1,
|
1,
|
||||||
@@ -491,22 +493,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BufferCache indirectBufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
|
||||||
BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
|
BufferCache parameterBufferCache = _processor.MemoryManager.GetBackingMemory(parameterBufferGpuVa).BufferCache;
|
||||||
|
|
||||||
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
|
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
|
||||||
|
|
||||||
MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
|
MultiRange indirectBufferRange = indirectBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
|
||||||
MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
|
MultiRange parameterBufferRange = parameterBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
|
||||||
|
|
||||||
_processor.ThreedClass.DrawIndirect(
|
_processor.ThreedClass.DrawIndirect(
|
||||||
topology,
|
topology,
|
||||||
|
indirectBufferCache,
|
||||||
|
parameterBufferCache,
|
||||||
indirectBufferRange,
|
indirectBufferRange,
|
||||||
parameterBufferRange,
|
parameterBufferRange,
|
||||||
maxDrawCount,
|
maxDrawCount,
|
||||||
stride,
|
stride,
|
||||||
indexCount,
|
indexCount,
|
||||||
Threed.IndirectDrawType.DrawIndexedIndirectCount);
|
IndirectDrawType.DrawIndexedIndirectCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class VtgAsComputeContext : IDisposable
|
class VtgAsComputeContext : IDisposable
|
||||||
{
|
{
|
||||||
|
private const int DummyBufferSize = 16;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -46,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
format.GetBytesPerElement(),
|
1,
|
||||||
format,
|
format,
|
||||||
DepthStencilMode.Depth,
|
DepthStencilMode.Depth,
|
||||||
Target.TextureBuffer,
|
Target.TextureBuffer,
|
||||||
@@ -519,6 +521,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
|
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the range for a dummy 16 bytes buffer, filled with zeros.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Dummy buffer range</returns>
|
||||||
|
public BufferRange GetDummyBufferRange()
|
||||||
|
{
|
||||||
|
if (_dummyBuffer == BufferHandle.Null)
|
||||||
|
{
|
||||||
|
_dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory);
|
||||||
|
_context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BufferRange(_dummyBuffer, 0, DummyBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the range for a sequential index buffer, with ever incrementing index values.
|
/// Gets the range for a sequential index buffer, with ever incrementing index values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
{
|
{
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
||||||
|
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
{
|
{
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
||||||
|
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vbStride = vertexBuffer.UnpackStride();
|
int vbStride = vertexBuffer.UnpackStride();
|
||||||
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
|
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
|
||||||
|
|
||||||
|
ulong oldVbSize = vbSize;
|
||||||
|
|
||||||
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
|
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
|
||||||
int componentSize = format.GetScalarSize();
|
int componentSize = format.GetScalarSize();
|
||||||
|
|
||||||
@@ -196,11 +200,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
|
|
||||||
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
|
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
|
||||||
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
|
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
|
||||||
_context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
|
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) });
|
||||||
|
|
||||||
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
|
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
|
||||||
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true);
|
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true);
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
|
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) });
|
||||||
|
|
||||||
_vacContext.VertexInfoBufferUpdater.Commit();
|
_vacContext.VertexInfoBufferUpdater.Commit();
|
||||||
|
|
||||||
@@ -228,7 +232,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
|
|
||||||
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
|
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
|
||||||
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
|
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
|
||||||
_context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
|
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) });
|
||||||
|
|
||||||
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
|
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 vertexBuffer = _vacContext.GetGeometryVertexDataBufferRange(_geometryVertexDataOffset, _geometryVertexDataSize, write: true);
|
||||||
BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true);
|
BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers([
|
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[]
|
||||||
|
{
|
||||||
new BufferAssignment(vertexDataBinding, vertexDataRange),
|
new BufferAssignment(vertexDataBinding, vertexDataRange),
|
||||||
new BufferAssignment(geometryVbBinding, vertexBuffer),
|
new BufferAssignment(geometryVbBinding, vertexBuffer),
|
||||||
new BufferAssignment(geometryIbBinding, indexBuffer)
|
new BufferAssignment(geometryIbBinding, indexBuffer),
|
||||||
]);
|
});
|
||||||
|
|
||||||
_context.Renderer.Pipeline.DispatchCompute(
|
_context.Renderer.Pipeline.DispatchCompute(
|
||||||
BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize),
|
BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize),
|
||||||
@@ -294,7 +299,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
|
|
||||||
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
|
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
|
||||||
_context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt);
|
_context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt);
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexBuffer)]);
|
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexBuffer) });
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetPrimitiveRestart(true, -1);
|
_context.Renderer.Pipeline.SetPrimitiveRestart(true, -1);
|
||||||
_context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive));
|
_context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive));
|
||||||
@@ -309,7 +314,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false);
|
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
|
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
|
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) });
|
||||||
_context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0);
|
_context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,6 +345,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
return maxOutputVertices / verticesPerPrimitive;
|
return maxOutputVertices / verticesPerPrimitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a dummy buffer as vertex buffer into a buffer texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reservations">Shader resource binding reservations</param>
|
||||||
|
/// <param name="index">Buffer texture index</param>
|
||||||
|
/// <param name="format">Buffer texture format</param>
|
||||||
|
private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format)
|
||||||
|
{
|
||||||
|
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
|
||||||
|
bufferTexture.SetStorage(_vacContext.GetDummyBufferRange());
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Binds a vertex buffer into a buffer texture.
|
/// Binds a vertex buffer into a buffer texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -352,7 +371,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
{
|
{
|
||||||
MemoryManager memoryManager = _channel.MemoryManager;
|
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);
|
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
|
||||||
bufferTexture.SetStorage(range);
|
bufferTexture.SetStorage(range);
|
||||||
@@ -394,7 +413,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
MemoryManager memoryManager = _channel.MemoryManager;
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
|
|
||||||
ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1);
|
ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1);
|
||||||
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(
|
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(
|
||||||
memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign),
|
memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign),
|
||||||
BufferStage.IndexBuffer);
|
BufferStage.IndexBuffer);
|
||||||
misalignedOffset = (int)misalign >> shift;
|
misalignedOffset = (int)misalign >> shift;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
// State associated with direct uniform buffer updates.
|
// State associated with direct uniform buffer updates.
|
||||||
// This state is used to attempt to batch together consecutive updates.
|
// This state is used to attempt to batch together consecutive updates.
|
||||||
|
private ulong _ubBeginGpuAddress = 0;
|
||||||
private ulong _ubBeginCpuAddress = 0;
|
private ulong _ubBeginCpuAddress = 0;
|
||||||
private ulong _ubFollowUpAddress = 0;
|
private ulong _ubFollowUpAddress = 0;
|
||||||
private ulong _ubByteCount = 0;
|
private ulong _ubByteCount = 0;
|
||||||
@@ -113,12 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
if (_ubFollowUpAddress != 0)
|
if (_ubFollowUpAddress != 0)
|
||||||
{
|
{
|
||||||
MemoryManager memoryManager = _channel.MemoryManager;
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
|
PhysicalMemory physicalMemory = memoryManager.GetBackingMemory(_ubBeginGpuAddress);
|
||||||
|
|
||||||
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
|
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
|
||||||
|
|
||||||
if (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;
|
_ubFollowUpAddress = 0;
|
||||||
|
|||||||
@@ -641,6 +641,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
public void DrawIndirect(
|
public void DrawIndirect(
|
||||||
ThreedClass engine,
|
ThreedClass engine,
|
||||||
PrimitiveTopology topology,
|
PrimitiveTopology topology,
|
||||||
|
BufferCache indirectBufferCache,
|
||||||
|
BufferCache parameterBufferCache,
|
||||||
MultiRange indirectBufferRange,
|
MultiRange indirectBufferRange,
|
||||||
MultiRange parameterBufferRange,
|
MultiRange parameterBufferRange,
|
||||||
int maxDrawCount,
|
int maxDrawCount,
|
||||||
@@ -662,8 +664,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalMemory memory = _channel.MemoryManager.Physical;
|
|
||||||
|
|
||||||
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
|
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
|
||||||
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
|
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
|
||||||
|
|
||||||
@@ -684,8 +684,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
if (hasCount)
|
if (hasCount)
|
||||||
{
|
{
|
||||||
BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
|
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
|
||||||
BufferRange parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
|
BufferRange parameterBuffer = parameterBufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
|
||||||
|
|
||||||
if (indexed)
|
if (indexed)
|
||||||
{
|
{
|
||||||
@@ -698,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
|
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
|
||||||
|
|
||||||
if (indexed)
|
if (indexed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -381,10 +381,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
BufferDescriptor sb = info.SBuffers[index];
|
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;
|
sbDescAddress += (ulong)sb.SbCbOffset * 4;
|
||||||
|
|
||||||
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
|
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress);
|
||||||
|
|
||||||
uint size;
|
uint size;
|
||||||
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
|
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
|
||||||
@@ -505,7 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
rtNoAlphaMask |= 1u << index;
|
rtNoAlphaMask |= 1u << index;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
TextureCache colorTextureCache = memoryManager.GetBackingMemory(colorState.Address.Pack()).TextureCache;
|
||||||
|
|
||||||
|
Image.Texture color = colorTextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
colorState,
|
colorState,
|
||||||
_vtgWritesRtLayer || layered,
|
_vtgWritesRtLayer || layered,
|
||||||
@@ -543,8 +545,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
RtDepthStencilState dsState = _state.State.RtDepthStencilState;
|
RtDepthStencilState dsState = _state.State.RtDepthStencilState;
|
||||||
Size3D dsSize = _state.State.RtDepthStencilSize;
|
Size3D dsSize = _state.State.RtDepthStencilSize;
|
||||||
|
TextureCache dsTextureCache = memoryManager.GetBackingMemory(dsState.Address.Pack()).TextureCache;
|
||||||
|
|
||||||
depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
depthStencil = dsTextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
dsState,
|
dsState,
|
||||||
dsSize,
|
dsSize,
|
||||||
@@ -1409,8 +1412,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateShaderState()
|
private void UpdateShaderState()
|
||||||
{
|
{
|
||||||
ShaderCache shaderCache = _channel.MemoryManager.Physical.ShaderCache;
|
|
||||||
|
|
||||||
_vtgWritesRtLayer = false;
|
_vtgWritesRtLayer = false;
|
||||||
|
|
||||||
ShaderAddresses addresses = new();
|
ShaderAddresses addresses = new();
|
||||||
@@ -1433,6 +1434,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
? _state.State.TexturePoolState.MaximumId
|
? _state.State.TexturePoolState.MaximumId
|
||||||
: _state.State.SamplerPoolState.MaximumId;
|
: _state.State.SamplerPoolState.MaximumId;
|
||||||
|
|
||||||
|
// Shader stages on different address spaces are not supported right now,
|
||||||
|
// but it should never happen in practice anyway.
|
||||||
|
ShaderCache shaderCache = _channel.MemoryManager.GetBackingMemory(addresses.VertexB).ShaderCache;
|
||||||
CachedShaderProgram gs = shaderCache.GetGraphicsShader(
|
CachedShaderProgram gs = shaderCache.GetGraphicsShader(
|
||||||
ref _state.State,
|
ref _state.State,
|
||||||
ref _pipeline,
|
ref _pipeline,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
|||||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
@@ -804,6 +805,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// Performs a indirect draw, with parameters from a GPU buffer.
|
/// Performs a indirect draw, with parameters from a GPU buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="topology">Primitive topology</param>
|
/// <param name="topology">Primitive topology</param>
|
||||||
|
/// <param name="indirectBufferCache">Buffer cache owning the buffer with the draw parameters</param>
|
||||||
|
/// <param name="parameterBufferCache">Buffer cache owning the buffer with the draw count</param>
|
||||||
/// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
|
/// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
|
||||||
/// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
|
/// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
|
||||||
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
||||||
@@ -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>
|
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
|
||||||
public void DrawIndirect(
|
public void DrawIndirect(
|
||||||
PrimitiveTopology topology,
|
PrimitiveTopology topology,
|
||||||
|
BufferCache indirectBufferCache,
|
||||||
|
BufferCache parameterBufferCache,
|
||||||
MultiRange indirectBufferRange,
|
MultiRange indirectBufferRange,
|
||||||
MultiRange parameterBufferRange,
|
MultiRange parameterBufferRange,
|
||||||
int maxDrawCount,
|
int maxDrawCount,
|
||||||
@@ -819,7 +824,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
int indexCount,
|
int indexCount,
|
||||||
IndirectDrawType drawType)
|
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>
|
/// <summary>
|
||||||
|
|||||||
@@ -234,6 +234,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
|||||||
TwodTexture dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
|
TwodTexture dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
|
||||||
TwodTexture srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
|
TwodTexture srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
|
||||||
|
|
||||||
|
TextureCache srcTextureCache = memoryManager.GetBackingMemory(srcCopyTexture.Address.Pack()).TextureCache;
|
||||||
|
TextureCache dstTextureCache = memoryManager.GetBackingMemory(dstCopyTexture.Address.Pack()).TextureCache;
|
||||||
|
|
||||||
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
|
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
|
||||||
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
|
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
|
||||||
|
|
||||||
@@ -305,7 +308,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
|||||||
// are the same, as we can't blit between different depth formats.
|
// are the same, as we can't blit between different depth formats.
|
||||||
bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format;
|
bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format;
|
||||||
|
|
||||||
Image.Texture srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
Image.Texture srcTexture = srcTextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
srcCopyTexture,
|
srcCopyTexture,
|
||||||
offset,
|
offset,
|
||||||
@@ -326,7 +329,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryManager.Physical.TextureCache.Lift(srcTexture);
|
srcTextureCache.Lift(srcTexture);
|
||||||
|
|
||||||
// When the source texture that was found has a depth format,
|
// When the source texture that was found has a depth format,
|
||||||
// we must enforce the target texture also has a depth format,
|
// we must enforce the target texture also has a depth format,
|
||||||
@@ -342,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
|||||||
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
|
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
|
||||||
}
|
}
|
||||||
|
|
||||||
Image.Texture dstTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
Image.Texture dstTexture = dstTextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
dstCopyTexture,
|
dstCopyTexture,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -58,22 +58,24 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
public void BindMemory(MemoryManager memoryManager)
|
public void BindMemory(MemoryManager memoryManager)
|
||||||
{
|
{
|
||||||
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
|
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
|
||||||
|
if (oldMemoryManager == memoryManager)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
memoryManager.Physical.IncrementReferenceCount();
|
memoryManager.AttachToChannel(BufferManager.Rebind);
|
||||||
|
|
||||||
if (oldMemoryManager != null)
|
if (oldMemoryManager != null)
|
||||||
{
|
{
|
||||||
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
oldMemoryManager.DetachFromChannel(BufferManager.Rebind);
|
||||||
oldMemoryManager.Physical.DecrementReferenceCount();
|
|
||||||
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
|
|
||||||
memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
|
memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
|
||||||
|
|
||||||
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
||||||
TextureManager.ReloadPools();
|
TextureManager.ReloadPools();
|
||||||
memoryManager.Physical.BufferCache.QueuePrune();
|
memoryManager.QueuePrune();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -86,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
TextureManager.ReloadPools();
|
TextureManager.ReloadPools();
|
||||||
|
|
||||||
MemoryManager memoryManager = Volatile.Read(ref _memoryManager);
|
MemoryManager memoryManager = Volatile.Read(ref _memoryManager);
|
||||||
memoryManager?.Physical.BufferCache.QueuePrune();
|
memoryManager?.QueuePrune();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -141,8 +143,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
|
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
|
||||||
if (oldMemoryManager != null)
|
if (oldMemoryManager != null)
|
||||||
{
|
{
|
||||||
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
oldMemoryManager.DetachFromChannel(BufferManager.Rebind);
|
||||||
oldMemoryManager.Physical.DecrementReferenceCount();
|
|
||||||
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
|||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Shader;
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -172,7 +173,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
|
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MemoryManager(physicalMemory, cpuMemorySize);
|
return new MemoryManager(this, physicalMemory, cpuMemorySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -197,7 +198,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
|
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
|
||||||
/// <param name="cpuMemory">Virtual memory owned by the process</param>
|
/// <param name="cpuMemory">Virtual memory owned by the process</param>
|
||||||
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
|
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
|
||||||
public void RegisterProcess(ulong pid, Cpu.IVirtualMemoryManagerTracked cpuMemory)
|
public void RegisterProcess(ulong pid, IVirtualMemoryManagerTracked cpuMemory)
|
||||||
{
|
{
|
||||||
PhysicalMemory physicalMemory = new(this, cpuMemory);
|
PhysicalMemory physicalMemory = new(this, cpuMemory);
|
||||||
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))
|
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
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="maximumId">Maximum ID of the texture pool</param>
|
||||||
/// <param name="bindingsArrayCache">Cache of texture array bindings</param>
|
/// <param name="bindingsArrayCache">Cache of texture array bindings</param>
|
||||||
/// <returns>The found or newly created texture pool</returns>
|
/// <returns>The found or newly created texture pool</returns>
|
||||||
public T FindOrCreate(GpuChannel channel, 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.
|
// Remove old entries from the cache, if possible.
|
||||||
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
|
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
|
||||||
@@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If not found, create a new one.
|
// 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.CacheNode = _pools.AddLast(pool);
|
||||||
pool.CacheTimestamp = _currentTimestamp;
|
pool.CacheTimestamp = _currentTimestamp;
|
||||||
@@ -112,9 +113,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the pool belongs to</param>
|
/// <param name="context">GPU context that the pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the pool belongs to</param>
|
/// <param name="channel">GPU channel that the pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">GPU backing memory of the pool</param>
|
||||||
/// <param name="address">Address of the pool in guest memory</param>
|
/// <param name="address">Address of the pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
|
/// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
|
||||||
protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId);
|
protected abstract T CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId);
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -20,11 +22,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">GPU backing memory of the pool</param>
|
||||||
/// <param name="address">Address of the sampler pool in guest memory</param>
|
/// <param name="address">Address of the sampler pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
|
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
|
||||||
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -660,6 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
||||||
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
||||||
|
BufferCache bufferCache = null;
|
||||||
|
|
||||||
for (int index = 0; index < length; index++)
|
for (int index = 0; index < length; index++)
|
||||||
{
|
{
|
||||||
@@ -673,7 +674,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
|
||||||
|
bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
entry.Textures[texture] = texture.InvalidatedSequence;
|
entry.Textures[texture] = texture.InvalidatedSequence;
|
||||||
@@ -702,11 +703,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); }
|
||||||
}
|
|
||||||
else
|
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)
|
else if (isImage)
|
||||||
@@ -797,11 +797,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range));
|
||||||
|
|
||||||
if (separateSamplerBuffer)
|
if (separateSamplerBuffer)
|
||||||
{
|
{
|
||||||
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -828,11 +828,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range));
|
||||||
|
|
||||||
if (separateSamplerBuffer)
|
if (separateSamplerBuffer)
|
||||||
{
|
{
|
||||||
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -901,16 +900,18 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||||
{
|
{
|
||||||
|
BufferCache bufferCache = textureBufferBounds.BufferCache;
|
||||||
|
|
||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
|
||||||
}
|
}
|
||||||
else
|
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)
|
else if (isImage)
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
|
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
|
||||||
cachedTextureBufferIndex = textureBufferIndex;
|
cachedTextureBufferIndex = textureBufferIndex;
|
||||||
|
|
||||||
if (samplerBufferIndex == textureBufferIndex)
|
if (samplerBufferIndex == textureBufferIndex)
|
||||||
@@ -410,7 +410,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
|
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
|
||||||
cachedSamplerBufferIndex = samplerBufferIndex;
|
cachedSamplerBufferIndex = samplerBufferIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,7 +524,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
_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.
|
// Cache is not used for buffer texture, it must always rebind.
|
||||||
state.CachedTexture = null;
|
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
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
|
|
||||||
_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.
|
// Cache is not used for buffer texture, it must always rebind.
|
||||||
state.CachedTexture = null;
|
state.CachedTexture = null;
|
||||||
@@ -715,9 +717,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
|
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
|
||||||
int textureId = TextureHandle.UnpackTextureId(packedId);
|
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||||
|
|
||||||
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolGpuVa);
|
||||||
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
||||||
|
|
||||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache);
|
||||||
|
|
||||||
TextureDescriptor descriptor;
|
TextureDescriptor descriptor;
|
||||||
|
|
||||||
@@ -751,12 +754,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
|
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
|
||||||
|
|
||||||
ulong textureBufferAddress = _isCompute
|
(PhysicalMemory texturePhysicalMemory, ulong textureBufferAddress) = _isCompute
|
||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
int handle = textureBufferAddress != MemoryManager.PteUnmapped
|
int handle = textureBufferAddress != MemoryManager.PteUnmapped
|
||||||
? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
|
? texturePhysicalMemory.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
||||||
@@ -771,12 +774,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
|
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
|
||||||
{
|
{
|
||||||
ulong samplerBufferAddress = _isCompute
|
(PhysicalMemory samplerPhysicalMemory, ulong samplerBufferAddress) = _isCompute
|
||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped
|
samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped
|
||||||
? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
|
? samplerPhysicalMemory.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -813,7 +816,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (poolAddress != MemoryManager.PteUnmapped)
|
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;
|
_texturePool = texturePool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -824,7 +828,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (poolAddress != MemoryManager.PteUnmapped)
|
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;
|
_samplerPool = samplerPool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Shader;
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -385,8 +386,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
|
public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
|
||||||
{
|
{
|
||||||
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
||||||
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolAddress);
|
||||||
|
|
||||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache);
|
||||||
|
|
||||||
return texturePool;
|
return texturePool;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,9 +160,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Backing memory of the pool</param>
|
||||||
/// <param name="address">Address of the texture pool in guest memory</param>
|
/// <param name="address">Address of the texture pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
||||||
public TexturePool(GpuContext context, GpuChannel channel, 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;
|
_channel = channel;
|
||||||
_aliasLists = new Dictionary<Texture, TextureAliasList>();
|
_aliasLists = new Dictionary<Texture, TextureAliasList>();
|
||||||
@@ -193,7 +194,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
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 this happens, then the texture address is invalid, we can't add it to the cache.
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
@@ -421,7 +424,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
continue;
|
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.
|
// 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))
|
if (!range.Equals(texture.Range))
|
||||||
{
|
{
|
||||||
// Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
|
// Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
|
||||||
if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range))
|
if (!textureCache.UpdateMapping(texture, range))
|
||||||
{
|
{
|
||||||
// Texture could not be remapped due to a collision, just delete it.
|
// Texture could not be remapped due to a collision, just delete it.
|
||||||
if (Interlocked.Exchange(ref Items[request.ID], null) != null)
|
if (Interlocked.Exchange(ref Items[request.ID], null) != null)
|
||||||
@@ -481,6 +485,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="size">Size of the range being invalidated</param>
|
/// <param name="size">Size of the range being invalidated</param>
|
||||||
protected override void InvalidateRangeImpl(ulong address, ulong size)
|
protected override void InvalidateRangeImpl(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
ProcessDereferenceQueue();
|
ProcessDereferenceQueue();
|
||||||
|
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
@@ -505,7 +510,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (texture.HasOneReference())
|
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)
|
if (Interlocked.Exchange(ref Items[id], null) != null)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -20,11 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Backing memory of the pool</param>
|
||||||
/// <param name="address">Address of the texture pool in guest memory</param>
|
/// <param name="address">Address of the texture pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
||||||
protected override TexturePool CreatePool(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
readonly struct BufferBounds : IEquatable<BufferBounds>
|
readonly struct BufferBounds : IEquatable<BufferBounds>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Physical memory backing the buffer.
|
||||||
|
/// </summary>
|
||||||
|
public PhysicalMemory Physical { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Buffer cache that owns the buffer.
|
||||||
|
/// </summary>
|
||||||
|
public BufferCache BufferCache => Physical.BufferCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Physical memory ranges where the buffer is mapped.
|
/// Physical memory ranges where the buffer is mapped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -29,8 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="range">Physical memory ranges where the buffer is mapped</param>
|
/// <param name="range">Physical memory ranges where the buffer is mapped</param>
|
||||||
/// <param name="flags">Buffer usage flags</param>
|
/// <param name="flags">Buffer usage flags</param>
|
||||||
public BufferBounds(MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
|
public BufferBounds(PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
|
||||||
{
|
{
|
||||||
|
Physical = physical;
|
||||||
Range = range;
|
Range = range;
|
||||||
Flags = flags;
|
Flags = flags;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -735,18 +735,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This does a GPU side copy.
|
/// This does a GPU side copy.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
/// <param name="context">GPU context</param>
|
||||||
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||||
/// <param name="srcVa">GPU virtual address of the copy source</param>
|
/// <param name="srcVa">GPU virtual address of the copy source</param>
|
||||||
/// <param name="dstVa">GPU virtual address of the copy destination</param>
|
/// <param name="dstVa">GPU virtual address of the copy destination</param>
|
||||||
/// <param name="size">Size in bytes of the copy</param>
|
/// <param name="size">Size in bytes of the copy</param>
|
||||||
public 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);
|
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcVa);
|
||||||
MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size, BufferStage.Copy);
|
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)
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -777,7 +781,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong dstSize = dstSubRange.Size - dstOffset;
|
ulong dstSize = dstSubRange.Size - dstOffset;
|
||||||
ulong copySize = Math.Min(srcSize, dstSize);
|
ulong copySize = Math.Min(srcSize, dstSize);
|
||||||
|
|
||||||
CopyBufferSingleRange(memoryManager, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
|
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
|
||||||
|
|
||||||
srcOffset += copySize;
|
srcOffset += copySize;
|
||||||
dstOffset += copySize;
|
dstOffset += copySize;
|
||||||
@@ -793,18 +797,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// This does a GPU side copy.
|
/// This does a GPU side copy.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||||
|
/// <param name="srcPhysical">Physical memory backing the source buffer.</param>
|
||||||
|
/// <param name="dstPhysical">Physical memory backing the destination buffer.</param>
|
||||||
/// <param name="srcAddress">Physical address of the copy source</param>
|
/// <param name="srcAddress">Physical address of the copy source</param>
|
||||||
/// <param name="dstAddress">Physical address of the copy destination</param>
|
/// <param name="dstAddress">Physical address of the copy destination</param>
|
||||||
/// <param name="size">Size in bytes of the copy</param>
|
/// <param name="size">Size in bytes of the copy</param>
|
||||||
private 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 srcBuffer = srcPhysical.BufferCache.GetBuffer(srcAddress, size, BufferStage.Copy);
|
||||||
Buffer dstBuffer = GetBuffer(dstAddress, size, BufferStage.Copy);
|
Buffer dstBuffer = dstPhysical.BufferCache.GetBuffer(dstAddress, size, BufferStage.Copy);
|
||||||
|
|
||||||
int srcOffset = (int)(srcAddress - srcBuffer.Address);
|
int srcOffset = (int)(srcAddress - srcBuffer.Address);
|
||||||
int dstOffset = (int)(dstAddress - dstBuffer.Address);
|
int dstOffset = (int)(dstAddress - dstBuffer.Address);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.CopyBuffer(
|
context.Renderer.Pipeline.CopyBuffer(
|
||||||
srcBuffer.Handle,
|
srcBuffer.Handle,
|
||||||
dstBuffer.Handle,
|
dstBuffer.Handle,
|
||||||
srcOffset,
|
srcOffset,
|
||||||
@@ -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.
|
// Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
|
||||||
|
|
||||||
dstBuffer.ClearModified(dstAddress, size);
|
dstBuffer.ClearModified(dstAddress, size);
|
||||||
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);
|
dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size);
|
||||||
@@ -849,7 +861,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
|
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
|
||||||
|
|
||||||
memoryManager.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);
|
buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,18 +66,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
Buffers = new BufferBounds[count];
|
Buffers = new BufferBounds[count];
|
||||||
Unaligned = new bool[count];
|
Unaligned = new bool[count];
|
||||||
|
|
||||||
Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL)));
|
Buffers.AsSpan().Fill(new BufferBounds(null, new MultiRange(MemoryManager.PteUnmapped, 0UL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the region of a buffer at a given slot.
|
/// Sets the region of a buffer at a given slot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">Buffer slot</param>
|
/// <param name="index">Buffer slot</param>
|
||||||
|
/// <param name="physical">Physical memory backing the buffer</param>
|
||||||
/// <param name="range">Physical memory regions where the buffer is mapped</param>
|
/// <param name="range">Physical memory regions where the buffer is mapped</param>
|
||||||
/// <param name="flags">Buffer usage flags</param>
|
/// <param name="flags">Buffer usage flags</param>
|
||||||
public void SetBounds(int index, 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>
|
/// <summary>
|
||||||
@@ -156,8 +157,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="type">Type of each index buffer element</param>
|
/// <param name="type">Type of each index buffer element</param>
|
||||||
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
|
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
|
||||||
{
|
{
|
||||||
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.Range = range;
|
||||||
_indexBuffer.Type = type;
|
_indexBuffer.Type = type;
|
||||||
|
|
||||||
@@ -186,11 +189,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
|
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
|
||||||
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
|
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
|
||||||
{
|
{
|
||||||
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;
|
ref VertexBuffer vb = ref _vertexBuffers[index];
|
||||||
_vertexBuffers[index].Stride = stride;
|
|
||||||
_vertexBuffers[index].Divisor = divisor;
|
vb.BufferCache = bufferCache;
|
||||||
|
vb.Range = range;
|
||||||
|
vb.Stride = stride;
|
||||||
|
vb.Divisor = divisor;
|
||||||
|
|
||||||
_vertexBuffersDirty = true;
|
_vertexBuffersDirty = true;
|
||||||
|
|
||||||
@@ -213,9 +220,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the transform feedback buffer</param>
|
/// <param name="size">Size in bytes of the transform feedback buffer</param>
|
||||||
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
|
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
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;
|
_transformFeedbackBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,11 +266,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
|
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>
|
/// <summary>
|
||||||
@@ -282,16 +291,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
RecordStorageAlignment(buffers, index, gpuVa);
|
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))
|
if (!buffers.Buffers[index].Range.Equals(range))
|
||||||
{
|
{
|
||||||
_gpStorageBuffersDirty = true;
|
_gpStorageBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffers.SetBounds(index, range, flags);
|
buffers.SetBounds(index, physical, range, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -303,9 +313,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||||
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
|
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
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>
|
/// <summary>
|
||||||
@@ -318,9 +329,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||||
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
|
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
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;
|
_gpUniformBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,9 +428,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">Index of the uniform buffer binding</param>
|
/// <param name="index">Index of the uniform buffer binding</param>
|
||||||
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
||||||
public 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>
|
/// <summary>
|
||||||
@@ -437,9 +450,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="stage">Index of the shader stage</param>
|
/// <param name="stage">Index of the shader stage</param>
|
||||||
/// <param name="index">Index of the uniform buffer binding</param>
|
/// <param name="index">Index of the uniform buffer binding</param>
|
||||||
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
||||||
public 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>
|
/// <summary>
|
||||||
@@ -478,12 +492,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void CommitComputeBindings()
|
public void CommitComputeBindings()
|
||||||
{
|
{
|
||||||
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
|
BindBuffers(_cpStorageBuffers, isStorage: true);
|
||||||
|
BindBuffers(_cpUniformBuffers, isStorage: false);
|
||||||
|
|
||||||
BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
|
CommitBufferTextureBindings();
|
||||||
BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
|
|
||||||
|
|
||||||
CommitBufferTextureBindings(bufferCache);
|
|
||||||
|
|
||||||
// Force rebind after doing compute work.
|
// Force rebind after doing compute work.
|
||||||
Rebind();
|
Rebind();
|
||||||
@@ -495,14 +507,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Commit any queued buffer texture bindings.
|
/// Commit any queued buffer texture bindings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bufferCache">Buffer cache</param>
|
/// <param name="bufferCache">Buffer cache</param>
|
||||||
private void CommitBufferTextureBindings(BufferCache bufferCache)
|
private void CommitBufferTextureBindings()
|
||||||
{
|
{
|
||||||
if (_bufferTextures.Count > 0)
|
if (_bufferTextures.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (BufferTextureBinding binding in _bufferTextures)
|
foreach (BufferTextureBinding binding in _bufferTextures)
|
||||||
{
|
{
|
||||||
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
||||||
BufferRange range = 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);
|
binding.Texture.SetStorage(range);
|
||||||
|
|
||||||
// The texture must be rebound to use the new storage if it was updated.
|
// The texture must be rebound to use the new storage if it was updated.
|
||||||
@@ -526,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
foreach (BufferTextureArrayBinding<ITextureArray> binding in _bufferTextureArrays)
|
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);
|
binding.Texture.SetStorage(range);
|
||||||
|
|
||||||
textureArray[0] = binding.Texture;
|
textureArray[0] = binding.Texture;
|
||||||
@@ -536,7 +548,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
foreach (BufferTextureArrayBinding<IImageArray> binding in _bufferImageArrays)
|
foreach (BufferTextureArrayBinding<IImageArray> binding in _bufferImageArrays)
|
||||||
{
|
{
|
||||||
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
||||||
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
|
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
|
||||||
binding.Texture.SetStorage(range);
|
binding.Texture.SetStorage(range);
|
||||||
|
|
||||||
textureArray[0] = binding.Texture;
|
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>
|
/// <param name="indexed">True if the index buffer is in use</param>
|
||||||
public void CommitGraphicsBindings(bool indexed)
|
public void CommitGraphicsBindings(bool indexed)
|
||||||
{
|
{
|
||||||
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
|
|
||||||
|
|
||||||
if (indexed)
|
if (indexed)
|
||||||
{
|
{
|
||||||
if (_indexBufferDirty || _rebind)
|
if (_indexBufferDirty || _rebind)
|
||||||
@@ -565,14 +575,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (!_indexBuffer.Range.IsUnmapped)
|
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);
|
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!_indexBuffer.Range.IsUnmapped)
|
else if (!_indexBuffer.Range.IsUnmapped)
|
||||||
{
|
{
|
||||||
bufferCache.SynchronizeBufferRange(_indexBuffer.Range);
|
_indexBuffer.BufferCache.SynchronizeBufferRange(_indexBuffer.Range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_rebind)
|
else if (_rebind)
|
||||||
@@ -597,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
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);
|
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
|
||||||
}
|
}
|
||||||
@@ -615,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferCache.SynchronizeBufferRange(vb.Range);
|
vb.BufferCache.SynchronizeBufferRange(vb.Range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,7 +647,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
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);
|
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
|
||||||
@@ -684,7 +694,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
_context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
|
_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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferCache.SynchronizeBufferRange(tfb.Range);
|
tfb.BufferCache.SynchronizeBufferRange(tfb.Range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
_gpStorageBuffersDirty = false;
|
_gpStorageBuffersDirty = false;
|
||||||
|
|
||||||
BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
|
BindBuffers(_gpStorageBuffers, isStorage: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -721,14 +731,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
_gpUniformBuffersDirty = false;
|
_gpUniformBuffersDirty = false;
|
||||||
|
|
||||||
BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
|
BindBuffers(_gpUniformBuffers, isStorage: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UpdateBuffers(_gpUniformBuffers);
|
UpdateBuffers(_gpUniformBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommitBufferTextureBindings(bufferCache);
|
CommitBufferTextureBindings();
|
||||||
|
|
||||||
_rebind = false;
|
_rebind = false;
|
||||||
|
|
||||||
@@ -742,7 +752,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="bindings">Buffer memory ranges to bind</param>
|
/// <param name="bindings">Buffer memory ranges to bind</param>
|
||||||
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
|
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
|
||||||
{
|
{
|
||||||
int rangesCount = 0;
|
int rangesCount = 0;
|
||||||
|
|
||||||
@@ -763,8 +773,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
||||||
BufferRange range = isStorage
|
BufferRange range = isStorage
|
||||||
? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
|
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
|
||||||
: bufferCache.GetBufferRange(bounds.Range, bufferStage);
|
: bounds.BufferCache.GetBufferRange(bounds.Range, bufferStage);
|
||||||
|
|
||||||
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
||||||
}
|
}
|
||||||
@@ -780,11 +790,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bind respective buffer bindings on the host API.
|
/// Bind respective buffer bindings on the host API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
|
|
||||||
/// <param name="buffers">Buffer memory ranges to bind</param>
|
/// <param name="buffers">Buffer memory ranges to bind</param>
|
||||||
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
|
private void BindBuffers(BuffersPerStage buffers, bool isStorage)
|
||||||
{
|
{
|
||||||
int rangesCount = 0;
|
int rangesCount = 0;
|
||||||
|
|
||||||
@@ -800,8 +809,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
||||||
BufferRange range = isStorage
|
BufferRange range = isStorage
|
||||||
? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
|
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
|
||||||
: bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
|
: bounds.BufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
|
||||||
|
|
||||||
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
||||||
}
|
}
|
||||||
@@ -854,7 +863,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
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(
|
public void SetBufferTextureStorage(
|
||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
bool isImage)
|
bool isImage)
|
||||||
{
|
{
|
||||||
_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>
|
/// <summary>
|
||||||
@@ -894,13 +904,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
ITextureArray array,
|
ITextureArray array,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
_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>
|
/// <summary>
|
||||||
@@ -917,13 +928,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
IImageArray array,
|
IImageArray array,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
_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>
|
/// <summary>
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ITexture Texture { get; }
|
public ITexture Texture { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Buffer cache that owns the buffer.
|
||||||
|
/// </summary>
|
||||||
|
public BufferCache BufferCache { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Physical ranges of memory where the buffer texture data is located.
|
/// Physical ranges of memory where the buffer texture data is located.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,18 +44,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="array">Array</param>
|
/// <param name="array">Array</param>
|
||||||
/// <param name="texture">Buffer texture</param>
|
/// <param name="texture">Buffer texture</param>
|
||||||
|
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
|
||||||
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
||||||
/// <param name="bindingInfo">Binding info</param>
|
/// <param name="bindingInfo">Binding info</param>
|
||||||
/// <param name="index">Index of the binding on the array</param>
|
/// <param name="index">Index of the binding on the array</param>
|
||||||
public BufferTextureArrayBinding(
|
public BufferTextureArrayBinding(
|
||||||
T array,
|
T array,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
Array = array;
|
Array = array;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
|
BufferCache = bufferCache;
|
||||||
Range = range;
|
Range = range;
|
||||||
BindingInfo = bindingInfo;
|
BindingInfo = bindingInfo;
|
||||||
Index = index;
|
Index = index;
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ITexture Texture { get; }
|
public ITexture Texture { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Buffer cache that owns the buffer.
|
||||||
|
/// </summary>
|
||||||
|
public BufferCache BufferCache { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Physical ranges of memory where the buffer texture data is located.
|
/// Physical ranges of memory where the buffer texture data is located.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -40,18 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stage">Shader stage accessing the texture</param>
|
/// <param name="stage">Shader stage accessing the texture</param>
|
||||||
/// <param name="texture">Buffer texture</param>
|
/// <param name="texture">Buffer texture</param>
|
||||||
|
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
|
||||||
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
||||||
/// <param name="bindingInfo">Binding info</param>
|
/// <param name="bindingInfo">Binding info</param>
|
||||||
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
||||||
public BufferTextureBinding(
|
public BufferTextureBinding(
|
||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
bool isImage)
|
bool isImage)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
|
BufferCache = bufferCache;
|
||||||
Range = range;
|
Range = range;
|
||||||
BindingInfo = bindingInfo;
|
BindingInfo = bindingInfo;
|
||||||
IsImage = isImage;
|
IsImage = isImage;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
struct IndexBuffer
|
struct IndexBuffer
|
||||||
{
|
{
|
||||||
|
public BufferCache BufferCache;
|
||||||
public MultiRange Range;
|
public MultiRange Range;
|
||||||
public IndexType Type;
|
public IndexType Type;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
@@ -35,10 +36,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
|
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
|
||||||
|
|
||||||
/// <summary>
|
private readonly GpuContext _context;
|
||||||
/// Physical memory where the virtual memory is mapped into.
|
private readonly List<PhysicalMemory> _physicalMemoryList;
|
||||||
/// </summary>
|
private readonly Dictionary<PhysicalMemory, byte> _physicalMemoryMap;
|
||||||
internal PhysicalMemory Physical { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Virtual range cache.
|
/// Virtual range cache.
|
||||||
@@ -53,19 +53,65 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU memory manager.
|
/// Creates a new instance of the GPU memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context</param>
|
||||||
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
|
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
|
||||||
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
|
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
|
||||||
internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize)
|
|
||||||
|
internal MemoryManager(GpuContext context, PhysicalMemory physicalMemory, ulong cpuMemorySize)
|
||||||
{
|
{
|
||||||
Physical = physicalMemory;
|
_context = context;
|
||||||
|
|
||||||
|
_physicalMemoryList = new List<PhysicalMemory>()
|
||||||
|
{
|
||||||
|
physicalMemory
|
||||||
|
};
|
||||||
|
|
||||||
|
_physicalMemoryMap = new Dictionary<PhysicalMemory, byte>
|
||||||
|
{
|
||||||
|
{ physicalMemory, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
VirtualRangeCache = new VirtualRangeCache(this);
|
VirtualRangeCache = new VirtualRangeCache(this);
|
||||||
CounterCache = new CounterCache();
|
CounterCache = new CounterCache();
|
||||||
_pageTable = new ulong[PtLvl0Size][];
|
_pageTable = new ulong[PtLvl0Size][];
|
||||||
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
|
MemoryUnmapped += physicalMemory.TextureCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
MemoryUnmapped += physicalMemory.BufferCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += CounterCache.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>
|
/// <summary>
|
||||||
@@ -81,15 +127,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (IsContiguous(va, size))
|
if (IsContiguous(va, size))
|
||||||
{
|
{
|
||||||
ulong address = Translate(va);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
if (tracked)
|
if (tracked)
|
||||||
{
|
{
|
||||||
return Physical.ReadTracked<T>(address);
|
return physicalMemory.ReadTracked<T>(address);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Physical.Read<T>(address);
|
return physicalMemory.Read<T>(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -113,7 +159,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (IsContiguous(va, size))
|
if (IsContiguous(va, size))
|
||||||
{
|
{
|
||||||
return Physical.GetSpan(Translate(va), size, tracked);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
return physicalMemory.GetSpan(address, size, tracked);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -138,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
bool isContiguous = true;
|
bool isContiguous = true;
|
||||||
int mappedSize;
|
int mappedSize;
|
||||||
|
|
||||||
if (ValidateAddress(va) && GetPte(va) != PteUnmapped && Physical.IsMapped(Translate(va)))
|
if (ValidateAddress(va) && IsMappedOnGpuAndPhysical(va))
|
||||||
{
|
{
|
||||||
ulong endVa = va + (ulong)size;
|
ulong endVa = va + (ulong)size;
|
||||||
ulong endVaAligned = (endVa + PageMask) & ~PageMask;
|
ulong endVaAligned = (endVa + PageMask) & ~PageMask;
|
||||||
@@ -151,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong nextVa = currentVa + PageSize;
|
ulong nextVa = currentVa + PageSize;
|
||||||
ulong nextPa = Translate(nextVa);
|
ulong nextPa = Translate(nextVa);
|
||||||
|
|
||||||
if (!ValidateAddress(nextVa) || GetPte(nextVa) == PteUnmapped || !Physical.IsMapped(nextPa))
|
if (!ValidateAddress(nextVa) || !IsMappedOnGpuAndPhysical(nextVa))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -180,7 +228,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (isContiguous)
|
if (isContiguous)
|
||||||
{
|
{
|
||||||
return Physical.GetSpan(Translate(va), mappedSize, tracked);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
return physicalMemory.GetSpan(address, mappedSize, tracked);
|
||||||
}
|
}
|
||||||
else
|
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>
|
/// <summary>
|
||||||
/// Reads data from a possibly non-contiguous region of GPU mapped memory.
|
/// Reads data from a possibly non-contiguous region of GPU mapped memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -209,22 +276,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
{
|
{
|
||||||
ulong pa = Translate(va);
|
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
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;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; offset < data.Length; 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);
|
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))
|
if (IsContiguous(va, size))
|
||||||
{
|
{
|
||||||
return Physical.GetWritableRegion(Translate(va), size, tracked);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
return physicalMemory.GetWritableRegion(address, size, tracked);
|
||||||
}
|
}
|
||||||
else
|
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>
|
/// <param name="data">The data to be written</param>
|
||||||
public void Write(ulong va, ReadOnlySpan<byte> data)
|
public void Write(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
WriteImpl(va, data, Physical.Write);
|
WriteImpl(va, data, (physical, va, data) => physical.Write(va, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -279,7 +348,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="data">The data to be written</param>
|
/// <param name="data">The data to be written</param>
|
||||||
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
|
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
WriteImpl(va, data, Physical.WriteTrackedResource);
|
WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -289,10 +358,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="data">The data to be written</param>
|
/// <param name="data">The data to be written</param>
|
||||||
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
|
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
WriteImpl(va, data, Physical.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>
|
/// <summary>
|
||||||
/// Writes data to possibly non-contiguous GPU mapped memory.
|
/// Writes data to possibly non-contiguous GPU mapped memory.
|
||||||
@@ -304,7 +373,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (IsContiguous(va, data.Length))
|
if (IsContiguous(va, data.Length))
|
||||||
{
|
{
|
||||||
writeCallback(Translate(va), data);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
writeCallback(physicalMemory, address, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -312,22 +383,67 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
{
|
{
|
||||||
ulong pa = Translate(va);
|
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
writeCallback(pa, data[..size]);
|
writeCallback(physicalMemory, pa, data[..size]);
|
||||||
|
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; offset < data.Length; 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);
|
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="size">Size in bytes of the mapping</param>
|
||||||
/// <param name="kind">Kind of the resource located at the mapping</param>
|
/// <param name="kind">Kind of the resource located at the mapping</param>
|
||||||
public void Map(ulong pa, ulong va, ulong size, PteKind kind)
|
public void Map(ulong pa, ulong va, ulong size, PteKind kind)
|
||||||
|
{
|
||||||
|
MapImpl(pa, va, size, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a given range of pages to the specified CPU virtual address from a different process.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// All addresses and sizes must be page aligned.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="pa">CPU virtual address to map into</param>
|
||||||
|
/// <param name="va">GPU virtual address to be mapped</param>
|
||||||
|
/// <param name="size">Size in bytes of the mapping</param>
|
||||||
|
/// <param name="kind">Kind of the resource located at the mapping</param>
|
||||||
|
/// <param name="ownedPid">PID of the process that owns the mapping</param>
|
||||||
|
public void MapForeign(ulong pa, ulong va, ulong size, PteKind kind, ulong ownedPid)
|
||||||
|
{
|
||||||
|
if (_context.PhysicalMemoryRegistry.TryGetValue(ownedPid, out PhysicalMemory physicalMemory))
|
||||||
|
{
|
||||||
|
MapImpl(pa, va, size, kind, physicalMemory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a given range of pages to the specified CPU virtual address.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// All addresses and sizes must be page aligned.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="pa">CPU virtual address to map into</param>
|
||||||
|
/// <param name="va">GPU virtual address to be mapped</param>
|
||||||
|
/// <param name="size">Size in bytes of the mapping</param>
|
||||||
|
/// <param name="kind">Kind of the resource located at the mapping</param>
|
||||||
|
/// <param name="physicalMemory">Optional physical memory to import for the mapping</param>
|
||||||
|
private void MapImpl(ulong pa, ulong va, ulong size, PteKind kind, PhysicalMemory physicalMemory = null)
|
||||||
{
|
{
|
||||||
lock (_pageTable)
|
lock (_pageTable)
|
||||||
{
|
{
|
||||||
UnmapEventArgs e = new(va, size);
|
UnmapEventArgs e = new(va, size);
|
||||||
MemoryUnmapped?.Invoke(this, e);
|
MemoryUnmapped?.Invoke(this, e);
|
||||||
|
byte pIndex = physicalMemory != null ? GetOrAddPhysicalMemory(physicalMemory) : (byte)0;
|
||||||
|
|
||||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||||
{
|
{
|
||||||
SetPte(va + offset, PackPte(pa + offset, kind));
|
SetPte(va + offset, PackPte(pa + offset, pIndex, kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
RunRemapActions(e);
|
RunRemapActions(e);
|
||||||
@@ -418,12 +570,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
for (int page = 0; page < pages - 1; page++)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Translate(va) + PageSize != Translate(va + PageSize))
|
if (GetPte(va) + PageSize != nextPte)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -457,7 +611,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
int pages = (int)((endVaRounded - va) / PageSize);
|
int pages = (int)((endVaRounded - va) / PageSize);
|
||||||
|
|
||||||
List<MemoryRange> regions = [];
|
List<MemoryRange> regions = new();
|
||||||
|
|
||||||
for (int page = 0; page < pages - 1; page++)
|
for (int page = 0; page < pages - 1; page++)
|
||||||
{
|
{
|
||||||
@@ -535,6 +689,49 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the backing memory for a given GPU virtual address.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address to get the backing memory from</param>
|
||||||
|
/// <returns>The backing memory for the specified GPU virtual address</returns>
|
||||||
|
internal PhysicalMemory GetBackingMemory(ulong va)
|
||||||
|
{
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return GetOwnPhysicalMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _physicalMemoryList[UnpackPIndexFromPte(pte)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the backing memory that is owned by this GPU memory manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The backing memory owned by this memory manager</returns>
|
||||||
|
private PhysicalMemory GetOwnPhysicalMemory()
|
||||||
|
{
|
||||||
|
return _physicalMemoryList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the index for a given physical memory on the list, adding it to the list if needed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="physicalMemory">Physical memory to get the index from</param>
|
||||||
|
/// <returns>The index of the physical memory on the list</returns>
|
||||||
|
private byte GetOrAddPhysicalMemory(PhysicalMemory physicalMemory)
|
||||||
|
{
|
||||||
|
if (!_physicalMemoryMap.TryGetValue(physicalMemory, out byte pIndex))
|
||||||
|
{
|
||||||
|
pIndex = checked((byte)_physicalMemoryList.Count);
|
||||||
|
_physicalMemoryList.Add(physicalMemory);
|
||||||
|
_physicalMemoryMap.Add(physicalMemory, pIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pIndex;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates a GPU virtual address.
|
/// Validates a GPU virtual address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -636,6 +833,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return Math.Min(maxSize, va - startVa);
|
return Math.Min(maxSize, va - startVa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Translates a GPU virtual address to a CPU virtual address and the associated physical memory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address to be translated</param>
|
||||||
|
/// <returns>CPU virtual address with the physical memory, or <see cref="PteUnmapped"/> if unmapped</returns>
|
||||||
|
private (PhysicalMemory, ulong) TranslateWithPhysicalMemory(ulong va)
|
||||||
|
{
|
||||||
|
if (!ValidateAddress(va))
|
||||||
|
{
|
||||||
|
return (GetOwnPhysicalMemory(), PteUnmapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return (GetOwnPhysicalMemory(), PteUnmapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (_physicalMemoryList[UnpackPIndexFromPte(pte)], UnpackPaFromPte(pte) + (va & PageMask));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the kind of a given memory page.
|
/// Gets the kind of a given memory page.
|
||||||
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
|
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
|
||||||
@@ -659,6 +878,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return UnpackKindFromPte(pte);
|
return UnpackKindFromPte(pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsForeignMapping(ulong va)
|
||||||
|
{
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnpackPIndexFromPte(pte) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Page Table entry for a given GPU virtual address.
|
/// Gets the Page Table entry for a given GPU virtual address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -704,11 +935,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Creates a page table entry from a physical address and kind.
|
/// Creates a page table entry from a physical address and kind.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pa">Physical address</param>
|
/// <param name="pa">Physical address</param>
|
||||||
|
/// <param name="pIndex">Index of the physical memory on the list</param>
|
||||||
/// <param name="kind">Kind</param>
|
/// <param name="kind">Kind</param>
|
||||||
/// <returns>Page table entry</returns>
|
/// <returns>Page table entry</returns>
|
||||||
private static ulong PackPte(ulong pa, 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>
|
/// <summary>
|
||||||
@@ -721,6 +953,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return (PteKind)(pte >> 56);
|
return (PteKind)(pte >> 56);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks the physical memory index in the list from a page table entry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pte">Page table entry</param>
|
||||||
|
/// <returns>Physical memory index</returns>
|
||||||
|
private static byte UnpackPIndexFromPte(ulong pte)
|
||||||
|
{
|
||||||
|
return (byte)(pte >> 48);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unpacks physical address from a page table entry.
|
/// Unpacks physical address from a page table entry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -728,7 +970,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <returns>Physical address</returns>
|
/// <returns>Physical address</returns>
|
||||||
private static ulong UnpackPaFromPte(ulong pte)
|
private static ulong UnpackPaFromPte(ulong pte)
|
||||||
{
|
{
|
||||||
return pte & 0xffffffffffffffUL;
|
return pte & 0xffffffffffffUL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
struct VertexBuffer
|
struct VertexBuffer
|
||||||
{
|
{
|
||||||
|
public BufferCache BufferCache;
|
||||||
public MultiRange Range;
|
public MultiRange Range;
|
||||||
public int Stride;
|
public int Stride;
|
||||||
public int Divisor;
|
public int Divisor;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using System;
|
using System;
|
||||||
@@ -66,11 +67,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public uint ConstantBuffer1Read(int offset)
|
public uint ConstantBuffer1Read(int offset)
|
||||||
{
|
{
|
||||||
ulong baseAddress = _compute
|
(PhysicalMemory physical, ulong baseAddress) = _compute
|
||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(1)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(1)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1);
|
||||||
|
|
||||||
return _channel.MemoryManager.Physical.Read<uint>(baseAddress + (ulong)offset);
|
return physical.Read<uint>(baseAddress + (ulong)offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|||||||
@@ -733,14 +733,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
byte[] codeB,
|
byte[] codeB,
|
||||||
bool asCompute)
|
bool asCompute)
|
||||||
{
|
{
|
||||||
ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
|
(PhysicalMemory physical, ulong cb1DataAddress) = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
|
||||||
|
|
||||||
MemoryManager memoryManager = channel.MemoryManager;
|
MemoryManager memoryManager = channel.MemoryManager;
|
||||||
|
|
||||||
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
|
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
|
||||||
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
|
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
|
||||||
byte[] cb1DataA = ReadArray(memoryManager, cb1DataAddress, vertexA.Cb1DataSize);
|
byte[] cb1DataA = ReadArray(physical, cb1DataAddress, vertexA.Cb1DataSize);
|
||||||
byte[] cb1DataB = ReadArray(memoryManager, cb1DataAddress, currentStage.Cb1DataSize);
|
byte[] cb1DataB = ReadArray(physical, cb1DataAddress, currentStage.Cb1DataSize);
|
||||||
|
|
||||||
ShaderDumpPaths pathsA = default;
|
ShaderDumpPaths pathsA = default;
|
||||||
ShaderDumpPaths pathsB = default;
|
ShaderDumpPaths pathsB = default;
|
||||||
@@ -775,11 +775,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
MemoryManager memoryManager = channel.MemoryManager;
|
MemoryManager memoryManager = channel.MemoryManager;
|
||||||
|
|
||||||
ulong cb1DataAddress = context.Stage == ShaderStage.Compute
|
(PhysicalMemory physical, ulong cb1DataAddress) = context.Stage == ShaderStage.Compute
|
||||||
? channel.BufferManager.GetComputeUniformBufferAddress(1)
|
? channel.BufferManager.GetComputeUniformBufferAddress(1)
|
||||||
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
|
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
|
||||||
|
|
||||||
byte[] cb1Data = ReadArray(memoryManager, cb1DataAddress, context.Cb1DataSize);
|
byte[] cb1Data = ReadArray(physical, cb1DataAddress, context.Cb1DataSize);
|
||||||
code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
|
code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
|
||||||
|
|
||||||
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
|
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
|
||||||
@@ -793,18 +793,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0.
|
/// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="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="address">Physical address of the region to read</param>
|
||||||
/// <param name="size">Size in bytes of the data</param>
|
/// <param name="size">Size in bytes of the data</param>
|
||||||
/// <returns>An array with the data at the specified memory location</returns>
|
/// <returns>An array with the data at the specified memory location</returns>
|
||||||
private static byte[] ReadArray(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 [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return memoryManager.Physical.GetSpan(address, size).ToArray();
|
return physicalMemory.GetSpan(address, size).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
|
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
|
||||||
cachedTextureBufferIndex = textureBufferIndex;
|
cachedTextureBufferIndex = textureBufferIndex;
|
||||||
|
|
||||||
if (samplerBufferIndex == textureBufferIndex)
|
if (samplerBufferIndex == textureBufferIndex)
|
||||||
@@ -710,7 +710,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
|
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
|
||||||
cachedSamplerBufferIndex = samplerBufferIndex;
|
cachedSamplerBufferIndex = samplerBufferIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,18 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
|
using Texture = Image.Texture;
|
||||||
|
|
||||||
|
public record TextureData(int Width, int Height, byte[] Data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GPU image presentation window.
|
/// GPU image presentation window.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Window
|
public class Window
|
||||||
{
|
{
|
||||||
|
private const int CaptureTextureWidth = 1280;
|
||||||
|
private const int CaptureTextureHeight = 720;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -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 readonly ConcurrentQueue<PresentationTexture> _frameQueue;
|
||||||
|
private PresentedTexture _lastPresentedTexture;
|
||||||
|
private ITexture _captureTexture;
|
||||||
|
|
||||||
private int _framesAvailable;
|
private int _framesAvailable;
|
||||||
|
|
||||||
@@ -188,6 +209,51 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextureData GetLastPresentedData()
|
||||||
|
{
|
||||||
|
PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture);
|
||||||
|
|
||||||
|
if (pt != null)
|
||||||
|
{
|
||||||
|
byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, pt.Crop);
|
||||||
|
|
||||||
|
int size = SizeCalculator.GetBlockLinearTextureSize(
|
||||||
|
CaptureTextureWidth,
|
||||||
|
CaptureTextureHeight,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
16,
|
||||||
|
1,
|
||||||
|
1).TotalSize;
|
||||||
|
|
||||||
|
byte[] data = new byte[size];
|
||||||
|
|
||||||
|
LayoutConverter.ConvertLinearToBlockLinear(data, CaptureTextureWidth, CaptureTextureHeight, CaptureTextureWidth * 4, 4, 16, inputData);
|
||||||
|
|
||||||
|
return new TextureData(CaptureTextureWidth, CaptureTextureHeight, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextureData(0, 0, Array.Empty<byte>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureData GetLastPresentedDataLinear()
|
||||||
|
{
|
||||||
|
PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture);
|
||||||
|
|
||||||
|
if (pt != null)
|
||||||
|
{
|
||||||
|
byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, new ImageCrop());
|
||||||
|
|
||||||
|
return new TextureData(pt.Texture.Info.Width, pt.Texture.Info.Height, inputData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextureData(0, 0, Array.Empty<byte>());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Presents a texture on the queue.
|
/// Presents a texture on the queue.
|
||||||
/// If the queue is empty, then no texture is presented.
|
/// If the queue is empty, then no texture is presented.
|
||||||
@@ -205,6 +271,10 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
|
|
||||||
pt.Cache.Tick();
|
pt.Cache.Tick();
|
||||||
|
|
||||||
|
EnsureCaptureTexture();
|
||||||
|
|
||||||
|
Volatile.Write(ref _lastPresentedTexture, new PresentedTexture(texture, pt.Crop));
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
ImageCrop crop = new(
|
ImageCrop crop = new(
|
||||||
@@ -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>
|
/// <summary>
|
||||||
/// Indicate that a frame on the queue is ready to be acquired.
|
/// Indicate that a frame on the queue is ready to be acquired.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Applets.Error
|
namespace Ryujinx.HLE.HOS.Applets.Error
|
||||||
{
|
{
|
||||||
@@ -159,13 +158,15 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
|||||||
|
|
||||||
string[] buttons = GetButtonsText(module, description, "DlgBtn");
|
string[] buttons = GetButtonsText(module, description, "DlgBtn");
|
||||||
|
|
||||||
bool showDetails = _horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Code: {module}-{description:0000}", "\n" + message, buttons);
|
(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);
|
||||||
if (showDetails)
|
if (showDetails)
|
||||||
{
|
{
|
||||||
message = GetMessageText(module, description, "FlvMsg");
|
message = GetMessageText(module, description, "FlvMsg");
|
||||||
buttons = GetButtonsText(module, description, "FlvBtn");
|
buttons = GetButtonsText(module, description, "FlvBtn");
|
||||||
|
|
||||||
_horizon.Device.UIHandler.DisplayErrorAppletDialog($"Details: {module}-{description:0000}", "\n" + message, buttons);
|
_horizon.Device.UIHandler.DisplayErrorAppletDialog($"Details: {module}-{description:0000}", "\n" + message, buttons, errorCodeTuple);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
private readonly ICpuContext _cpuContext;
|
private readonly ICpuContext _cpuContext;
|
||||||
private T _memoryManager;
|
private T _memoryManager;
|
||||||
|
|
||||||
public IVirtualMemoryManager AddressSpace => _memoryManager;
|
public IVirtualMemoryManagerTracked AddressSpace => _memoryManager;
|
||||||
|
|
||||||
public ulong AddressSpaceSize { get; }
|
public ulong AddressSpaceSize { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
internal ServerBase TimeServer { get; private set; }
|
internal ServerBase TimeServer { get; private set; }
|
||||||
internal ServerBase ViServer { get; private set; }
|
internal ServerBase ViServer { get; private set; }
|
||||||
internal ServerBase ViServerM { 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 ServerBase LdnServer { get; private set; }
|
||||||
|
|
||||||
internal KSharedMemory HidSharedMem { get; private set; }
|
internal KSharedMemory HidSharedMem { get; private set; }
|
||||||
@@ -254,7 +254,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
TimeServer = new ServerBase(KernelContext, "TimeServer");
|
TimeServer = new ServerBase(KernelContext, "TimeServer");
|
||||||
ViServer = new ServerBase(KernelContext, "ViServerU");
|
ViServer = new ServerBase(KernelContext, "ViServerU");
|
||||||
ViServerM = new ServerBase(KernelContext, "ViServerM");
|
ViServerM = new ServerBase(KernelContext, "ViServerM");
|
||||||
ViServerS = new ServerBase(KernelContext, "ViServerS");
|
ViServerS = new ViServer(KernelContext, "ViServerS");
|
||||||
LdnServer = new ServerBase(KernelContext, "LdnServer");
|
LdnServer = new ServerBase(KernelContext, "LdnServer");
|
||||||
|
|
||||||
StartNewServices();
|
StartNewServices();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
interface IProcessContext : IDisposable
|
interface IProcessContext : IDisposable
|
||||||
{
|
{
|
||||||
IVirtualMemoryManager AddressSpace { get; }
|
IVirtualMemoryManagerTracked AddressSpace { get; }
|
||||||
|
|
||||||
ulong AddressSpaceSize { get; }
|
ulong AddressSpaceSize { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
private IProcessContextFactory _contextFactory;
|
private IProcessContextFactory _contextFactory;
|
||||||
public IProcessContext Context { get; private set; }
|
public IProcessContext Context { get; private set; }
|
||||||
public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
|
public IVirtualMemoryManagerTracked CpuMemory => Context.AddressSpace;
|
||||||
|
|
||||||
public HleProcessDebugger Debugger { get; private set; }
|
public HleProcessDebugger Debugger { get; private set; }
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
class ProcessContext : IProcessContext
|
class ProcessContext : IProcessContext
|
||||||
{
|
{
|
||||||
public IVirtualMemoryManager AddressSpace { get; }
|
public IVirtualMemoryManagerTracked AddressSpace { get; }
|
||||||
|
|
||||||
public ulong AddressSpaceSize { get; }
|
public ulong AddressSpaceSize { get; }
|
||||||
|
|
||||||
public ProcessContext(IVirtualMemoryManager asManager, ulong addressSpaceSize)
|
public ProcessContext(IVirtualMemoryManagerTracked asManager, ulong addressSpaceSize)
|
||||||
{
|
{
|
||||||
AddressSpace = asManager;
|
AddressSpace = asManager;
|
||||||
AddressSpaceSize = addressSpaceSize;
|
AddressSpaceSize = addressSpaceSize;
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.OverlayAppletProxy;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
|
||||||
|
{
|
||||||
|
class IOverlayAppletProxy : IpcService
|
||||||
|
{
|
||||||
|
private readonly ulong _pid;
|
||||||
|
|
||||||
|
public IOverlayAppletProxy(ulong pid)
|
||||||
|
{
|
||||||
|
_pid = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(0)]
|
||||||
|
// GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter>
|
||||||
|
public ResultCode GetCommonStateGetter(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new ICommonStateGetter(context));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1)]
|
||||||
|
// GetSelfController() -> object<nn::am::service::ISelfController>
|
||||||
|
public ResultCode GetSelfController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new ISelfController(context, _pid));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(2)]
|
||||||
|
// GetWindowController() -> object<nn::am::service::IWindowController>
|
||||||
|
public ResultCode GetWindowController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IWindowController(_pid));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(3)]
|
||||||
|
// GetAudioController() -> object<nn::am::service::IAudioController>
|
||||||
|
public ResultCode GetAudioController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IAudioController());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(4)]
|
||||||
|
// GetDisplayController() -> object<nn::am::service::IDisplayController>
|
||||||
|
public ResultCode GetDisplayController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IDisplayController(context));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(11)]
|
||||||
|
// GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator>
|
||||||
|
public ResultCode GetLibraryAppletCreator(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new ILibraryAppletCreator());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(20)]
|
||||||
|
// GetOverlayFunctions() -> object<nn::am::service::IOverlayFunctions>
|
||||||
|
public ResultCode GetOverlayFunctions(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IOverlayFunctions(context.Device.System));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(21)]
|
||||||
|
// GetAppletCommonFunctions() -> object<nn::am::service::IAppletCommonFunctions>
|
||||||
|
public ResultCode GetAppletCommonFunctions(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IAppletCommonFunctions());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(23)]
|
||||||
|
// GetGlobalStateController() -> object<nn::am::service::IGlobalStateController>
|
||||||
|
public ResultCode GetGlobalStateController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IGlobalStateController());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1000)]
|
||||||
|
// GetDebugFunctions() -> object<nn::am::service::IDebugFunctions>
|
||||||
|
public ResultCode GetDebugFunctions(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IDebugFunctions());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
using LibHac.Util;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.OverlayAppletProxy
|
||||||
|
{
|
||||||
|
class IOverlayFunctions : IpcService
|
||||||
|
{
|
||||||
|
|
||||||
|
public IOverlayFunctions(Horizon system)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(0)]
|
||||||
|
// BeginToWatchShortHomeButtonMessage()
|
||||||
|
public ResultCode BeginToWatchShortHomeButtonMessage(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1)]
|
||||||
|
// EndToWatchShortHomeButtonMessage()
|
||||||
|
public ResultCode EndToWatchShortHomeButtonMessage(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(2)]
|
||||||
|
// GetApplicationIdForLogo() -> nn::ncm::ApplicationId
|
||||||
|
public ResultCode GetApplicationIdForLogo(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
context.ResponseData.Write(0L);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(3)]
|
||||||
|
// SetGpuTimeSliceBoost(u64)
|
||||||
|
public ResultCode SetGpuTimeSliceBoost(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(4)]
|
||||||
|
// SetAutoSleepTimeAndDimmingTimeEnabled(u8)
|
||||||
|
public ResultCode SetAutoSleepTimeAndDimmingTimeEnabled(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(5)]
|
||||||
|
// TerminateApplicationAndSetReason(u32)
|
||||||
|
public ResultCode TerminateApplicationAndSetReason(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(6)]
|
||||||
|
// SetScreenShotPermissionGlobally(u8)
|
||||||
|
public ResultCode SetScreenShotPermissionGlobally(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(10)]
|
||||||
|
// StartShutdownSequenceForOverlay()
|
||||||
|
public ResultCode StartShutdownSequenceForOverlay(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(11)]
|
||||||
|
// StartRebootSequenceForOverlay()
|
||||||
|
public ResultCode StartRebootSequenceForOverlay(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(20)]
|
||||||
|
// SetHandlingHomeButtonShortPressedEnabled(u8)
|
||||||
|
public ResultCode SetHandlingHomeButtonShortPressedEnabled(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(21)]
|
||||||
|
// SetHandlingTouchScreenInputEnabled(u8)
|
||||||
|
public ResultCode SetHandlingTouchScreenInputEnabled(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(30)]
|
||||||
|
// SetHealthWarningShowingState(u8)
|
||||||
|
public ResultCode SetHealthWarningShowingState(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(31)]
|
||||||
|
// IsHealthWarningRequired() -> bool
|
||||||
|
public ResultCode IsHealthWarningRequired(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
context.ResponseData.Write(false);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(90)]
|
||||||
|
// SetRequiresGpuResourceUse(u8)
|
||||||
|
public ResultCode SetRequiresGpuResourceUse(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(101)]
|
||||||
|
// BeginToObserveHidInputForDevelop()
|
||||||
|
public ResultCode BeginToObserveHidInputForDevelop(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,5 +102,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
|
|
||||||
return ResultCode.Success;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,7 +225,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
public ResultCode CreateManagedDisplayLayer(ServiceCtx context)
|
public ResultCode CreateManagedDisplayLayer(ServiceCtx context)
|
||||||
{
|
{
|
||||||
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, _pid);
|
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, _pid);
|
||||||
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
|
|
||||||
|
|
||||||
context.ResponseData.Write(layerId);
|
context.ResponseData.Write(layerId);
|
||||||
|
|
||||||
@@ -236,9 +235,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
// IsSystemBufferSharingEnabled()
|
// IsSystemBufferSharingEnabled()
|
||||||
public ResultCode IsSystemBufferSharingEnabled(ServiceCtx context)
|
public ResultCode IsSystemBufferSharingEnabled(ServiceCtx context)
|
||||||
{
|
{
|
||||||
// NOTE: Service checks a private field and return an error if the SystemBufferSharing is disabled.
|
// TODO: Implement this once we have a way to check if we're not an AppletId.Application
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
return ResultCode.NotImplemented;
|
[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+
|
[CommandCmif(44)] // 10.0.0+
|
||||||
@@ -247,7 +255,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
{
|
{
|
||||||
context.Device.System.SurfaceFlinger.CreateLayer(out long displayLayerId, _pid);
|
context.Device.System.SurfaceFlinger.CreateLayer(out long displayLayerId, _pid);
|
||||||
context.Device.System.SurfaceFlinger.CreateLayer(out long recordingLayerId, _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(displayLayerId);
|
||||||
context.ResponseData.Write(recordingLayerId);
|
context.ResponseData.Write(recordingLayerId);
|
||||||
|
|||||||
@@ -27,6 +27,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(300)]
|
||||||
|
// OpenOverlayAppletProxy(pid, handle<copy>) -> object<nn::am::service::IOverlayAppletProxy>
|
||||||
|
public ResultCode OpenOverlayAppletProxy(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IOverlayAppletProxy(context.Request.HandleDesc.PId));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(350)]
|
[CommandCmif(350)]
|
||||||
// OpenSystemApplicationProxy(u64, pid, handle<copy>) -> object<nn::am::service::IApplicationProxy>
|
// OpenSystemApplicationProxy(u64, pid, handle<copy>) -> object<nn::am::service::IApplicationProxy>
|
||||||
public ResultCode OpenSystemApplicationProxy(ServiceCtx context)
|
public ResultCode OpenSystemApplicationProxy(ServiceCtx context)
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new();
|
public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new();
|
||||||
public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new();
|
public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new();
|
||||||
|
|
||||||
|
internal PlayerIndex LastActiveNpad { get; set; }
|
||||||
|
|
||||||
public NpadDevices(Switch device, bool active = true) : base(device, active)
|
public NpadDevices(Switch device, bool active = true) : base(device, active)
|
||||||
{
|
{
|
||||||
_configuredTypes = new ControllerType[MaxControllers];
|
_configuredTypes = new ControllerType[MaxControllers];
|
||||||
@@ -384,6 +386,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LastActiveNpad = state.PlayerId;
|
||||||
|
|
||||||
ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
|
ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
|
||||||
|
|
||||||
NpadCommonState newState = new()
|
NpadCommonState newState = new()
|
||||||
@@ -639,5 +643,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
return rumbleQueue;
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
// GetLastActiveNpad(u32) -> u8, u8
|
// GetLastActiveNpad(u32) -> u8, u8
|
||||||
public ResultCode GetLastActiveNpad(ServiceCtx context)
|
public ResultCode GetLastActiveNpad(ServiceCtx context)
|
||||||
{
|
{
|
||||||
// TODO: RequestData seems to have garbage data, reading an extra uint seems to fix the issue.
|
context.ResponseData.Write((byte)context.Device.Hid.Npads.GetLastActiveNpadId());
|
||||||
context.RequestData.ReadUInt32();
|
|
||||||
|
|
||||||
ResultCode resultCode = GetAppletFooterUiTypeImpl(context, out AppletFooterUiType appletFooterUiType);
|
return ResultCode.Success;
|
||||||
|
|
||||||
context.ResponseData.Write((byte)appletFooterUiType);
|
|
||||||
context.ResponseData.Write((byte)0);
|
|
||||||
|
|
||||||
return resultCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandCmif(307)]
|
[CommandCmif(307)]
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
|
|||||||
public async Task<ushort> NatPunch()
|
public async Task<ushort> NatPunch()
|
||||||
{
|
{
|
||||||
NatDiscoverer discoverer = new();
|
NatDiscoverer discoverer = new();
|
||||||
CancellationTokenSource cts = new(5000);
|
CancellationTokenSource cts = new(2500);
|
||||||
|
|
||||||
NatDevice device;
|
NatDevice device;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
@@ -105,6 +106,26 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
return ResultCode.Success;
|
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)]
|
[CommandCmif(11)]
|
||||||
// SetConnectionConfirmationOption(i8)
|
// SetConnectionConfirmationOption(i8)
|
||||||
public ResultCode SetConnectionConfirmationOption(ServiceCtx context)
|
public ResultCode SetConnectionConfirmationOption(ServiceCtx context)
|
||||||
@@ -142,5 +163,38 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
|
|
||||||
return ResultCode.Success;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments.NvMapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(arguments.NvMapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
{
|
{
|
||||||
if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -301,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
|
_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;
|
arguments.Offset = va;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +380,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
ulong mapOffs = (ulong)argument.MapOffset << 16;
|
ulong mapOffs = (ulong)argument.MapOffset << 16;
|
||||||
PteKind kind = (PteKind)argument.Kind;
|
PteKind kind = (PteKind)argument.Kind;
|
||||||
|
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, nvmapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(nvmapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -389,13 +389,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
return NvInternalResult.InvalidInput;
|
return NvInternalResult.InvalidInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
gmm.Map(mapOffs + map.Address, gpuVa, size, kind);
|
Map(mapOffs + map.Address, gpuVa, size, kind, map.OwnerPid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NvInternalResult.Success;
|
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() { }
|
public override void Close() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
foreach (CommandBuffer commandBuffer in commandBuffers)
|
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);
|
ReadOnlySpan<byte> data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
||||||
{
|
{
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBufferEntry.MapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -279,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
||||||
{
|
{
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBufferEntry.MapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
uint size = BitUtils.AlignUp(arguments.Size, (uint)MemoryManager.PageSize);
|
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}!");
|
Logger.Debug?.Print(LogClass.ServiceNv, $"Created map {arguments.Handle} with size 0x{size:x8}!");
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult FromId(ref NvMapFromId arguments)
|
private NvInternalResult FromId(ref NvMapFromId arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Id);
|
NvMapHandle map = GetMapFromHandle(arguments.Id);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult Alloc(ref NvMapAlloc arguments)
|
private NvInternalResult Alloc(ref NvMapAlloc arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
|
NvMapHandle map = GetMapFromHandle(arguments.Handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -152,7 +152,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult Free(ref NvMapFree arguments)
|
private NvInternalResult Free(ref NvMapFree arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
|
NvMapHandle map = GetMapFromHandle(arguments.Handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -179,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult Param(ref NvMapParam arguments)
|
private NvInternalResult Param(ref NvMapParam arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
|
NvMapHandle map = GetMapFromHandle(arguments.Handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -217,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult GetId(ref NvMapGetId arguments)
|
private NvInternalResult GetId(ref NvMapGetId arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
|
NvMapHandle map = GetMapFromHandle(arguments.Handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -237,24 +237,29 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
// _maps.TryRemove(GetOwner(), out _);
|
// _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);
|
return _maps.Add(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool DeleteMapWithHandle(ulong pid, int handle)
|
private static bool DeleteMapWithHandle(int handle)
|
||||||
{
|
{
|
||||||
return _maps.Delete(handle) != null;
|
return _maps.Delete(handle) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void IncrementMapRefCount(ulong pid, int handle)
|
public static void IncrementMapRefCount(ulong pid, int handle)
|
||||||
{
|
{
|
||||||
GetMapFromHandle(pid, handle)?.IncrementRefCount();
|
GetMapFromHandle(handle)?.IncrementRefCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool DecrementMapRefCount(ulong pid, int handle)
|
public static bool DecrementMapRefCount(ulong pid, int handle)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(pid, handle);
|
NvMapHandle map = GetMapFromHandle(handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -263,7 +268,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
if (map.DecrementRefCount() <= 0)
|
if (map.DecrementRefCount() <= 0)
|
||||||
{
|
{
|
||||||
DeleteMapWithHandle(pid, handle);
|
DeleteMapWithHandle(handle);
|
||||||
|
|
||||||
Logger.Debug?.Print(LogClass.ServiceNv, $"Deleted map {handle}!");
|
Logger.Debug?.Print(LogClass.ServiceNv, $"Deleted map {handle}!");
|
||||||
|
|
||||||
@@ -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);
|
return _maps.Get(handle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
public ulong Address;
|
public ulong Address;
|
||||||
public bool Allocated;
|
public bool Allocated;
|
||||||
public ulong DmaMapAddress;
|
public ulong DmaMapAddress;
|
||||||
|
public ulong OwnerPid;
|
||||||
|
|
||||||
private long _dupes;
|
private long _dupes;
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,15 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
ServerLoop();
|
ServerLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual ulong CalculateRequiredHeapSize()
|
||||||
|
{
|
||||||
|
return 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void CustomInit(KernelContext context, ulong pid, ulong heapAddress)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private void ServerLoop()
|
private void ServerLoop()
|
||||||
{
|
{
|
||||||
_selfProcess = KernelStatic.GetCurrentProcess();
|
_selfProcess = KernelStatic.GetCurrentProcess();
|
||||||
@@ -197,8 +206,12 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
|
|
||||||
InitDone.Set();
|
InitDone.Set();
|
||||||
|
|
||||||
|
ulong heapSize = CalculateRequiredHeapSize() + PointerBufferSize;
|
||||||
|
|
||||||
ulong messagePtr = _selfThread.TlsAddress;
|
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 + 0x0, 0);
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||||||
{
|
{
|
||||||
if (errorCode != LinuxError.SUCCESS)
|
if (errorCode != LinuxError.SUCCESS)
|
||||||
{
|
{
|
||||||
|
if (errorCode != LinuxError.EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Operation failed with error {errorCode}.");
|
||||||
|
}
|
||||||
result = -1;
|
result = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +70,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||||||
BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32();
|
BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32();
|
||||||
ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32();
|
ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Creating socket with domain={domain}, type={type}, protocol={protocol}");
|
||||||
|
|
||||||
BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift);
|
BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift);
|
||||||
type &= BsdSocketType.TypeMask;
|
type &= BsdSocketType.TypeMask;
|
||||||
|
|
||||||
@@ -95,12 +101,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId)
|
|
||||||
{
|
|
||||||
Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking),
|
|
||||||
};
|
|
||||||
|
|
||||||
LinuxError errno = LinuxError.SUCCESS;
|
LinuxError errno = LinuxError.SUCCESS;
|
||||||
|
ISocket newBsdSocket;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId)
|
||||||
|
{
|
||||||
|
Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (SocketException exception)
|
||||||
|
{
|
||||||
|
LinuxError errNo = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
|
return WriteBsdResult(context, 0, errNo);
|
||||||
|
}
|
||||||
|
|
||||||
int newSockFd = _context.RegisterFileDescriptor(newBsdSocket);
|
int newSockFd = _context.RegisterFileDescriptor(newBsdSocket);
|
||||||
|
|
||||||
@@ -111,6 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||||||
|
|
||||||
if (exempt)
|
if (exempt)
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.ServiceBsd, "Disconnecting exempt socket.");
|
||||||
newBsdSocket.Disconnect();
|
newBsdSocket.Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,6 +813,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||||||
{
|
{
|
||||||
errno = socket.Listen(backlog);
|
errno = socket.Listen(backlog);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Invalid socket fd '{socketFd}'.");
|
||||||
|
}
|
||||||
|
|
||||||
return WriteBsdResult(context, 0, errno);
|
return WriteBsdResult(context, 0, errno);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,18 +92,30 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
{
|
{
|
||||||
newSocket = new ManagedSocket(Socket.Accept());
|
newSocket = new ManagedSocket(Socket.Accept());
|
||||||
|
|
||||||
|
IPEndPoint remoteEndPoint = newSocket.RemoteEndPoint;
|
||||||
|
bool isPrivateIp = remoteEndPoint.Address.ToString().StartsWith("192.168.");
|
||||||
|
Logger.Info?.PrintMsg(LogClass.ServiceBsd,
|
||||||
|
isPrivateIp
|
||||||
|
? $"Accepted connection from {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}"
|
||||||
|
: $"Accepted connection from {ProtocolType}/***:{remoteEndPoint.Port}");
|
||||||
|
|
||||||
return LinuxError.SUCCESS;
|
return LinuxError.SUCCESS;
|
||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
newSocket = null;
|
newSocket = null;
|
||||||
|
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinuxError Bind(IPEndPoint localEndPoint)
|
public LinuxError Bind(IPEndPoint localEndPoint)
|
||||||
{
|
{
|
||||||
|
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket binding to: {ProtocolType}/{localEndPoint.Port}");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Socket.Bind(localEndPoint);
|
Socket.Bind(localEndPoint);
|
||||||
@@ -112,6 +124,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,6 +139,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
|
|
||||||
public LinuxError Connect(IPEndPoint remoteEndPoint)
|
public LinuxError Connect(IPEndPoint remoteEndPoint)
|
||||||
{
|
{
|
||||||
|
bool isLDNPrivateIP = remoteEndPoint.Address.ToString().StartsWith("192.168.");
|
||||||
|
if (isLDNPrivateIP)
|
||||||
|
{
|
||||||
|
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/***:{remoteEndPoint.Port}");
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Socket.Connect(remoteEndPoint);
|
Socket.Connect(remoteEndPoint);
|
||||||
@@ -137,6 +162,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,11 +173,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
|
|
||||||
public void Disconnect()
|
public void Disconnect()
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.ServiceBsd, "Socket disconnecting");
|
||||||
Socket.Disconnect(true);
|
Socket.Disconnect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.ServiceBsd, "Socket closed");
|
||||||
Socket.Close();
|
Socket.Close();
|
||||||
Socket.Dispose();
|
Socket.Dispose();
|
||||||
}
|
}
|
||||||
@@ -159,10 +190,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
{
|
{
|
||||||
Socket.Listen(backlog);
|
Socket.Listen(backlog);
|
||||||
|
|
||||||
|
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket listening: {ProtocolType}/{(Socket.LocalEndPoint as IPEndPoint).Port}");
|
||||||
|
|
||||||
return LinuxError.SUCCESS;
|
return LinuxError.SUCCESS;
|
||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,11 +219,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasEmittedBlockingWarning = false;
|
private bool _hasEmittedBlockingWarning;
|
||||||
|
|
||||||
public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags)
|
public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags)
|
||||||
{
|
{
|
||||||
@@ -202,10 +243,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
shouldBlockAfterOperation = true;
|
shouldBlockAfterOperation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Blocking && !hasEmittedBlockingWarning)
|
if (Blocking && !_hasEmittedBlockingWarning)
|
||||||
{
|
{
|
||||||
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
|
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
|
||||||
hasEmittedBlockingWarning = true;
|
_hasEmittedBlockingWarning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
|
receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
|
||||||
@@ -214,6 +255,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
receiveSize = -1;
|
receiveSize = -1;
|
||||||
|
|
||||||
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
@@ -245,10 +290,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
shouldBlockAfterOperation = true;
|
shouldBlockAfterOperation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Blocking && !hasEmittedBlockingWarning)
|
if (Blocking && !_hasEmittedBlockingWarning)
|
||||||
{
|
{
|
||||||
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
|
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
|
||||||
hasEmittedBlockingWarning = true;
|
_hasEmittedBlockingWarning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Socket.IsBound)
|
if (!Socket.IsBound)
|
||||||
@@ -265,6 +310,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
receiveSize = -1;
|
receiveSize = -1;
|
||||||
|
|
||||||
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
@@ -288,6 +337,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
sendSize = -1;
|
sendSize = -1;
|
||||||
|
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
@@ -304,6 +357,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
sendSize = -1;
|
sendSize = -1;
|
||||||
|
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
@@ -341,6 +398,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,6 +448,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,6 +584,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,6 +626,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
}
|
}
|
||||||
catch (SocketException exception)
|
catch (SocketException exception)
|
||||||
{
|
{
|
||||||
|
if (exception.SocketErrorCode != SocketError.WouldBlock)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
|
||||||
|
}
|
||||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
{ BsdSocketOption.SoLinger, SocketOptionName.Linger },
|
{ BsdSocketOption.SoLinger, SocketOptionName.Linger },
|
||||||
{ BsdSocketOption.SoOobInline, SocketOptionName.OutOfBandInline },
|
{ BsdSocketOption.SoOobInline, SocketOptionName.OutOfBandInline },
|
||||||
{ BsdSocketOption.SoReusePort, SocketOptionName.ReuseAddress },
|
{ BsdSocketOption.SoReusePort, SocketOptionName.ReuseAddress },
|
||||||
|
{ BsdSocketOption.SoNoSigpipe, SocketOptionName.DontLinger },
|
||||||
{ BsdSocketOption.SoSndBuf, SocketOptionName.SendBuffer },
|
{ BsdSocketOption.SoSndBuf, SocketOptionName.SendBuffer },
|
||||||
{ BsdSocketOption.SoRcvBuf, SocketOptionName.ReceiveBuffer },
|
{ BsdSocketOption.SoRcvBuf, SocketOptionName.ReceiveBuffer },
|
||||||
{ BsdSocketOption.SoSndLoWat, SocketOptionName.SendLowWater },
|
{ BsdSocketOption.SoSndLoWat, SocketOptionName.SendLowWater },
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
|
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -64,10 +66,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy
|
|||||||
{
|
{
|
||||||
if (_proxy.Supported(domain, type, protocol))
|
if (_proxy.Supported(domain, type, protocol))
|
||||||
{
|
{
|
||||||
|
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket is using LDN proxy");
|
||||||
return new LdnProxySocket(domain, type, protocol, _proxy);
|
return new LdnProxySocket(domain, type, protocol, _proxy);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"LDN proxy does not support socket {domain}, {type}, {protocol}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Opening socket using host networking stack");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DefaultSocket(domain, type, protocol, lanInterfaceId);
|
return new DefaultSocket(domain, type, protocol, lanInterfaceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Common.PreciseSleep;
|
using Ryujinx.Common.PreciseSleep;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -38,8 +39,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
private readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
public long RenderLayerId { get; private set; }
|
|
||||||
|
|
||||||
private class Layer
|
private class Layer
|
||||||
{
|
{
|
||||||
public int ProducerBinderId;
|
public int ProducerBinderId;
|
||||||
@@ -60,7 +59,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
_layers = new Dictionary<long, Layer>();
|
_layers = new Dictionary<long, Layer>();
|
||||||
RenderLayerId = 0;
|
|
||||||
|
|
||||||
_composerThread = new Thread(HandleComposition)
|
_composerThread = new Thread(HandleComposition)
|
||||||
{
|
{
|
||||||
@@ -239,34 +237,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
private void CloseLayer(long layerId, Layer layer)
|
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)
|
if (layer.State == LayerState.ManagedOpened)
|
||||||
{
|
{
|
||||||
layer.State = LayerState.ManagedClosed;
|
layer.State = LayerState.ManagedClosed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRenderLayer(long layerId)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
RenderLayerId = layerId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Layer GetLayerByIdLocked(long layerId)
|
private Layer GetLayerByIdLocked(long layerId)
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<long, Layer> pair in _layers)
|
foreach (KeyValuePair<long, Layer> pair in _layers)
|
||||||
@@ -360,41 +336,55 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
// TODO: support multilayers (& multidisplay ?)
|
foreach (var (layerId, layer) in _layers)
|
||||||
if (RenderLayerId == 0)
|
|
||||||
{
|
{
|
||||||
return;
|
if (layer.State == LayerState.NotInitialized || layer.State == LayerState.ManagedClosed)
|
||||||
}
|
continue;
|
||||||
|
|
||||||
Layer layer = GetLayerByIdLocked(RenderLayerId);
|
if (_device.System.KernelContext.Processes.TryGetValue(layer.Owner, out var process))
|
||||||
|
|
||||||
Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
|
|
||||||
|
|
||||||
if (acquireStatus == Status.Success)
|
|
||||||
{
|
|
||||||
if (_device.VSyncMode == VSyncMode.Unbounded)
|
|
||||||
{
|
{
|
||||||
if (_swapInterval != 0)
|
if (process.State == ProcessState.Exiting || process.State == ProcessState.Exited)
|
||||||
{
|
{
|
||||||
UpdateSwapInterval(0);
|
HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
|
||||||
_vSyncMode = _device.VSyncMode;
|
|
||||||
|
if (_layers.Remove(layerId))
|
||||||
|
{
|
||||||
|
CloseLayer(layerId, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
|
||||||
}
|
|
||||||
else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation)
|
if (acquireStatus == Status.Success)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
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)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.SurfaceFlinger, $"Failed to acquire buffer for layer {layerId} (status: {acquireStatus})");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,7 +403,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
ulong bufferOffset = (ulong)item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset;
|
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;
|
ulong frameBufferAddress = map.Address + bufferOffset;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
@@ -36,6 +37,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
public int PlanesCount;
|
public int PlanesCount;
|
||||||
|
|
||||||
[FieldOffset(0x34)]
|
[FieldOffset(0x34)]
|
||||||
public NvGraphicBufferSurfaceArray Surfaces;
|
public Array3<NvGraphicBufferSurface> Surfaces;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
|||||||
ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId);
|
ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId);
|
||||||
|
|
||||||
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid);
|
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid);
|
||||||
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
|
|
||||||
|
|
||||||
context.ResponseData.Write(layerId);
|
context.ResponseData.Write(layerId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
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
|
namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
||||||
{
|
{
|
||||||
@@ -8,6 +15,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
|||||||
private readonly IApplicationDisplayService _applicationDisplayService;
|
private readonly IApplicationDisplayService _applicationDisplayService;
|
||||||
#pragma warning restore IDE0052
|
#pragma warning restore IDE0052
|
||||||
|
|
||||||
|
private KEvent _sharedFramebufferAcquirableEvent;
|
||||||
|
private int _sharedFramebufferAcquirableEventHandle;
|
||||||
|
|
||||||
public ISystemDisplayService(IApplicationDisplayService applicationDisplayService)
|
public ISystemDisplayService(IApplicationDisplayService applicationDisplayService)
|
||||||
{
|
{
|
||||||
_applicationDisplayService = applicationDisplayService;
|
_applicationDisplayService = applicationDisplayService;
|
||||||
@@ -57,5 +67,138 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
|||||||
|
|
||||||
return ResultCode.Success;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -248,8 +248,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
|
|
||||||
|
|
||||||
using Parcel parcel = new(0x28, 0x4);
|
using Parcel parcel = new(0x28, 0x4);
|
||||||
|
|
||||||
parcel.WriteObject(producer, "dispdrv\0");
|
parcel.WriteObject(producer, "dispdrv\0");
|
||||||
@@ -286,8 +284,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
// TODO: support multi display.
|
// TODO: support multi display.
|
||||||
IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, 0, LayerState.Stray);
|
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);
|
using Parcel parcel = new(0x28, 0x4);
|
||||||
|
|
||||||
parcel.WriteObject(producer, "dispdrv\0");
|
parcel.WriteObject(producer, "dispdrv\0");
|
||||||
|
|||||||
19
src/Ryujinx.HLE/HOS/Services/Vi/Types/SharedBufferMap.cs
Normal file
19
src/Ryujinx.HLE/HOS/Services/Vi/Types/SharedBufferMap.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
235
src/Ryujinx.HLE/HOS/Services/Vi/ViServer.cs
Normal file
235
src/Ryujinx.HLE/HOS/Services/Vi/ViServer.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Audio.Backends.CompatLayer;
|
using Ryujinx.Audio.Backends.CompatLayer;
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Apm;
|
using Ryujinx.HLE.HOS.Services.Apm;
|
||||||
@@ -158,5 +161,18 @@ namespace Ryujinx.HLE
|
|||||||
Shared = null;
|
Shared = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool LoadSystemProgramId(ulong programId)
|
||||||
|
{
|
||||||
|
string contentPath = System.ContentManager.GetInstalledContentPath(programId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||||
|
string filePath = VirtualFileSystem.SwitchPathToSystemPath(contentPath);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(filePath))
|
||||||
|
{
|
||||||
|
throw new InvalidSystemResourceException("Specified title ID is not installed on the system.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Processes.LoadNca(filePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,10 +45,12 @@ namespace Ryujinx.HLE.UI
|
|||||||
/// <param name="value">The value associated to the <paramref name="kind"/>.</param>
|
/// <param name="value">The value associated to the <paramref name="kind"/>.</param>
|
||||||
void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value);
|
void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
/// Displays a Message Dialog box specific to Error Applet and blocks until it is closed.
|
/// Displays a Message Dialog box specific to Error Applet and blocks until it is closed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>False when OK is pressed, True when another button (Details) is pressed.</returns>
|
/// <returns>False when OK is pressed, True when another button (Details) is pressed.</returns>
|
||||||
bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText);
|
// ReSharper disable once UnusedParameter.Global
|
||||||
|
bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a handler to process keyboard inputs into text strings.
|
/// Creates a handler to process keyboard inputs into text strings.
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
using MsgPack;
|
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Prepo.Types;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon
|
namespace Ryujinx.Horizon
|
||||||
{
|
{
|
||||||
public static class HorizonStatic
|
public static class HorizonStatic
|
||||||
{
|
{
|
||||||
internal static void HandlePlayReport(MessagePackObject report) => PlayReportPrinted?.Invoke(report);
|
internal static void HandlePlayReport(PlayReport report) =>
|
||||||
|
new Thread(() => PlayReport?.Invoke(report))
|
||||||
|
{
|
||||||
|
Name = "HLE.PlayReportEvent",
|
||||||
|
IsBackground = true,
|
||||||
|
Priority = ThreadPriority.AboveNormal
|
||||||
|
}.Start();
|
||||||
|
|
||||||
public static event Action<MessagePackObject> PlayReportPrinted;
|
public static event Action<PlayReport> PlayReport;
|
||||||
|
|
||||||
[ThreadStatic]
|
[field: ThreadStatic]
|
||||||
private static HorizonOptions _options;
|
public static HorizonOptions Options { get; private set; }
|
||||||
|
|
||||||
[ThreadStatic]
|
[field: ThreadStatic]
|
||||||
private static ISyscallApi _syscall;
|
public static ISyscallApi Syscall { get; private set; }
|
||||||
|
|
||||||
[ThreadStatic]
|
[field: ThreadStatic]
|
||||||
private static IVirtualMemoryManager _addressSpace;
|
public static IVirtualMemoryManager AddressSpace { get; private set; }
|
||||||
|
|
||||||
[ThreadStatic]
|
[field: ThreadStatic]
|
||||||
private static IThreadContext _threadContext;
|
public static IThreadContext ThreadContext { get; private set; }
|
||||||
|
|
||||||
[ThreadStatic]
|
[field: ThreadStatic]
|
||||||
private static int _threadHandle;
|
public static int CurrentThreadHandle { get; private set; }
|
||||||
|
|
||||||
public static HorizonOptions Options => _options;
|
|
||||||
public static ISyscallApi Syscall => _syscall;
|
|
||||||
public static IVirtualMemoryManager AddressSpace => _addressSpace;
|
|
||||||
public static IThreadContext ThreadContext => _threadContext;
|
|
||||||
public static int CurrentThreadHandle => _threadHandle;
|
|
||||||
|
|
||||||
public static void Register(
|
public static void Register(
|
||||||
HorizonOptions options,
|
HorizonOptions options,
|
||||||
@@ -39,11 +40,11 @@ namespace Ryujinx.Horizon
|
|||||||
IThreadContext threadContext,
|
IThreadContext threadContext,
|
||||||
int threadHandle)
|
int threadHandle)
|
||||||
{
|
{
|
||||||
_options = options;
|
Options = options;
|
||||||
_syscall = syscallApi;
|
Syscall = syscallApi;
|
||||||
_addressSpace = addressSpace;
|
AddressSpace = addressSpace;
|
||||||
_threadContext = threadContext;
|
ThreadContext = threadContext;
|
||||||
_threadHandle = threadHandle;
|
CurrentThreadHandle = threadHandle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,6 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
|||||||
{
|
{
|
||||||
partial class PrepoService : IPrepoService
|
partial class PrepoService : IPrepoService
|
||||||
{
|
{
|
||||||
enum PlayReportKind
|
|
||||||
{
|
|
||||||
Normal,
|
|
||||||
System,
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ArpApi _arp;
|
private readonly ArpApi _arp;
|
||||||
private readonly PrepoServicePermissionLevel _permissionLevel;
|
private readonly PrepoServicePermissionLevel _permissionLevel;
|
||||||
private ulong _systemSessionId;
|
private ulong _systemSessionId;
|
||||||
@@ -198,6 +192,13 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
|||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
|
MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
|
||||||
|
|
||||||
|
PlayReport playReport = new()
|
||||||
|
{
|
||||||
|
Kind = playReportKind,
|
||||||
|
Room = gameRoom,
|
||||||
|
ReportData = deserializedReport
|
||||||
|
};
|
||||||
|
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine("PlayReport log:");
|
builder.AppendLine("PlayReport log:");
|
||||||
builder.AppendLine($" Kind: {playReportKind}");
|
builder.AppendLine($" Kind: {playReportKind}");
|
||||||
@@ -207,10 +208,12 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
|||||||
if (pid != 0)
|
if (pid != 0)
|
||||||
{
|
{
|
||||||
builder.AppendLine($" Pid: {pid}");
|
builder.AppendLine($" Pid: {pid}");
|
||||||
|
playReport.Pid = pid;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder.AppendLine($" ApplicationId: {applicationId}");
|
builder.AppendLine($" ApplicationId: {applicationId}");
|
||||||
|
playReport.AppId = applicationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result result = _arp.GetApplicationInstanceId(out ulong applicationInstanceId, pid);
|
Result result = _arp.GetApplicationInstanceId(out ulong applicationInstanceId, pid);
|
||||||
@@ -221,17 +224,20 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
|||||||
|
|
||||||
_arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty applicationLaunchProperty, applicationInstanceId).AbortOnFailure();
|
_arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty applicationLaunchProperty, applicationInstanceId).AbortOnFailure();
|
||||||
|
|
||||||
|
playReport.Version = applicationLaunchProperty.Version;
|
||||||
|
|
||||||
builder.AppendLine($" ApplicationVersion: {applicationLaunchProperty.Version}");
|
builder.AppendLine($" ApplicationVersion: {applicationLaunchProperty.Version}");
|
||||||
|
|
||||||
if (!userId.IsNull)
|
if (!userId.IsNull)
|
||||||
{
|
{
|
||||||
builder.AppendLine($" UserId: {userId}");
|
builder.AppendLine($" UserId: {userId}");
|
||||||
|
playReport.UserId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AppendLine($" Room: {gameRoom}");
|
builder.AppendLine($" Room: {gameRoom}");
|
||||||
builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
|
builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
|
||||||
|
|
||||||
HorizonStatic.HandlePlayReport(deserializedReport);
|
HorizonStatic.HandlePlayReport(playReport);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString());
|
Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString());
|
||||||
|
|
||||||
|
|||||||
24
src/Ryujinx.Horizon/Prepo/Types/PlayReport.cs
Normal file
24
src/Ryujinx.Horizon/Prepo/Types/PlayReport.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -185,6 +185,15 @@ namespace Ryujinx.Input.HLE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool InputUpdatesBlocked
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
return _blockInputUpdates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void BlockInputUpdates()
|
public void BlockInputUpdates()
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -9,7 +10,7 @@ namespace Ryujinx.Memory
|
|||||||
/// Represents a address space manager.
|
/// Represents a address space manager.
|
||||||
/// Supports virtual memory region mapping, address translation and read/write access to mapped regions.
|
/// Supports virtual memory region mapping, address translation and read/write access to mapped regions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManager
|
public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManagerTracked
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool UsesPrivateAllocations => false;
|
public bool UsesPrivateAllocations => false;
|
||||||
@@ -21,6 +22,8 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
private readonly MemoryBlock _backingMemory;
|
private readonly MemoryBlock _backingMemory;
|
||||||
private readonly PageTable<nuint> _pageTable;
|
private readonly PageTable<nuint> _pageTable;
|
||||||
|
private readonly MemoryTracking _tracking;
|
||||||
|
private bool _writeTracked;
|
||||||
|
|
||||||
protected override ulong AddressSpaceSize { get; }
|
protected override ulong AddressSpaceSize { get; }
|
||||||
|
|
||||||
@@ -44,6 +47,7 @@ namespace Ryujinx.Memory
|
|||||||
AddressSpaceSize = asSize;
|
AddressSpaceSize = asSize;
|
||||||
_backingMemory = backingMemory;
|
_backingMemory = backingMemory;
|
||||||
_pageTable = new PageTable<nuint>();
|
_pageTable = new PageTable<nuint>();
|
||||||
|
_tracking = new MemoryTracking(this, 0x1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -227,7 +231,7 @@ namespace Ryujinx.Memory
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest = false)
|
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)
|
protected unsafe override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
|
||||||
@@ -240,5 +244,29 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
|
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
|
||||||
=> GetHostAddress(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Ryujinx.Memory.Tracking;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace Ryujinx.Memory
|
||||||
{
|
{
|
||||||
public interface IVirtualMemoryManagerTracked : IVirtualMemoryManager
|
public interface IVirtualMemoryManagerTracked : IVirtualMemoryManager
|
||||||
{
|
{
|
||||||
@@ -41,6 +41,7 @@ using Ryujinx.HLE.FileSystem;
|
|||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
|
using Ryujinx.HLE.Loaders.Processes;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@@ -517,7 +518,7 @@ namespace Ryujinx.Ava
|
|||||||
Device?.System.ChangeDockedModeState(e.NewValue);
|
Device?.System.ChangeDockedModeState(e.NewValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
|
public void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
|
||||||
{
|
{
|
||||||
Device?.SetVolume(e.NewValue);
|
Device?.SetVolume(e.NewValue);
|
||||||
|
|
||||||
@@ -672,6 +673,7 @@ namespace Ryujinx.Ava
|
|||||||
DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now;
|
DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now;
|
||||||
|
|
||||||
InitEmulatedSwitch();
|
InitEmulatedSwitch();
|
||||||
|
Device.LoadSystemProgramId(0x010000000000100C);
|
||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
|
|
||||||
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||||
@@ -938,7 +940,9 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.System.EnableInternetAccess,
|
ConfigurationState.Instance.System.EnableInternetAccess,
|
||||||
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||||
ConfigurationState.Instance.System.FsGlobalAccessLogMode,
|
ConfigurationState.Instance.System.FsGlobalAccessLogMode,
|
||||||
ConfigurationState.Instance.System.SystemTimeOffset,
|
ConfigurationState.Instance.System.MatchSystemTime
|
||||||
|
? 0
|
||||||
|
: ConfigurationState.Instance.System.SystemTimeOffset,
|
||||||
ConfigurationState.Instance.System.TimeZone,
|
ConfigurationState.Instance.System.TimeZone,
|
||||||
ConfigurationState.Instance.System.MemoryManagerMode,
|
ConfigurationState.Instance.System.MemoryManagerMode,
|
||||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||||
@@ -1039,6 +1043,7 @@ namespace Ryujinx.Ava
|
|||||||
if (_viewModel.StartGamesInFullscreen)
|
if (_viewModel.StartGamesInFullscreen)
|
||||||
{
|
{
|
||||||
_viewModel.WindowState = WindowState.FullScreen;
|
_viewModel.WindowState = WindowState.FullScreen;
|
||||||
|
_viewModel.Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_viewModel.WindowState is WindowState.FullScreen || _viewModel.StartGamesWithoutUI)
|
if (_viewModel.WindowState is WindowState.FullScreen || _viewModel.StartGamesWithoutUI)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Common
|
|
||||||
{
|
|
||||||
public static class ThemeManager
|
|
||||||
{
|
|
||||||
public static event Action ThemeChanged;
|
|
||||||
|
|
||||||
public static void OnThemeChanged()
|
|
||||||
{
|
|
||||||
ThemeChanged?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,15 @@
|
|||||||
using DiscordRPC;
|
using DiscordRPC;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
using MsgPack;
|
|
||||||
using Ryujinx.Ava.Utilities;
|
using Ryujinx.Ava.Utilities;
|
||||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||||
using Ryujinx.Ava.Utilities.Configuration;
|
using Ryujinx.Ava.Utilities.Configuration;
|
||||||
|
using Ryujinx.Ava.Utilities.PlayReport;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Helper;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.Loaders.Processes;
|
using Ryujinx.HLE.Loaders.Processes;
|
||||||
using Ryujinx.Horizon;
|
using Ryujinx.Horizon;
|
||||||
using System;
|
using Ryujinx.Horizon.Prepo.Types;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Ava
|
namespace Ryujinx.Ava
|
||||||
@@ -28,7 +24,7 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
private static readonly string _description =
|
private static readonly string _description =
|
||||||
ReleaseInformation.IsValid
|
ReleaseInformation.IsValid
|
||||||
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
|
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}"
|
||||||
: "dev build";
|
: "dev build";
|
||||||
|
|
||||||
private const string ApplicationId = "1293250299716173864";
|
private const string ApplicationId = "1293250299716173864";
|
||||||
@@ -41,6 +37,9 @@ namespace Ryujinx.Ava
|
|||||||
private static RichPresence _discordPresencePlaying;
|
private static RichPresence _discordPresencePlaying;
|
||||||
private static ApplicationMetadata _currentApp;
|
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()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
_discordPresenceMain = new RichPresence
|
_discordPresenceMain = new RichPresence
|
||||||
@@ -56,7 +55,8 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
||||||
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
|
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
|
||||||
HorizonStatic.PlayReportPrinted += HandlePlayReport;
|
HorizonStatic.PlayReport += HandlePlayReport;
|
||||||
|
PlayReports.Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
|
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
|
||||||
@@ -117,11 +117,6 @@ namespace Ryujinx.Ava
|
|||||||
_currentApp = appMeta;
|
_currentApp = appMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdatePlayingState()
|
|
||||||
{
|
|
||||||
_discordClient?.SetPresence(_discordPresencePlaying);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SwitchToMainState()
|
private static void SwitchToMainState()
|
||||||
{
|
{
|
||||||
_discordClient?.SetPresence(_discordPresenceMain);
|
_discordClient?.SetPresence(_discordPresenceMain);
|
||||||
@@ -129,27 +124,28 @@ namespace Ryujinx.Ava
|
|||||||
_currentApp = null;
|
_currentApp = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandlePlayReport(MessagePackObject playReport)
|
private static void HandlePlayReport(PlayReport playReport)
|
||||||
{
|
{
|
||||||
if (_discordClient is null) return;
|
if (_discordClient is null) return;
|
||||||
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
|
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
|
||||||
if (_discordPresencePlaying is null) return;
|
if (_discordPresencePlaying is null) return;
|
||||||
|
|
||||||
PlayReportFormattedValue value = PlayReport.Analyzer.Run(TitleIDs.CurrentApplication.Value, _currentApp, playReport);
|
FormattedValue formattedValue =
|
||||||
|
PlayReports.Analyzer.Format(TitleIDs.CurrentApplication.Value, _currentApp, playReport);
|
||||||
|
|
||||||
if (!value.Handled) return;
|
if (!formattedValue.Handled) return;
|
||||||
|
|
||||||
if (value.Reset)
|
_discordPresencePlaying.Details = TruncateToByteLength(
|
||||||
{
|
formattedValue.Reset
|
||||||
_discordPresencePlaying.Details = $"Playing {_currentApp.Title}";
|
? $"Playing {_currentApp.Title}"
|
||||||
Logger.Info?.Print(LogClass.UI, "Reset Discord RPC based on a supported play report value formatter.");
|
: formattedValue.FormattedString
|
||||||
}
|
);
|
||||||
else
|
|
||||||
{
|
if (_discordClient.CurrentPresence.Details.Equals(_discordPresencePlaying.Details))
|
||||||
_discordPresencePlaying.Details = value.FormattedString;
|
return; //don't trigger an update if the set presence Details are identical to current
|
||||||
Logger.Info?.Print(LogClass.UI, "Updated Discord RPC based on a supported play report.");
|
|
||||||
}
|
_discordClient.SetPresence(_discordPresencePlaying);
|
||||||
UpdatePlayingState();
|
Logger.Info?.Print(LogClass.UI, "Updated Discord RPC based on a supported play report.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string TruncateToByteLength(string input)
|
private static string TruncateToByteLength(string input)
|
||||||
|
|||||||
@@ -289,7 +289,8 @@ namespace Ryujinx.Headless
|
|||||||
|
|
||||||
DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
|
DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
|
||||||
|
|
||||||
if (_inputConfiguration.OfType<StandardControllerInputConfig>().Any(ic => ic.Led.UseRainbow))
|
if (_inputConfiguration.OfType<StandardControllerInputConfig>()
|
||||||
|
.Any(ic => ic?.Led?.UseRainbow ?? false))
|
||||||
Rainbow.Enable();
|
Rainbow.Enable();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ namespace Ryujinx.Headless
|
|||||||
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
|
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
|
||||||
|
|
||||||
if (NeedsOverride(nameof(IgnoreControllerApplet)))
|
if (NeedsOverride(nameof(IgnoreControllerApplet)))
|
||||||
IgnoreControllerApplet = configurationState.System.IgnoreApplet;
|
IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -387,7 +387,7 @@ namespace Ryujinx.Headless
|
|||||||
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
|
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
|
||||||
public string GraphicsShadersDumpPath { get; set; }
|
public string GraphicsShadersDumpPath { get; set; }
|
||||||
|
|
||||||
[Option("graphics-backend", Required = false, Default = GraphicsBackend.OpenGl, HelpText = "Change Graphics Backend to use.")]
|
[Option("graphics-backend", Required = false, Default = GraphicsBackend.Vulkan, HelpText = "Change Graphics Backend to use.")]
|
||||||
public GraphicsBackend GraphicsBackend { get; set; }
|
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.")]
|
[Option("preferred-gpu-vendor", Required = false, Default = "", HelpText = "When using the Vulkan backend, prefer using the GPU from the specified vendor.")]
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ namespace Ryujinx.Headless
|
|||||||
Exit();
|
Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText)
|
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null)
|
||||||
{
|
{
|
||||||
SDL_MessageBoxData data = new()
|
SDL_MessageBoxData data = new()
|
||||||
{
|
{
|
||||||
@@ -521,7 +521,7 @@ namespace Ryujinx.Headless
|
|||||||
message = message,
|
message = message,
|
||||||
buttons = new SDL_MessageBoxButtonData[buttonsText.Length],
|
buttons = new SDL_MessageBoxButtonData[buttonsText.Length],
|
||||||
numbuttons = buttonsText.Length,
|
numbuttons = buttonsText.Length,
|
||||||
window = WindowHandle,
|
window = WindowHandle
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < buttonsText.Length; i++)
|
for (int i = 0; i < buttonsText.Length; i++)
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ namespace Ryujinx.Ava
|
|||||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
|
||||||
{
|
{
|
||||||
_ = MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
_ = MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviewerDetached = true;
|
PreviewerDetached = true;
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
public class RyujinxApp : Application
|
public class RyujinxApp : Application
|
||||||
{
|
{
|
||||||
|
public static event Action ThemeChanged;
|
||||||
|
|
||||||
internal static string FormatTitle(LocaleKeys? windowTitleKey = null, bool includeVersion = true)
|
internal static string FormatTitle(LocaleKeys? windowTitleKey = null, bool includeVersion = true)
|
||||||
=> windowTitleKey is null
|
=> windowTitleKey is null
|
||||||
? $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)}"
|
? $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)}"
|
||||||
@@ -33,6 +35,9 @@ namespace Ryujinx.Ava
|
|||||||
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>()
|
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>()
|
||||||
.MainWindow.Cast<MainWindow>();
|
.MainWindow.Cast<MainWindow>();
|
||||||
|
|
||||||
|
public static IClassicDesktopStyleApplicationLifetime AppLifetime => Current!
|
||||||
|
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>();
|
||||||
|
|
||||||
public static bool IsClipboardAvailable(out IClipboard clipboard)
|
public static bool IsClipboardAvailable(out IClipboard clipboard)
|
||||||
{
|
{
|
||||||
clipboard = MainWindow.Clipboard;
|
clipboard = MainWindow.Clipboard;
|
||||||
@@ -109,7 +114,7 @@ namespace Ryujinx.Ava
|
|||||||
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeManager.OnThemeChanged();
|
ThemeChanged?.Invoke();
|
||||||
|
|
||||||
RequestedThemeVariant = baseStyle switch
|
RequestedThemeVariant = baseStyle switch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
bool okPressed = false;
|
bool okPressed = false;
|
||||||
|
|
||||||
if (ConfigurationState.Instance.System.IgnoreApplet)
|
if (ConfigurationState.Instance.System.IgnoreControllerApplet)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
@@ -75,31 +75,32 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
bool opened = false;
|
bool opened = false;
|
||||||
|
|
||||||
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
|
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
string.Empty,
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel],
|
LocaleManager.Instance[LocaleKeys.DialogOpenSettingsWindowLabel],
|
||||||
string.Empty,
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
||||||
(int)Symbol.Important,
|
(int)Symbol.Important,
|
||||||
deferEvent,
|
deferEvent,
|
||||||
async window =>
|
async window =>
|
||||||
{
|
{
|
||||||
if (opened)
|
if (opened)
|
||||||
{
|
{
|
||||||
return;
|
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)
|
if (response == UserResult.Ok)
|
||||||
{
|
{
|
||||||
@@ -110,7 +111,9 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex));
|
await ContentDialogHelper.CreateErrorDialog(
|
||||||
|
LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||||
|
LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex));
|
||||||
|
|
||||||
dialogCloseEvent.Set();
|
dialogCloseEvent.Set();
|
||||||
}
|
}
|
||||||
@@ -134,7 +137,9 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_parent.ViewModel.AppHost.NpadManager.BlockInputUpdates();
|
_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)
|
if (result == UserResult.Ok)
|
||||||
{
|
{
|
||||||
@@ -146,7 +151,9 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
{
|
{
|
||||||
error = true;
|
error = true;
|
||||||
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
|
await ContentDialogHelper.CreateErrorDialog(
|
||||||
|
LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||||
|
LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -177,7 +184,8 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
args.InitialText = "Ryujinx";
|
args.InitialText = "Ryujinx";
|
||||||
args.StringLengthMin = 1;
|
args.StringLengthMin = 1;
|
||||||
args.StringLengthMax = 25;
|
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)
|
if (result == UserResult.Ok)
|
||||||
{
|
{
|
||||||
inputText = userInput;
|
inputText = userInput;
|
||||||
@@ -201,11 +209,13 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
dialogCloseEvent.Set();
|
dialogCloseEvent.Set();
|
||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.CabinetScanDialog],
|
await ContentDialogHelper.CreateInfoDialog(
|
||||||
string.Empty,
|
LocaleManager.Instance[LocaleKeys.CabinetScanDialog],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
string.Empty,
|
||||||
string.Empty,
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
LocaleManager.Instance[LocaleKeys.CabinetTitle]);
|
string.Empty,
|
||||||
|
LocaleManager.Instance[LocaleKeys.CabinetTitle]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
dialogCloseEvent.WaitOne();
|
dialogCloseEvent.WaitOne();
|
||||||
}
|
}
|
||||||
@@ -217,7 +227,8 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
_parent.ViewModel.AppHost?.Stop();
|
_parent.ViewModel.AppHost?.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
|
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons,
|
||||||
|
(uint Module, uint Description)? errorCode = null)
|
||||||
{
|
{
|
||||||
ManualResetEvent dialogCloseEvent = new(false);
|
ManualResetEvent dialogCloseEvent = new(false);
|
||||||
|
|
||||||
@@ -229,9 +240,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
{
|
{
|
||||||
ErrorAppletWindow msgDialog = new(_parent, buttons, message)
|
ErrorAppletWindow msgDialog = new(_parent, buttons, message)
|
||||||
{
|
{
|
||||||
Title = title,
|
Title = title, WindowStartupLocation = WindowStartupLocation.CenterScreen, Width = 400
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterScreen,
|
|
||||||
Width = 400
|
|
||||||
};
|
};
|
||||||
|
|
||||||
object response = await msgDialog.Run();
|
object response = await msgDialog.Run();
|
||||||
@@ -249,7 +258,9 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
{
|
{
|
||||||
dialogCloseEvent.Set();
|
dialogCloseEvent.Set();
|
||||||
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex));
|
await ContentDialogHelper.CreateErrorDialog(
|
||||||
|
LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||||
|
LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -278,13 +289,11 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
.ForEach(profile => profiles.Add(new Models.UserProfile(profile, nav)));
|
.ForEach(profile => profiles.Add(new Models.UserProfile(profile, nav)));
|
||||||
|
|
||||||
profiles.Add(new Models.UserProfile(guest, nav));
|
profiles.Add(new Models.UserProfile(guest, nav));
|
||||||
UserSelectorDialogViewModel viewModel = new()
|
ProfileSelectorDialogViewModel viewModel = new()
|
||||||
{
|
{
|
||||||
Profiles = profiles,
|
Profiles = profiles, SelectedUserId = _parent.AccountManager.LastOpenedUser.UserId
|
||||||
SelectedUserId = _parent.AccountManager.LastOpenedUser.UserId
|
|
||||||
};
|
};
|
||||||
UserSelectorDialog content = new(viewModel);
|
(selected, _) = await ProfileSelectorDialog.ShowInputDialog(viewModel);
|
||||||
(selected, _) = await UserSelectorDialog.ShowInputDialog(content);
|
|
||||||
|
|
||||||
dialogCloseEvent.Set();
|
dialogCloseEvent.Set();
|
||||||
});
|
});
|
||||||
@@ -311,6 +320,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Ryujinx.Ava.UI.Applet.UserSelectorDialog"
|
x:Class="Ryujinx.Ava.UI.Applet.ProfileSelectorDialog"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
@@ -12,9 +12,9 @@
|
|||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Focusable="True"
|
Focusable="True"
|
||||||
x:DataType="viewModels:UserSelectorDialogViewModel">
|
x:DataType="viewModels:ProfileSelectorDialogViewModel">
|
||||||
<Design.DataContext>
|
<Design.DataContext>
|
||||||
<viewModels:UserSelectorDialogViewModel />
|
<viewModels:ProfileSelectorDialogViewModel />
|
||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
|
|
||||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
@@ -16,15 +16,15 @@ using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
|
|||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Applet
|
namespace Ryujinx.Ava.UI.Applet
|
||||||
{
|
{
|
||||||
public partial class UserSelectorDialog : UserControl, INotifyPropertyChanged
|
public partial class ProfileSelectorDialog : UserControl
|
||||||
{
|
{
|
||||||
public UserSelectorDialogViewModel ViewModel { get; set; }
|
public ProfileSelectorDialogViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
public UserSelectorDialog(UserSelectorDialogViewModel viewModel)
|
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
|
||||||
{
|
{
|
||||||
|
DataContext = ViewModel = viewModel;
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ViewModel = viewModel;
|
|
||||||
DataContext = ViewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Grid_PointerEntered(object sender, PointerEventArgs e)
|
private void Grid_PointerEntered(object sender, PointerEventArgs e)
|
||||||
@@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
if (ViewModel.Profiles[selectedIndex] is UserProfile userProfile)
|
if (ViewModel.Profiles[selectedIndex] is UserProfile userProfile)
|
||||||
{
|
{
|
||||||
ViewModel.SelectedUserId = userProfile.UserId;
|
ViewModel.SelectedUserId = userProfile.UserId;
|
||||||
Logger.Info?.Print(LogClass.UI, $"Selected user: {userProfile.UserId}");
|
Logger.Info?.Print(LogClass.UI, $"Selected: {userProfile.UserId}", "ProfileSelector");
|
||||||
|
|
||||||
ObservableCollection<BaseModel> newProfiles = [];
|
ObservableCollection<BaseModel> newProfiles = [];
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<(UserId Id, bool Result)> ShowInputDialog(UserSelectorDialog content)
|
public static async Task<(UserId Id, bool Result)> ShowInputDialog(ProfileSelectorDialogViewModel viewModel)
|
||||||
{
|
{
|
||||||
ContentDialog contentDialog = new()
|
ContentDialog contentDialog = new()
|
||||||
{
|
{
|
||||||
@@ -87,22 +87,25 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
|
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
|
||||||
SecondaryButtonText = string.Empty,
|
SecondaryButtonText = string.Empty,
|
||||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.Cancel],
|
CloseButtonText = LocaleManager.Instance[LocaleKeys.Cancel],
|
||||||
Content = content,
|
Content = new ProfileSelectorDialog(viewModel),
|
||||||
Padding = new Thickness(0)
|
Padding = new Thickness(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
UserId result = UserId.Null;
|
UserId result = UserId.Null;
|
||||||
bool input = false;
|
bool input = false;
|
||||||
|
|
||||||
|
contentDialog.Closed += Handler;
|
||||||
|
|
||||||
|
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||||
|
|
||||||
|
return (result, input);
|
||||||
|
|
||||||
void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
|
void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.Result == ContentDialogResult.Primary)
|
if (eventArgs.Result == ContentDialogResult.Primary)
|
||||||
{
|
{
|
||||||
if (contentDialog.Content is UserSelectorDialog view)
|
result = viewModel.SelectedUserId;
|
||||||
{
|
input = true;
|
||||||
result = view.ViewModel.SelectedUserId;
|
|
||||||
input = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -110,12 +113,6 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
input = false;
|
input = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentDialog.Closed += Handler;
|
|
||||||
|
|
||||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
|
||||||
|
|
||||||
return (result, input);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,17 @@
|
|||||||
Header="{ext:Locale GameListContextMenuCreateShortcut}"
|
Header="{ext:Locale GameListContextMenuCreateShortcut}"
|
||||||
Icon="{ext:Icon fa-solid fa-bookmark}"
|
Icon="{ext:Icon fa-solid fa-bookmark}"
|
||||||
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
||||||
|
<MenuItem
|
||||||
|
IsVisible="{Binding HasCompatibilityEntry}"
|
||||||
|
Click="OpenApplicationCompatibility_Click"
|
||||||
|
Header="{ext:Locale GameListContextMenuShowCompatEntry}"
|
||||||
|
Icon="{ext:Icon mdi-gamepad}"
|
||||||
|
ToolTip.Tip="{ext:Locale GameListContextMenuShowCompatEntryToolTip}"/>
|
||||||
|
<MenuItem
|
||||||
|
Click="OpenApplicationData_Click"
|
||||||
|
Header="{ext:Locale GameListContextMenuShowGameData}"
|
||||||
|
Icon="{ext:Icon mdi-chart-line}"
|
||||||
|
ToolTip.Tip="{ext:Locale GameListContextMenuShowGameDataToolTip}"/>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenUserSaveDirectory_Click"
|
Click="OpenUserSaveDirectory_Click"
|
||||||
@@ -74,7 +85,6 @@
|
|||||||
Header="{ext:Locale GameListContextMenuTrimXCI}"
|
Header="{ext:Locale GameListContextMenuTrimXCI}"
|
||||||
IsEnabled="{Binding TrimXCIEnabled}"
|
IsEnabled="{Binding TrimXCIEnabled}"
|
||||||
ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" />
|
||||||
<Separator />
|
|
||||||
<MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}">
|
<MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="PurgePtcCache_Click"
|
Click="PurgePtcCache_Click"
|
||||||
@@ -112,6 +122,7 @@
|
|||||||
Header="{ext:Locale GameListContextMenuExtractDataRomFS}"
|
Header="{ext:Locale GameListContextMenuExtractDataRomFS}"
|
||||||
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataRomFSToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataRomFSToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
IsVisible="{Binding HasDlc}"
|
||||||
Click="ExtractAocRomFs_Click"
|
Click="ExtractAocRomFs_Click"
|
||||||
Header="{ext:Locale GameListContextMenuExtractDataAocRomFS}"
|
Header="{ext:Locale GameListContextMenuExtractDataAocRomFS}"
|
||||||
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataAocRomFSToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataAocRomFSToolTip}" />
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
|||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Ava.Utilities;
|
using Ryujinx.Ava.Utilities;
|
||||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||||
|
using Ryujinx.Ava.Utilities.Compat;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Helper;
|
using Ryujinx.Common.Helper;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
@@ -333,7 +334,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DownloadableContentModel selectedDlc = await DlcSelectView.Show(viewModel.SelectedApplication.IdBase, viewModel.ApplicationLibrary);
|
DownloadableContentModel selectedDlc = await DlcSelectView.Show(viewModel.SelectedApplication.Id, viewModel.ApplicationLibrary);
|
||||||
|
|
||||||
if (selectedDlc is not null)
|
if (selectedDlc is not null)
|
||||||
{
|
{
|
||||||
@@ -386,6 +387,18 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
|
||||||
|
{
|
||||||
|
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
|
await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void OpenApplicationData_Click(object sender, RoutedEventArgs args)
|
||||||
|
{
|
||||||
|
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
|
await ApplicationDataView.Show(viewModel.SelectedApplication);
|
||||||
|
}
|
||||||
|
|
||||||
public async void RunApplication_Click(object sender, RoutedEventArgs args)
|
public async void RunApplication_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
@@ -394,12 +407,8 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
public async void TrimXCI_Click(object sender, RoutedEventArgs args)
|
public async void TrimXCI_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
MainWindowViewModel viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
|
||||||
{
|
|
||||||
await viewModel.TrimXCIFile(viewModel.SelectedApplication.Path);
|
await viewModel.TrimXCIFile(viewModel.SelectedApplication.Path);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
189
src/Ryujinx/UI/Controls/ApplicationDataView.axaml
Normal file
189
src/Ryujinx/UI/Controls/ApplicationDataView.axaml
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
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"
|
||||||
|
x:DataType="viewModels:ApplicationDataViewModel">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Margin="0"
|
||||||
|
MaxWidth="256"
|
||||||
|
MinWidth="256"
|
||||||
|
Source="{Binding AppData.Icon, Converter={x:Static helpers:BitmapArrayValueConverter.Instance}}" />
|
||||||
|
<Border Margin="5, 0" Width="1" Height="256" BorderBrush="Gray" Background="Gray" />
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<Grid
|
||||||
|
RowDefinitions="Auto,Auto,Auto"
|
||||||
|
ColumnDefinitions="*">
|
||||||
|
<StackPanel Grid.Row="0">
|
||||||
|
<TextBlock HorizontalAlignment="Left"
|
||||||
|
Text="{Binding FormattedVersion}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBlock HorizontalAlignment="Left"
|
||||||
|
Text="{Binding FormattedDeveloper}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBlock HorizontalAlignment="Stretch"
|
||||||
|
Text="{Binding FormattedFileExtension}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBlock HorizontalAlignment="Stretch"
|
||||||
|
Text="{Binding FormattedFileSize}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
<Separator Grid.Row="1" Margin="0, 10, 0, 10" Height="1" BorderBrush="Gray" Background="Gray" />
|
||||||
|
<StackPanel Grid.Row="2"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="5">
|
||||||
|
<StackPanel Orientation="Horizontal" IsVisible="{Binding AppData.HasPlayabilityInfo}">
|
||||||
|
<TextBlock Padding="0, 0, 5, 0" Text="{ext:Locale GameListHeaderCompatibilityStatus}" />
|
||||||
|
<Button
|
||||||
|
Click="PlayabilityStatus_OnClick"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Background="{DynamicResource AppListBackgroundColor}"
|
||||||
|
Padding="0">
|
||||||
|
<TextBlock
|
||||||
|
Margin="1.5"
|
||||||
|
Tag="{Binding AppData.IdString}"
|
||||||
|
Text="{Binding AppData.LocalizedStatus}"
|
||||||
|
Foreground="{Binding AppData.PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||||
|
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>
|
||||||
|
<Button.Styles>
|
||||||
|
<Style Selector="Button">
|
||||||
|
<Setter Property="MinWidth"
|
||||||
|
Value="0" />
|
||||||
|
<!-- avoids very wide buttons from the overall project avalonia style -->
|
||||||
|
</Style>
|
||||||
|
</Button.Styles>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Padding="0, 0, 5, 0" Text="{ext:Locale GameListHeaderTitleId}" />
|
||||||
|
<Button
|
||||||
|
Click="IdString_OnClick"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Background="{DynamicResource AppListBackgroundColor}"
|
||||||
|
Padding="0">
|
||||||
|
<TextBlock
|
||||||
|
Margin="1.5"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Text="{Binding AppData.IdString}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</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}"
|
||||||
|
Text="{Binding FormattedLdnInfo}"
|
||||||
|
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>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
83
src/Ryujinx/UI/Controls/ApplicationDataView.axaml.cs
Normal file
83
src/Ryujinx/UI/Controls/ApplicationDataView.axaml.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input.Platform;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using Ryujinx.Ava.UI.Windows;
|
||||||
|
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||||
|
using Ryujinx.Ava.Utilities.Compat;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Controls
|
||||||
|
{
|
||||||
|
public partial class ApplicationDataView : UserControl
|
||||||
|
{
|
||||||
|
public static async Task Show(ApplicationData appData)
|
||||||
|
{
|
||||||
|
ContentDialog contentDialog = new()
|
||||||
|
{
|
||||||
|
Title = appData.Name,
|
||||||
|
PrimaryButtonText = string.Empty,
|
||||||
|
SecondaryButtonText = string.Empty,
|
||||||
|
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
||||||
|
MinWidth = 256,
|
||||||
|
Content = new ApplicationDataView { DataContext = new ApplicationDataViewModel(appData) }
|
||||||
|
};
|
||||||
|
|
||||||
|
Style closeButton = new(x => x.Name("CloseButton"));
|
||||||
|
closeButton.Setters.Add(new Setter(WidthProperty, 160d));
|
||||||
|
|
||||||
|
Style closeButtonParent = new(x => x.Name("CommandSpace"));
|
||||||
|
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty,
|
||||||
|
Avalonia.Layout.HorizontalAlignment.Center));
|
||||||
|
|
||||||
|
contentDialog.Styles.Add(closeButton);
|
||||||
|
contentDialog.Styles.Add(closeButtonParent);
|
||||||
|
|
||||||
|
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationDataView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not Button { Content: TextBlock playabilityLabel })
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (RyujinxApp.AppLifetime.Windows.TryGetFirst(x => x is ContentDialogOverlayWindow, out Window window))
|
||||||
|
window.Close(ContentDialogResult.None);
|
||||||
|
|
||||||
|
await CompatibilityList.Show((string)playabilityLabel.Tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void IdString_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is not MainWindowViewModel mwvm)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sender is not Button { Content: TextBlock idText })
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text);
|
||||||
|
if (appData is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await clipboard.SetTextAsync(appData.IdString);
|
||||||
|
|
||||||
|
NotificationHelper.ShowInformation(
|
||||||
|
"Copied Title ID",
|
||||||
|
$"{appData.Name} ({appData.IdString})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -86,6 +86,41 @@
|
|||||||
Text="{Binding Version}"
|
Text="{Binding Version}"
|
||||||
TextAlignment="Start"
|
TextAlignment="Start"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
<Button
|
||||||
|
Click="PlayabilityStatus_OnClick"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
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>
|
||||||
|
<TextBlock
|
||||||
|
Margin="1.5"
|
||||||
|
Tag="{Binding IdString}"
|
||||||
|
Text="{Binding LocalizedStatus}"
|
||||||
|
Foreground="{Binding PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||||
|
TextAlignment="Start"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<Button.Styles>
|
||||||
|
<Style Selector="Button">
|
||||||
|
<Setter Property="MinWidth"
|
||||||
|
Value="0" />
|
||||||
|
<!-- avoids very wide buttons from the overall project avalonia style -->
|
||||||
|
</Style>
|
||||||
|
</Button.Styles>
|
||||||
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
@@ -117,7 +152,8 @@
|
|||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Text="{Binding Converter={helpers:MultiplayerInfoConverter}}"
|
IsVisible="{Binding HasLdnGames}"
|
||||||
|
Text="{Binding Converter={x:Static helpers:MultiplayerInfoConverter.Instance}}"
|
||||||
TextAlignment="Start"
|
TextAlignment="Start"
|
||||||
TextWrapping="Wrap"/>
|
TextWrapping="Wrap"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user