Compare commits
288 Commits
Author | SHA1 | Date | |
---|---|---|---|
306f7e93a0 | |||
8954ff3af2 | |||
d2f3adbf69 | |||
d511c845b7 | |||
21c9ac6240 | |||
81c9052847 | |||
9367e3c35d | |||
52cf141874 | |||
8a352df3c6 | |||
c545c59851 | |||
96ea4e8c8e | |||
b8f48bcf64 | |||
6966211e07 | |||
57524a4c8a | |||
f4539c49d8 | |||
12c62fdbc2 | |||
e3c6be5e29 | |||
4741a05df9 | |||
c6676007bf | |||
92b0b7d753 | |||
232237bf28 | |||
c27e453fd3 | |||
0e037d0213 | |||
0dca1fbe12 | |||
35d91a0e58 | |||
a73a5d7e85 | |||
832a5e8852 | |||
96d1f0da2d | |||
597388ecda | |||
1cf6d7b7bb | |||
7bc9d0cdad | |||
dc0dbc50ab | |||
994f4dc77d | |||
c9e297b74c | |||
dd514a115c | |||
7e0b4bd538 | |||
378080eb87 | |||
e8f5e97fa4 | |||
f3873620a3 | |||
986ac9ff83 | |||
42b9c1e8fe | |||
3b375525fb | |||
e6658c133c | |||
5b42a4d2c4 | |||
8f0c89ffd6 | |||
2c9715acf6 | |||
274af65f69 | |||
4ca78eded5 | |||
6cb6b15612 | |||
2725e40838 | |||
c2e4c8f98e | |||
b53e7ffd46 | |||
ac66643346 | |||
21e88f17f6 | |||
5626f2ca1c | |||
402f05b8ef | |||
fb27042e01 | |||
69a9de33d3 | |||
bba51c2eeb | |||
fc26189fe1 | |||
a40c90e7dd | |||
f864a49014 | |||
ecbf303266 | |||
b3bf05356b | |||
cb4b58052f | |||
f8cdd5f484 | |||
22202be394 | |||
17ba217940 | |||
aae4595bdb | |||
880fd3cfcb | |||
f679f25e08 | |||
c2709b3bdd | |||
2b6e81deea | |||
7271f1b18e | |||
5fda543f84 | |||
95c06de4c1 | |||
49c63ea077 | |||
531da8a1c0 | |||
5cbdfbc7a4 | |||
e0544dd9c7 | |||
aa784c3e5e | |||
9205077590 | |||
0ed40c7175 | |||
40d47b7aa2 | |||
ec0bb74968 | |||
42f7f98666 | |||
95bad6995c | |||
3d42995822 | |||
9095941fd1 | |||
ba71141bdc | |||
0a0675a7f6 | |||
a7c6e6a8cf | |||
0bc8151c7e | |||
40c17673f5 | |||
a8950d6ac4 | |||
162798b026 | |||
1b28ecd63e | |||
895d9b53bc | |||
0e06aace45 | |||
adf4ebcd60 | |||
470a8031a4 | |||
5440d4ad5c | |||
dde208b480 | |||
4c3d2d5d75 | |||
fab11ba3f1 | |||
332891b5ff | |||
7df4fcada7 | |||
d6698680be | |||
e5c9838b0b | |||
f8ec878796 | |||
9ff21f9ab6 | |||
aa021085cf | |||
1f5d881860 | |||
1f664100bd | |||
1f5e1ffa80 | |||
264438ff19 | |||
3b8ac1641a | |||
4250732353 | |||
4d1579acbf | |||
6279f5e430 | |||
b7d2bff6aa | |||
7c327fecb3 | |||
cc1a933a2f | |||
dd574146fb | |||
2c94ac455e | |||
e18d258fa0 | |||
36f10df775 | |||
680e548022 | |||
21c4176157 | |||
3b4ff2d6d9 | |||
12504f280c | |||
250fc51374 | |||
206e0882c2 | |||
609abc8b9b | |||
cee7121058 | |||
cd124bda58 | |||
9f12e50a54 | |||
097562bc6c | |||
db4242c5dc | |||
4dd77316f7 | |||
3f98369a17 | |||
c26aeefe03 | |||
666e05f5cb | |||
8d9d508dc7 | |||
e27f5522e2 | |||
add2a9d151 | |||
9e50dd99d7 | |||
0dec91bb42 | |||
d9b63353b0 | |||
eabd0ec93f | |||
138d5dc64a | |||
3e68a87d63 | |||
69b6ef7a4a | |||
40e87c634e | |||
79d1c190db | |||
2bc88467eb | |||
baf8752e74 | |||
d5e4378aea | |||
6dbcdfea47 | |||
c5258cf082 | |||
5c89e22bb9 | |||
11ecff2ff0 | |||
4c3f09644a | |||
e187a8870a | |||
a64fee29dc | |||
9ef94c8292 | |||
915d6d044c | |||
a4780ab33b | |||
a947a45d81 | |||
9db73f74cf | |||
a1efd87c45 | |||
49be977588 | |||
c95be55091 | |||
63dedbda86 | |||
c532118d94 | |||
52d6f2e656 | |||
c9bc4eaf58 | |||
3249f8ff41 | |||
1b41b285ac | |||
f5a6f45b27 | |||
210557951b | |||
4c2d9ff3ff | |||
8198b99935 | |||
460f96967d | |||
7ca779a26d | |||
b5032b3c91 | |||
f0a3dff136 | |||
f659dcb9d8 | |||
a34fb0e939 | |||
21ce8a9b80 | |||
9ecbee8032 | |||
80519af67d | |||
26e30faff3 | |||
0992310b76 | |||
009c1101d2 | |||
ba95ee54ab | |||
4ce4299ca2 | |||
17620d18db | |||
9f1cf6458c | |||
67b4e63cff | |||
c05c688ee8 | |||
b2623dc27d | |||
5131b71437 | |||
7870423671 | |||
b72916fbc1 | |||
da073fce61 | |||
1fc90e57d2 | |||
eafcc314a9 | |||
6e9bd4de13 | |||
05a41b31bc | |||
eed17f963e | |||
c09c0c002d | |||
d56d335c0b | |||
23c844b2aa | |||
81691b9e37 | |||
2dc422bc14 | |||
a80fa5e33f | |||
954e995321 | |||
dad9ab6bb6 | |||
f0562b9c75 | |||
b8556530f2 | |||
4f3af839be | |||
155736c986 | |||
dba908dc78 | |||
ecee34a50c | |||
9b5a0c3889 | |||
80b4972139 | |||
5d85468302 | |||
9b1cc2cec6 | |||
e691622f0a | |||
f663a5cd38 | |||
f7c2e867f4 | |||
cedd200745 | |||
58207685c0 | |||
095ad923ad | |||
f07ae7d53f | |||
c308f09722 | |||
f1eef29409 | |||
1f8d66db7c | |||
c3a5716a95 | |||
1f1e2a7f03 | |||
e54f9dc4b4 | |||
edfd4d70c0 | |||
fc43aecbbd | |||
58d7a1fe97 | |||
7aa430f1a5 | |||
6bf460e104 | |||
efb135b74c | |||
a707842e14 | |||
a5a9b9bc8b | |||
17078ad929 | |||
32450d45de | |||
ed7a0474c6 | |||
fe9c49949a | |||
052b23c83c | |||
e4f68592c3 | |||
1dcd44b94f | |||
61b1ce252f | |||
5f38086f94 | |||
7bae440d3a | |||
f1943fd0b6 | |||
ec8d4f3af5 | |||
b3f0978869 | |||
f614d2c435 | |||
40c9416097 | |||
618c8edc79 | |||
99fc4fa61b | |||
f6d5499a16 | |||
26bf13a65d | |||
96cf242bcf | |||
59755818ef | |||
f8beeeb7d3 | |||
cb250162cb | |||
7528f94536 | |||
43081c16c4 | |||
780627e7b0 | |||
9044cb38d1 | |||
a53cfdab78 | |||
c7f9962dde | |||
296c4a3d01 | |||
e7cf4e6eaf | |||
a1a4771ac1 | |||
2fd819613f | |||
ad6ff6ce99 | |||
dc30d94852 | |||
4f293f8cbe | |||
32a1cd83fd | |||
e3d0ccf8d5 |
@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion
|
|||||||
|
|
||||||
#### C# Coding Conventions ####
|
#### C# Coding Conventions ####
|
||||||
|
|
||||||
|
# Namespace preferences
|
||||||
|
csharp_style_namespace_declarations = block_scoped:warning
|
||||||
|
resharper_csharp_namespace_body = block_scoped
|
||||||
|
|
||||||
# var preferences
|
# var preferences
|
||||||
csharp_style_var_elsewhere = false:silent
|
csharp_style_var_elsewhere = false:silent
|
||||||
csharp_style_var_for_built_in_types = false:silent
|
csharp_style_var_for_built_in_types = false:silent
|
||||||
|
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
43
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,43 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug Report
|
|
||||||
about: Something doesn't work correctly in Ryujinx. Game-specific issues should be posted at https://github.com/Ryujinx/Ryujinx-Games-List instead, unless it is a provable regression.
|
|
||||||
#assignees:
|
|
||||||
---
|
|
||||||
|
|
||||||
## Bug Report
|
|
||||||
|
|
||||||
[ If any section does not apply, replace its contents with "N/A". ]</br>
|
|
||||||
[ Lines between [ ] (square brackets) should be removed before posting. ]
|
|
||||||
|
|
||||||
### What's the issue you encountered?
|
|
||||||
|
|
||||||
[ Describe the issue in detail and what you were doing beforehand. ]</br>
|
|
||||||
[ Did you make any changes related to Ryujinx itself? ]</br>
|
|
||||||
[ If so, make sure to include details relating to what exactly you changed. ]
|
|
||||||
|
|
||||||
### How can the issue be reproduced?
|
|
||||||
|
|
||||||
[ Include a detailed step by step process for recreating your issue. ]
|
|
||||||
|
|
||||||
### Log file
|
|
||||||
|
|
||||||
[ Logs files can be found under ``Logs`` folder in Ryujinx program folder. ]</br>
|
|
||||||
[ If you don't include a crash report in instances of crash related issues, we will ask you one to provide one. ]
|
|
||||||
|
|
||||||
### Environment?
|
|
||||||
|
|
||||||
- Ryujinx version: 1.0.X</br>
|
|
||||||
[ Replace X's with the Ryujinx version at time of crash. ]
|
|
||||||
- Game version: X.X.X</br>
|
|
||||||
[ Replace X's with the game version at time of crash. ]
|
|
||||||
- System Specs:
|
|
||||||
- OS: *(e.g. Windows 10)*
|
|
||||||
- CPU: *(e.g. i7-6700)*
|
|
||||||
- GPU: *(e.g. NVIDIA RTX 2070)*
|
|
||||||
- RAM: *(e.g. 16GiB)*
|
|
||||||
- Applied Mods : [ Yes (Which ones) / No ]
|
|
||||||
|
|
||||||
### Additional context?
|
|
||||||
|
|
||||||
Additional info about your environment:</br>
|
|
||||||
[ Any other information relevant to your issue. ]
|
|
86
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
86
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: File a bug report
|
||||||
|
title: "[Bug]"
|
||||||
|
labels: bug
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: issue
|
||||||
|
attributes:
|
||||||
|
label: Description of the issue
|
||||||
|
description: What's the issue you encountered?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: repro
|
||||||
|
attributes:
|
||||||
|
label: Reproduction steps
|
||||||
|
description: How can the issue be reproduced?
|
||||||
|
placeholder: Describe each step as precisely as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Log file
|
||||||
|
description: A log file will help our developers to better diagnose and fix the issue.
|
||||||
|
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: OS
|
||||||
|
placeholder: "e.g. Windows 10"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: ryujinx-version
|
||||||
|
attributes:
|
||||||
|
label: Ryujinx version
|
||||||
|
placeholder: "e.g. 1.0.470"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: game-version
|
||||||
|
attributes:
|
||||||
|
label: Game version
|
||||||
|
placeholder: "e.g. 1.1.1"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: cpu
|
||||||
|
attributes:
|
||||||
|
label: CPU
|
||||||
|
placeholder: "e.g. i7-6700"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: gpu
|
||||||
|
attributes:
|
||||||
|
label: GPU
|
||||||
|
placeholder: "e.g. NVIDIA RTX 2070"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: ram
|
||||||
|
attributes:
|
||||||
|
label: RAM
|
||||||
|
placeholder: "e.g. 16GB"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: mods
|
||||||
|
attributes:
|
||||||
|
label: List of applied mods
|
||||||
|
placeholder: You can list applied mods here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional context?
|
||||||
|
description: |
|
||||||
|
- Additional info about your environment:
|
||||||
|
- Any other information relevant to your issue.
|
||||||
|
validations:
|
||||||
|
required: false
|
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature Request
|
|
||||||
about: Suggest a new feature for Ryujinx.
|
|
||||||
#assignees:
|
|
||||||
---
|
|
||||||
|
|
||||||
## Feature Request
|
|
||||||
|
|
||||||
[ If any section does not apply, replace its contents with "N/A". ]</br>
|
|
||||||
[ If you do not have the information needed for a section, replace its contents with "Unknown". ]</br>
|
|
||||||
[ Lines between [ ] (square brackets) are to be removed before posting. ]</br>
|
|
||||||
|
|
||||||
[ Please search for existing [feature requests](https://github.com/Ryujinx/Ryujinx/issues) before you make your own request. ]</br>
|
|
||||||
[ Duplicate requests will be marked as such and you will be referred to the original request. ]
|
|
||||||
|
|
||||||
### What feature are you suggesting?
|
|
||||||
#### Overview:
|
|
||||||
- [ Include the basic, high-level concepts for this feature here. ]
|
|
||||||
|
|
||||||
#### Smaller Details:
|
|
||||||
- [ These may include specific methods of implementation etc. ]
|
|
||||||
|
|
||||||
#### Nature of Request:
|
|
||||||
[ Remove all that do not apply to your request. ]
|
|
||||||
- Addition
|
|
||||||
- [ Ex: Addition of certain original features or features from other community projects. ]
|
|
||||||
- [ If you are suggesting porting features or including features from other projects, include what license they are distributed under and what, if any libraries those project use. ]
|
|
||||||
- Change
|
|
||||||
- Removal
|
|
||||||
- [Ex: Removal of certain features or implementation due to a specific issue/bug or because of low quality code, etc.]
|
|
||||||
|
|
||||||
### Why would this feature be useful?
|
|
||||||
[ If this is a feature for an end-user, how does it benefit the end-user? ]</br>
|
|
||||||
[ If this feature is for developers, what does it add to Ryujinx that did not already exist? ]
|
|
30
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: Suggest a new feature for Ryujinx.
|
||||||
|
title: "[Feature Request]"
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: overview
|
||||||
|
attributes:
|
||||||
|
label: Overview
|
||||||
|
description: Include the basic, high-level concepts for this feature here.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: details
|
||||||
|
attributes:
|
||||||
|
label: Smaller details
|
||||||
|
description: These may include specific methods of implementation etc.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: request
|
||||||
|
attributes:
|
||||||
|
label: Nature of request
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: feature
|
||||||
|
attributes:
|
||||||
|
label: Why would this feature be useful?
|
||||||
|
validations:
|
||||||
|
required: true
|
@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
name: Missing CPU Instruction
|
|
||||||
about: CPU Instruction is missing in Ryujinx.
|
|
||||||
#assignees:
|
|
||||||
---
|
|
||||||
|
|
||||||
## Missing CPU Instruction
|
|
||||||
|
|
||||||
[ If any section does not apply, replace its contents with "N/A". ]</br>
|
|
||||||
[ If you do not have the information needed for a section, replace its contents with "Unknown". ]</br>
|
|
||||||
[ Lines between [ ] (square brackets) are to be removed before posting. ]
|
|
||||||
|
|
||||||
[ Please search for existing [missing CPU instruction](https://github.com/Ryujinx/Ryujinx/issues) before you make your own issue. ]</br>
|
|
||||||
[ See the following [issue](https://github.com/Ryujinx/Ryujinx/issues/1405) as an example ]</br>
|
|
||||||
[ Duplicate issue will be marked as such and you will be referred to the original request. ]
|
|
||||||
|
|
||||||
### What CPU instruction is missing?
|
|
||||||
|
|
||||||
Requires the *INSTRUCTION* instruction.</br>
|
|
||||||
[ Replace *INSTRUCTION* by the instruction name, e.g. VADDL.U16 ]
|
|
||||||
|
|
||||||
```
|
|
||||||
*
|
|
||||||
```
|
|
||||||
[ Add the undefined instruction error message in the above code block ]
|
|
||||||
|
|
||||||
### Instruction name
|
|
||||||
```
|
|
||||||
*
|
|
||||||
```
|
|
||||||
[ Include the name from [armconverter.com](https://armconverter.com/?disasm) or [shell-storm.org](http://shell-storm.org/online/Online-Assembler-and-Disassembler/?arch=arm64&endianness=big&dis_with_raw=True&dis_with_ins=True) in the above code block ]
|
|
||||||
|
|
||||||
### Required by:
|
|
||||||
[ Add our (games list database)[https://github.com/Ryujinx/Ryujinx-Games-List/issues] links of games who require this instruction ]
|
|
26
.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml
vendored
Normal file
26
.github/ISSUE_TEMPLATE/missing_cpu_instruction.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
name: Missing CPU Instruction
|
||||||
|
description: CPU Instruction is missing in Ryujinx.
|
||||||
|
title: "[CPU]"
|
||||||
|
labels: [cpu, not-implemented]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: instruction
|
||||||
|
attributes:
|
||||||
|
label: CPU instruction
|
||||||
|
description: What CPU instruction is missing?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: name
|
||||||
|
attributes:
|
||||||
|
label: Instruction name
|
||||||
|
description: Include the name from [armconverter.com](https://armconverter.com/?disasm) or [shell-storm.org](http://shell-storm.org/online/Online-Assembler-and-Disassembler/?arch=arm64&endianness=big&dis_with_raw=True&dis_with_ins=True) in the above code block
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: required
|
||||||
|
attributes:
|
||||||
|
label: Required by
|
||||||
|
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
|
||||||
|
validations:
|
||||||
|
required: true
|
35
.github/ISSUE_TEMPLATE/missing_service_call.md
vendored
35
.github/ISSUE_TEMPLATE/missing_service_call.md
vendored
@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
name: Missing Service Call
|
|
||||||
about: Service call is missing in Ryujinx.
|
|
||||||
#assignees:
|
|
||||||
---
|
|
||||||
|
|
||||||
## Missing Service Call
|
|
||||||
|
|
||||||
[ If any section does not apply, replace its contents with "N/A". ]</br>
|
|
||||||
[ If you do not have the information needed for a section, replace its contents with "Unknown". ]</br>
|
|
||||||
[ Lines between [ ] (square brackets) are to be removed before posting. ]
|
|
||||||
|
|
||||||
[ Please search for existing [missing service call](https://github.com/Ryujinx/Ryujinx/issues) before you make your own issue. ]</br>
|
|
||||||
[ See the following [issue](https://github.com/Ryujinx/Ryujinx/issues/1431) as an example ]</br>
|
|
||||||
[ Duplicate issue will be marked as such and you will be referred to the original request. ]
|
|
||||||
|
|
||||||
### What service call is missing?
|
|
||||||
|
|
||||||
*SERVICE* *INTERFACE*: *NUMBER* (*NAME*) is not implemented.</br>
|
|
||||||
[ Replace *SERVICE* by the service name, e.g. appletAE ]</br>
|
|
||||||
[ Replace *INTERFACE* by the interface name, e.g. IAllSystemAppletProxiesService ]</br>
|
|
||||||
[ Replace *NUMBER* by the call number, e.g. 100 ]</br>
|
|
||||||
[ Replace *NAME* by the call name, e.g. OpenSystemAppletProxy ]</br>
|
|
||||||
[ e.g. appletAE IAllSystemAppletProxiesService: 100 (OpenSystemAppletProxy) ]
|
|
||||||
|
|
||||||
[ Add related links to the specific call from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) ]
|
|
||||||
|
|
||||||
### Service description
|
|
||||||
```
|
|
||||||
*
|
|
||||||
```
|
|
||||||
[ Include the description/explanation from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) in the above code block ]
|
|
||||||
|
|
||||||
### Required by:
|
|
||||||
[ Add our (games list database)[https://github.com/Ryujinx/Ryujinx-Games-List/issues] links of games who require this call ]
|
|
25
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
Normal file
25
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: Missing Service Call
|
||||||
|
description: Service call is missing in Ryujinx.
|
||||||
|
labels: not-implemented
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: instruction
|
||||||
|
attributes:
|
||||||
|
label: Service call
|
||||||
|
description: What service call is missing?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: name
|
||||||
|
attributes:
|
||||||
|
label: Service description
|
||||||
|
description: Include the description/explanation from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) in the above code block
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: required
|
||||||
|
attributes:
|
||||||
|
label: Required by
|
||||||
|
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this service.
|
||||||
|
validations:
|
||||||
|
required: true
|
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: Missing Shader Instruction
|
||||||
|
description: Shader Instruction is missing in Ryujinx.
|
||||||
|
title: "[GPU]"
|
||||||
|
labels: [gpu, not-implemented]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: instruction
|
||||||
|
attributes:
|
||||||
|
label: Shader instruction
|
||||||
|
description: What shader instruction is missing?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: required
|
||||||
|
attributes:
|
||||||
|
label: Required by
|
||||||
|
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
|
||||||
|
validations:
|
||||||
|
required: true
|
102
.github/workflows/build.yml
vendored
102
.github/workflows/build.yml
vendored
@ -18,10 +18,20 @@ on:
|
|||||||
- '*.yml'
|
- '*.yml'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: pr-checks-${{ github.event.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
RYUJINX_BASE_VERSION: "1.1.0"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.os }} (${{ matrix.configuration }})
|
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 45
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||||
@ -33,7 +43,7 @@ jobs:
|
|||||||
RELEASE_ZIP_OS_NAME: linux_x64
|
RELEASE_ZIP_OS_NAME: linux_x64
|
||||||
|
|
||||||
- os: macOS-latest
|
- os: macOS-latest
|
||||||
OS_NAME: MacOS x64
|
OS_NAME: macOS x64
|
||||||
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
||||||
RELEASE_ZIP_OS_NAME: osx_x64
|
RELEASE_ZIP_OS_NAME: osx_x64
|
||||||
|
|
||||||
@ -43,47 +53,107 @@ jobs:
|
|||||||
RELEASE_ZIP_OS_NAME: win_x64
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
env:
|
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
|
||||||
RYUJINX_BASE_VERSION: "1.1.0"
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v3
|
- uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
global-json-file: global.json
|
||||||
|
|
||||||
- name: Get git short hash
|
- name: Get git short hash
|
||||||
id: git_short_hash
|
id: git_short_hash
|
||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||||
|
|
||||||
- name: Publish Ryujinx
|
- name: Publish Ryujinx
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Headless.SDL2
|
- name: Publish Ryujinx.Headless.SDL2
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Ava
|
- name: Publish Ryujinx.Ava
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
|
- name: Set executable bit
|
||||||
|
run: |
|
||||||
|
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||||
|
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
|
||||||
|
chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh
|
||||||
|
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish
|
path: publish
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish_sdl2_headless
|
path: publish_sdl2_headless
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
- name: Upload Ryujinx.Ava artifact
|
- name: Upload Ryujinx.Ava artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||||
path: publish_ava
|
path: publish_ava
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||||
|
|
||||||
|
build_macos:
|
||||||
|
name: macOS Universal (${{ matrix.configuration }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
configuration: [ Debug, Release ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Setup LLVM 14
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 14
|
||||||
|
|
||||||
|
- name: Install rcodesign
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||||
|
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||||
|
rm apple-codesign.tar.gz
|
||||||
|
mv rcodesign $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get git short hash
|
||||||
|
id: git_short_hash
|
||||||
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Publish macOS
|
||||||
|
run: |
|
||||||
|
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||||
|
|
||||||
|
- name: Upload Ryujinx.Ava artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
|
||||||
|
path: "publish_ava/*.tar.gz"
|
||||||
|
if: github.event_name == 'pull_request'
|
172
.github/workflows/flatpak.yml
vendored
Normal file
172
.github/workflows/flatpak.yml
vendored
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
name: Flatpak release job
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
ryujinx_version:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
|
||||||
|
concurrency: flatpak-release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||||
|
GIT_COMMITTER_NAME: "RyujinxBot"
|
||||||
|
GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com"
|
||||||
|
RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj"
|
||||||
|
NUGET_SOURCES_DESTDIR: "nuget-sources"
|
||||||
|
RYUJINX_VERSION: "${{ inputs.ryujinx_version }}"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: Ryujinx
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
global-json-file: Ryujinx/global.json
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
working-directory: Ryujinx
|
||||||
|
run: |
|
||||||
|
echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: flathub/org.ryujinx.Ryujinx
|
||||||
|
token: ${{ secrets.RYUJINX_BOT_PAT }}
|
||||||
|
submodules: recursive
|
||||||
|
path: flathub
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: python -m pip install PyYAML lxml
|
||||||
|
|
||||||
|
- name: Restore Nuget packages
|
||||||
|
run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }}
|
||||||
|
|
||||||
|
- name: Generate nuget_sources.json
|
||||||
|
shell: python
|
||||||
|
run: |
|
||||||
|
from pathlib import Path
|
||||||
|
import base64
|
||||||
|
import binascii
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
sources = []
|
||||||
|
|
||||||
|
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||||
|
name = path.parent.parent.name
|
||||||
|
version = path.parent.name
|
||||||
|
filename = '{}.{}.nupkg'.format(name, version)
|
||||||
|
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||||
|
|
||||||
|
with path.open() as fp:
|
||||||
|
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||||
|
|
||||||
|
sources.append({
|
||||||
|
'type': 'file',
|
||||||
|
'url': url,
|
||||||
|
'sha512': sha512,
|
||||||
|
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||||
|
'dest-filename': filename,
|
||||||
|
})
|
||||||
|
|
||||||
|
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||||
|
json.dump(sources, fp, indent=4)
|
||||||
|
|
||||||
|
- name: Update flatpak metadata
|
||||||
|
id: metadata
|
||||||
|
env:
|
||||||
|
RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }}
|
||||||
|
shell: python
|
||||||
|
run: |
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
from datetime import datetime
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure we don't destroy multiline strings
|
||||||
|
def str_presenter(dumper, data):
|
||||||
|
if len(data.splitlines()) > 1:
|
||||||
|
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
||||||
|
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||||||
|
|
||||||
|
|
||||||
|
yaml.representer.SafeRepresenter.add_representer(str, str_presenter)
|
||||||
|
|
||||||
|
yaml_file = "flathub/org.ryujinx.Ryujinx.yml"
|
||||||
|
xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml"
|
||||||
|
|
||||||
|
with open(yaml_file, "r") as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
|
||||||
|
for source in data["modules"][0]["sources"]:
|
||||||
|
if type(source) is str:
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
source["type"] == "git"
|
||||||
|
and source["url"] == "https://github.com/Ryujinx/Ryujinx.git"
|
||||||
|
):
|
||||||
|
source["commit"] = os.environ['RYUJINX_GIT_HASH']
|
||||||
|
|
||||||
|
is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION']
|
||||||
|
|
||||||
|
with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out:
|
||||||
|
if is_same_version:
|
||||||
|
gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}")
|
||||||
|
else:
|
||||||
|
gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}")
|
||||||
|
|
||||||
|
if not is_same_version:
|
||||||
|
data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION']
|
||||||
|
|
||||||
|
with open(yaml_file, "w") as f:
|
||||||
|
yaml.safe_dump(data, f, sort_keys=False)
|
||||||
|
|
||||||
|
parser = etree.XMLParser(remove_blank_text=True)
|
||||||
|
tree = etree.parse(xml_file, parser)
|
||||||
|
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
releases = root.find("releases")
|
||||||
|
|
||||||
|
element = etree.Element("release")
|
||||||
|
element.set("version", os.environ['RYUJINX_VERSION'])
|
||||||
|
element.set("date", datetime.now().date().isoformat())
|
||||||
|
releases.insert(0, element)
|
||||||
|
|
||||||
|
# Ensure 4 spaces
|
||||||
|
etree.indent(root, space=" ")
|
||||||
|
|
||||||
|
with open(xml_file, "wb") as f:
|
||||||
|
f.write(
|
||||||
|
etree.tostring(
|
||||||
|
tree,
|
||||||
|
pretty_print=True,
|
||||||
|
encoding="UTF-8",
|
||||||
|
doctype='<?xml version="1.0" encoding="UTF-8"?>',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
- name: Push flatpak update
|
||||||
|
working-directory: flathub
|
||||||
|
env:
|
||||||
|
COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }}
|
||||||
|
run: |
|
||||||
|
git config user.name "${{ env.GIT_COMMITTER_NAME }}"
|
||||||
|
git config user.email "${{ env.GIT_COMMITTER_EMAIL }}"
|
||||||
|
git add .
|
||||||
|
git commit -m "$COMMIT_MESSAGE"
|
||||||
|
git push origin master
|
3
.github/workflows/nightly_pr_comment.yml
vendored
3
.github/workflows/nightly_pr_comment.yml
vendored
@ -7,6 +7,7 @@ jobs:
|
|||||||
pr_comment:
|
pr_comment:
|
||||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
@ -65,4 +66,4 @@ jobs:
|
|||||||
} else {
|
} else {
|
||||||
core.info(`Creating a comment`);
|
core.info(`Creating a comment`);
|
||||||
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
||||||
}
|
}
|
192
.github/workflows/release.yml
vendored
192
.github/workflows/release.yml
vendored
@ -13,89 +13,117 @@ on:
|
|||||||
|
|
||||||
concurrency: release
|
concurrency: release
|
||||||
|
|
||||||
|
env:
|
||||||
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
RYUJINX_BASE_VERSION: "1.1"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
tag:
|
||||||
|
name: Create tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Create tag
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.git.createRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||||
|
sha: context.sha
|
||||||
|
})
|
||||||
|
|
||||||
release:
|
release:
|
||||||
runs-on: windows-latest
|
name: Release ${{ matrix.OS_NAME }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
env:
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
strategy:
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
matrix:
|
||||||
RYUJINX_BASE_VERSION: "1.1"
|
os: [ ubuntu-latest, windows-latest ]
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
|
include:
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx"
|
- os: ubuntu-latest
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
|
OS_NAME: Linux x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: linux-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: linux_x64
|
||||||
|
|
||||||
|
- os: windows-latest
|
||||||
|
OS_NAME: Windows x64
|
||||||
|
DOTNET_RUNTIME_IDENTIFIER: win10-x64
|
||||||
|
RELEASE_ZIP_OS_NAME: win_x64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v3
|
- uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
global-json-file: global.json
|
||||||
|
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
run: |
|
run: |
|
||||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Configure for release
|
- name: Configure for release
|
||||||
run: |
|
run: |
|
||||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create output dir
|
- name: Create output dir
|
||||||
run: "mkdir release_output"
|
run: "mkdir release_output"
|
||||||
- name: Publish Windows
|
|
||||||
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_gtk/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
||||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
||||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
|
dotnet publish -c Release -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_windows
|
pushd publish_gtk
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_windows_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_windows_ava
|
pushd publish_ava
|
||||||
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Publish Linux
|
|
||||||
run: |
|
|
||||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
|
||||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
|
|
||||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
|
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_linux
|
pushd publish_gtk
|
||||||
tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
|
||||||
gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
|
||||||
rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_linux_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
tar --exclude "publish/Ryujinx.Headless.SDL2" --exclude "publish/Ryujinx.sh" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2"
|
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
|
||||||
gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
|
||||||
rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_linux_ava
|
pushd publish_ava
|
||||||
tar --exclude "publish/Ryujinx.Ava" --exclude "publish/Ryujinx.sh" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
||||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Ava" "publish/Ryujinx.Ava"
|
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
|
||||||
gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
|
||||||
rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@ -105,10 +133,78 @@ jobs:
|
|||||||
name: ${{ steps.version_info.outputs.build_version }}
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: "For more informations about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
removeArtifacts: true
|
|
||||||
replacesArtifacts: true
|
replacesArtifacts: true
|
||||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
macos_release:
|
||||||
|
name: Release MacOS universal
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Setup LLVM 14
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 14
|
||||||
|
|
||||||
|
- name: Install rcodesign
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||||
|
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||||
|
rm apple-codesign.tar.gz
|
||||||
|
mv rcodesign $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Configure for release
|
||||||
|
run: |
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Publish macOS
|
||||||
|
run: |
|
||||||
|
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||||
|
|
||||||
|
- name: Pushing new release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
artifacts: "publish_ava/*.tar.gz"
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
allowUpdates: true
|
||||||
|
replacesArtifacts: true
|
||||||
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
flatpak_release:
|
||||||
|
uses: ./.github/workflows/flatpak.yml
|
||||||
|
needs: release
|
||||||
|
with:
|
||||||
|
ryujinx_version: "1.1.${{ github.run_number }}"
|
||||||
|
secrets: inherit
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -170,3 +170,6 @@ launchSettings.json
|
|||||||
|
|
||||||
# NetCore Publishing Profiles
|
# NetCore Publishing Profiles
|
||||||
PublishProfiles/
|
PublishProfiles/
|
||||||
|
|
||||||
|
# Glade backup files
|
||||||
|
*.glade~
|
||||||
|
@ -1,173 +0,0 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
|
||||||
using System;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen.Arm64
|
|
||||||
{
|
|
||||||
static class CodeGenCommon
|
|
||||||
{
|
|
||||||
public const int TcAddressRegister = 8;
|
|
||||||
public const int ReservedRegister = 17;
|
|
||||||
|
|
||||||
public static bool ConstFitsOnSImm7(int value, int scale)
|
|
||||||
{
|
|
||||||
return (((value >> scale) << 25) >> (25 - scale)) == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ConstFitsOnSImm9(int value)
|
|
||||||
{
|
|
||||||
return ((value << 23) >> 23) == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ConstFitsOnUImm12(int value)
|
|
||||||
{
|
|
||||||
return (value & 0xfff) == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ConstFitsOnUImm12(int value, OperandType type)
|
|
||||||
{
|
|
||||||
int scale = Assembler.GetScaleForType(type);
|
|
||||||
return (((value >> scale) & 0xfff) << scale) == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryEncodeBitMask(Operand operand, out int immN, out int immS, out int immR)
|
|
||||||
{
|
|
||||||
ulong value = operand.Value;
|
|
||||||
|
|
||||||
if (operand.Type == OperandType.I32)
|
|
||||||
{
|
|
||||||
value |= value << 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TryEncodeBitMask(value, out immN, out immS, out immR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryEncodeBitMask(ulong value, out int immN, out int immS, out int immR)
|
|
||||||
{
|
|
||||||
// Some special values also can't be encoded:
|
|
||||||
// 0 can't be encoded because we need to subtract 1 from onesCount (which would became negative if 0).
|
|
||||||
// A value with all bits set can't be encoded because it is reserved according to the spec, because:
|
|
||||||
// Any value AND all ones will be equal itself, so it's effectively a no-op.
|
|
||||||
// Any value OR all ones will be equal all ones, so one can just use MOV.
|
|
||||||
// Any value XOR all ones will be equal its inverse, so one can just use MVN.
|
|
||||||
if (value == ulong.MaxValue)
|
|
||||||
{
|
|
||||||
immN = 0;
|
|
||||||
immS = 0;
|
|
||||||
immR = 0;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bitLength = CountSequence(value);
|
|
||||||
|
|
||||||
if ((value >> bitLength) != 0)
|
|
||||||
{
|
|
||||||
bitLength += CountSequence(value >> bitLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
int bitLengthLog2 = BitOperations.Log2((uint)bitLength);
|
|
||||||
int bitLengthPow2 = 1 << bitLengthLog2;
|
|
||||||
|
|
||||||
if (bitLengthPow2 < bitLength)
|
|
||||||
{
|
|
||||||
bitLengthLog2++;
|
|
||||||
bitLengthPow2 <<= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int selectedESize = 64;
|
|
||||||
int repetitions = 1;
|
|
||||||
int onesCount = BitOperations.PopCount(value);
|
|
||||||
|
|
||||||
if (bitLengthPow2 < 64 && (value >> bitLengthPow2) != 0)
|
|
||||||
{
|
|
||||||
for (int eSizeLog2 = bitLengthLog2; eSizeLog2 < 6; eSizeLog2++)
|
|
||||||
{
|
|
||||||
bool match = true;
|
|
||||||
int eSize = 1 << eSizeLog2;
|
|
||||||
ulong mask = (1UL << eSize) - 1;
|
|
||||||
ulong eValue = value & mask;
|
|
||||||
|
|
||||||
for (int e = 1; e < 64 / eSize; e++)
|
|
||||||
{
|
|
||||||
if (((value >> (e * eSize)) & mask) != eValue)
|
|
||||||
{
|
|
||||||
match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match)
|
|
||||||
{
|
|
||||||
selectedESize = eSize;
|
|
||||||
repetitions = 64 / eSize;
|
|
||||||
onesCount = BitOperations.PopCount(eValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find rotation. We have two cases, one where the highest bit is 0
|
|
||||||
// and one where it is 1.
|
|
||||||
// If it's 1, we just need to count the number of 1 bits on the MSB to find the right rotation.
|
|
||||||
// If it's 0, we just need to count the number of 0 bits on the LSB to find the left rotation,
|
|
||||||
// then we can convert it to the right rotation shift by subtracting the value from the element size.
|
|
||||||
int rotation;
|
|
||||||
long vHigh = (long)(value << (64 - selectedESize));
|
|
||||||
if (vHigh < 0)
|
|
||||||
{
|
|
||||||
rotation = BitOperations.LeadingZeroCount(~(ulong)vHigh);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rotation = (selectedESize - BitOperations.TrailingZeroCount(value)) & (selectedESize - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconstruct value and see if it matches. If not, we can't encode.
|
|
||||||
ulong reconstructed = onesCount == 64 ? ulong.MaxValue : RotateRight((1UL << onesCount) - 1, rotation, selectedESize);
|
|
||||||
|
|
||||||
for (int bit = 32; bit >= selectedESize; bit >>= 1)
|
|
||||||
{
|
|
||||||
reconstructed |= reconstructed << bit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reconstructed != value || onesCount == 0)
|
|
||||||
{
|
|
||||||
immN = 0;
|
|
||||||
immS = 0;
|
|
||||||
immR = 0;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
immR = rotation;
|
|
||||||
|
|
||||||
// immN indicates that there are no repetitions.
|
|
||||||
// The MSB of immS indicates the amount of repetitions, and the LSB the number of bits set.
|
|
||||||
if (repetitions == 1)
|
|
||||||
{
|
|
||||||
immN = 1;
|
|
||||||
immS = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
immN = 0;
|
|
||||||
immS = (0xf80 >> BitOperations.Log2((uint)repetitions)) & 0x3f;
|
|
||||||
}
|
|
||||||
|
|
||||||
immS |= onesCount - 1;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int CountSequence(ulong value)
|
|
||||||
{
|
|
||||||
return BitOperations.TrailingZeroCount(value) + BitOperations.TrailingZeroCount(~value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ulong RotateRight(ulong bits, int shift, int size)
|
|
||||||
{
|
|
||||||
return (bits >> shift) | ((bits << (size - shift)) & (size == 64 ? ulong.MaxValue : (1UL << size) - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
namespace ARMeilleure.Decoders;
|
|
||||||
|
|
||||||
interface IOpCode32Exception
|
|
||||||
{
|
|
||||||
int Id { get; }
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Emit;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
static class DelegateHelper
|
|
||||||
{
|
|
||||||
private const string DelegateTypesAssemblyName = "JitDelegateTypes";
|
|
||||||
|
|
||||||
private static readonly ModuleBuilder _modBuilder;
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, Type> _delegateTypesCache;
|
|
||||||
|
|
||||||
static DelegateHelper()
|
|
||||||
{
|
|
||||||
AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run);
|
|
||||||
|
|
||||||
_modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
|
|
||||||
|
|
||||||
_delegateTypesCache = new Dictionary<string, Type>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Delegate GetDelegate(MethodInfo info)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(info);
|
|
||||||
|
|
||||||
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
|
|
||||||
Type returnType = info.ReturnType;
|
|
||||||
|
|
||||||
Type delegateType = GetDelegateType(parameters, returnType);
|
|
||||||
|
|
||||||
return Delegate.CreateDelegate(delegateType, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type GetDelegateType(Type[] parameters, Type returnType)
|
|
||||||
{
|
|
||||||
string key = GetFunctionSignatureKey(parameters, returnType);
|
|
||||||
|
|
||||||
if (!_delegateTypesCache.TryGetValue(key, out Type delegateType))
|
|
||||||
{
|
|
||||||
delegateType = MakeDelegateType(parameters, returnType, key);
|
|
||||||
|
|
||||||
_delegateTypesCache.TryAdd(key, delegateType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return delegateType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetFunctionSignatureKey(Type[] parameters, Type returnType)
|
|
||||||
{
|
|
||||||
string sig = GetTypeName(returnType);
|
|
||||||
|
|
||||||
foreach (Type type in parameters)
|
|
||||||
{
|
|
||||||
sig += '_' + GetTypeName(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetTypeName(Type type)
|
|
||||||
{
|
|
||||||
return type.FullName.Replace(".", string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
private const MethodAttributes CtorAttributes =
|
|
||||||
MethodAttributes.RTSpecialName |
|
|
||||||
MethodAttributes.HideBySig |
|
|
||||||
MethodAttributes.Public;
|
|
||||||
|
|
||||||
private const TypeAttributes DelegateTypeAttributes =
|
|
||||||
TypeAttributes.Class |
|
|
||||||
TypeAttributes.Public |
|
|
||||||
TypeAttributes.Sealed |
|
|
||||||
TypeAttributes.AnsiClass |
|
|
||||||
TypeAttributes.AutoClass;
|
|
||||||
|
|
||||||
private const MethodImplAttributes ImplAttributes =
|
|
||||||
MethodImplAttributes.Runtime |
|
|
||||||
MethodImplAttributes.Managed;
|
|
||||||
|
|
||||||
private const MethodAttributes InvokeAttributes =
|
|
||||||
MethodAttributes.Public |
|
|
||||||
MethodAttributes.HideBySig |
|
|
||||||
MethodAttributes.NewSlot |
|
|
||||||
MethodAttributes.Virtual;
|
|
||||||
|
|
||||||
private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) };
|
|
||||||
|
|
||||||
private static Type MakeDelegateType(Type[] parameters, Type returnType, string name)
|
|
||||||
{
|
|
||||||
TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate));
|
|
||||||
|
|
||||||
builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes);
|
|
||||||
|
|
||||||
builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);
|
|
||||||
|
|
||||||
return builder.CreateTypeInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
using ARMeilleure.Instructions;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
|
||||||
{
|
|
||||||
static class Delegates
|
|
||||||
{
|
|
||||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr)
|
|
||||||
{
|
|
||||||
if (index >= 0 && index < _delegates.Count)
|
|
||||||
{
|
|
||||||
funcPtr = _delegates.Values[index].FuncPtr; // O(1).
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
funcPtr = default;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr GetDelegateFuncPtrByIndex(int index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= _delegates.Count)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException($"({nameof(index)} = {index})");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _delegates.Values[index].FuncPtr; // O(1).
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(info);
|
|
||||||
|
|
||||||
string key = GetKey(info);
|
|
||||||
|
|
||||||
if (!_delegates.TryGetValue(key, out DelegateInfo dlgInfo)) // O(log(n)).
|
|
||||||
{
|
|
||||||
throw new KeyNotFoundException($"({nameof(key)} = {key})");
|
|
||||||
}
|
|
||||||
|
|
||||||
return dlgInfo.FuncPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetDelegateIndex(MethodInfo info)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(info);
|
|
||||||
|
|
||||||
string key = GetKey(info);
|
|
||||||
|
|
||||||
int index = _delegates.IndexOfKey(key); // O(log(n)).
|
|
||||||
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
throw new KeyNotFoundException($"({nameof(key)} = {key})");
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetDelegateInfo(MethodInfo info)
|
|
||||||
{
|
|
||||||
string key = GetKey(info);
|
|
||||||
|
|
||||||
Delegate dlg = DelegateHelper.GetDelegate(info);
|
|
||||||
|
|
||||||
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetKey(MethodInfo info)
|
|
||||||
{
|
|
||||||
return $"{info.DeclaringType.Name}.{info.Name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly SortedList<string, DelegateInfo> _delegates;
|
|
||||||
|
|
||||||
static Delegates()
|
|
||||||
{
|
|
||||||
_delegates = new SortedList<string, DelegateInfo>();
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) }));
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) }));
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) }));
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }));
|
|
||||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Truncate), new Type[] { typeof(double) }));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) }));
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Ceiling), new Type[] { typeof(float) }));
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) }));
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }));
|
|
||||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Truncate), new Type[] { typeof(float) }));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)));
|
|
||||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
|
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,40 +3,40 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Desktop" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="Crc32.NET" Version="1.2.0" />
|
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
<PackageVersion Include="DynamicData" Version="7.12.11" />
|
<PackageVersion Include="DynamicData" Version="7.14.2" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
||||||
<PackageVersion Include="LibHac" Version="0.17.0" />
|
<PackageVersion Include="LibHac" Version="0.18.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.1" />
|
||||||
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.1-build23" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.1" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||||
@ -44,11 +44,10 @@
|
|||||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.26.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" />
|
||||||
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
<PackageVersion Include="System.Management" Version="7.0.1" />
|
||||||
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
||||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 of those being considered playable.
|
As of April 2023, Ryujinx has been tested on approximately 4,050 titles; over 4,000 boot past menus and into gameplay, with roughly 3,400 of those being considered playable.
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -96,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
|||||||
|
|
||||||
- **GPU**
|
- **GPU**
|
||||||
|
|
||||||
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently six graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Anti-Aliasing, Scaling Filters (including FSR), Anisotropic Filtering and Aspect Ratio Adjustment. These enhancements can be adjusted or toggled as desired in the GUI.
|
||||||
|
|
||||||
- **Input**
|
- **Input**
|
||||||
|
|
||||||
|
@ -1,177 +0,0 @@
|
|||||||
<UserControl
|
|
||||||
x:Class="Ryujinx.Ava.UI.Controls.GameGridView"
|
|
||||||
xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
|
||||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
|
||||||
d:DesignHeight="450"
|
|
||||||
d:DesignWidth="800"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Focusable="True">
|
|
||||||
<UserControl.Resources>
|
|
||||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
|
||||||
<MenuFlyout x:Key="GameContextMenu">
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding ToggleFavorite}"
|
|
||||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
|
|
||||||
<Separator />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenUserSaveDirectory}"
|
|
||||||
IsEnabled="{Binding EnabledUserSaveDirectory}"
|
|
||||||
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenDeviceSaveDirectory}"
|
|
||||||
IsEnabled="{Binding EnabledDeviceSaveDirectory}"
|
|
||||||
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenBcatSaveDirectory}"
|
|
||||||
IsEnabled="{Binding EnabledBcatSaveDirectory}"
|
|
||||||
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
|
|
||||||
<Separator />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenTitleUpdateManager}"
|
|
||||||
Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenDownloadableContentManager}"
|
|
||||||
Header="{locale:Locale GameListContextMenuManageDlc}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenCheatManager}"
|
|
||||||
Header="{locale:Locale GameListContextMenuManageCheat}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenModsDirectory}"
|
|
||||||
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenSdModsDirectory}"
|
|
||||||
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
|
|
||||||
<Separator />
|
|
||||||
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding PurgePtcCache}"
|
|
||||||
Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding PurgeShaderCache}"
|
|
||||||
Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenPtcDirectory}"
|
|
||||||
Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding OpenShaderCacheDirectory}"
|
|
||||||
Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding ExtractExeFs}"
|
|
||||||
Header="{locale:Locale GameListContextMenuExtractDataExeFS}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding ExtractRomFs}"
|
|
||||||
Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
|
|
||||||
<MenuItem
|
|
||||||
Command="{Binding ExtractLogo}"
|
|
||||||
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
|
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
|
|
||||||
</MenuItem>
|
|
||||||
</MenuFlyout>
|
|
||||||
</UserControl.Resources>
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<ListBox
|
|
||||||
Grid.Row="0"
|
|
||||||
Padding="8"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
ContextFlyout="{StaticResource GameContextMenu}"
|
|
||||||
DoubleTapped="GameList_DoubleTapped"
|
|
||||||
Items="{Binding AppsObservableList}"
|
|
||||||
SelectionChanged="GameList_SelectionChanged">
|
|
||||||
<ListBox.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<flex:FlexPanel
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
AlignContent="FlexStart"
|
|
||||||
JustifyContent="Center" />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ListBox.ItemsPanel>
|
|
||||||
<ListBox.Styles>
|
|
||||||
<Style Selector="ListBoxItem">
|
|
||||||
<Setter Property="Margin" Value="5" />
|
|
||||||
<Setter Property="CornerRadius" Value="4" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
|
|
||||||
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" />
|
|
||||||
</Style>
|
|
||||||
</ListBox.Styles>
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Grid>
|
|
||||||
<Border
|
|
||||||
Margin="10"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
|
|
||||||
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
|
|
||||||
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
|
|
||||||
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
|
|
||||||
ClipToBounds="True"
|
|
||||||
CornerRadius="4">
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Image
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
|
|
||||||
<Panel
|
|
||||||
Grid.Row="1"
|
|
||||||
Height="50"
|
|
||||||
Margin="0 10 0 0"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}">
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{Binding TitleName}"
|
|
||||||
TextAlignment="Center"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
</Panel>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
<ui:SymbolIcon
|
|
||||||
Margin="5,5,0,0"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
FontSize="16"
|
|
||||||
Foreground="{DynamicResource SystemAccentColor}"
|
|
||||||
IsVisible="{Binding Favorite}"
|
|
||||||
Symbol="StarFilled" />
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListBox.ItemTemplate>
|
|
||||||
</ListBox>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
@ -1,32 +0,0 @@
|
|||||||
<UserControl
|
|
||||||
x:Class="Ryujinx.Ava.UI.Controls.InputDialog"
|
|
||||||
xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Focusable="True">
|
|
||||||
<Grid
|
|
||||||
Margin="5,10,5,5"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Center">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock HorizontalAlignment="Center" Text="{Binding Message}" />
|
|
||||||
<TextBox
|
|
||||||
Grid.Row="1"
|
|
||||||
Width="300"
|
|
||||||
Margin="10"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
MaxLength="{Binding MaxLength}"
|
|
||||||
Text="{Binding Input, Mode=TwoWay}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="2"
|
|
||||||
Margin="5,5,5,10"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Text="{Binding SubMessage}" />
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
@ -1,57 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
using FluentAvalonia.UI.Controls;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
|
||||||
{
|
|
||||||
public partial class InputDialog : UserControl
|
|
||||||
{
|
|
||||||
public string Message { get; set; }
|
|
||||||
public string Input { get; set; }
|
|
||||||
public string SubMessage { get; set; }
|
|
||||||
|
|
||||||
public uint MaxLength { get; }
|
|
||||||
|
|
||||||
public InputDialog(string message, string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
|
||||||
{
|
|
||||||
Message = message;
|
|
||||||
Input = input;
|
|
||||||
SubMessage = subMessage;
|
|
||||||
MaxLength = maxLength;
|
|
||||||
|
|
||||||
DataContext = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputDialog()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, string message,
|
|
||||||
string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
|
||||||
{
|
|
||||||
UserResult result = UserResult.Cancel;
|
|
||||||
|
|
||||||
InputDialog content = new InputDialog(message, input, subMessage, maxLength);
|
|
||||||
ContentDialog contentDialog = new ContentDialog
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
|
||||||
SecondaryButtonText = "",
|
|
||||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel],
|
|
||||||
Content = content,
|
|
||||||
PrimaryButtonCommand = MiniCommand.Create(() =>
|
|
||||||
{
|
|
||||||
result = UserResult.Ok;
|
|
||||||
input = content.Input;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
await contentDialog.ShowAsync();
|
|
||||||
|
|
||||||
return (result, input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Avalonia;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Helpers
|
|
||||||
{
|
|
||||||
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
static partial class MetalHelper
|
|
||||||
{
|
|
||||||
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
|
|
||||||
|
|
||||||
private struct Selector
|
|
||||||
{
|
|
||||||
public readonly IntPtr NativePtr;
|
|
||||||
|
|
||||||
public unsafe Selector(string value)
|
|
||||||
{
|
|
||||||
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
|
|
||||||
byte* data = stackalloc byte[size];
|
|
||||||
|
|
||||||
fixed (char* pValue = value)
|
|
||||||
{
|
|
||||||
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
NativePtr = sel_registerName(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator Selector(string value) => new Selector(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static unsafe IntPtr GetClass(string value)
|
|
||||||
{
|
|
||||||
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
|
|
||||||
byte* data = stackalloc byte[size];
|
|
||||||
|
|
||||||
fixed (char* pValue = value)
|
|
||||||
{
|
|
||||||
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objc_getClass(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct NSPoint
|
|
||||||
{
|
|
||||||
public double X;
|
|
||||||
public double Y;
|
|
||||||
|
|
||||||
public NSPoint(double x, double y)
|
|
||||||
{
|
|
||||||
X = x;
|
|
||||||
Y = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct NSRect
|
|
||||||
{
|
|
||||||
public NSPoint Pos;
|
|
||||||
public NSPoint Size;
|
|
||||||
|
|
||||||
public NSRect(double x, double y, double width, double height)
|
|
||||||
{
|
|
||||||
Pos = new NSPoint(x, y);
|
|
||||||
Size = new NSPoint(width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr GetMetalLayer(out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds)
|
|
||||||
{
|
|
||||||
// Create a new CAMetalLayer.
|
|
||||||
IntPtr layerClass = GetClass("CAMetalLayer");
|
|
||||||
IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc");
|
|
||||||
objc_msgSend(metalLayer, "init");
|
|
||||||
|
|
||||||
// Create a child NSView to render into.
|
|
||||||
IntPtr nsViewClass = GetClass("NSView");
|
|
||||||
IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc");
|
|
||||||
objc_msgSend(child, "init", new NSRect(0, 0, 0, 0));
|
|
||||||
|
|
||||||
// Make its renderer our metal layer.
|
|
||||||
objc_msgSend(child, "setWantsLayer:", (byte)1);
|
|
||||||
objc_msgSend(child, "setLayer:", metalLayer);
|
|
||||||
objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
|
||||||
|
|
||||||
// Ensure the scale factor is up to date.
|
|
||||||
updateBounds = (Rect rect) => {
|
|
||||||
objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
|
||||||
};
|
|
||||||
|
|
||||||
nsView = child;
|
|
||||||
return metalLayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DestroyMetalLayer(IntPtr nsView, IntPtr metalLayer)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static unsafe partial IntPtr sel_registerName(byte* data);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static unsafe partial IntPtr objc_getClass(byte* data);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")]
|
|
||||||
private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models
|
|
||||||
{
|
|
||||||
public class Amiibo
|
|
||||||
{
|
|
||||||
public struct AmiiboJson
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; }
|
|
||||||
[JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AmiiboApi
|
|
||||||
{
|
|
||||||
[JsonPropertyName("name")] public string Name { get; set; }
|
|
||||||
[JsonPropertyName("head")] public string Head { get; set; }
|
|
||||||
[JsonPropertyName("tail")] public string Tail { get; set; }
|
|
||||||
[JsonPropertyName("image")] public string Image { get; set; }
|
|
||||||
[JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; }
|
|
||||||
[JsonPropertyName("character")] public string Character { get; set; }
|
|
||||||
[JsonPropertyName("gameSeries")] public string GameSeries { get; set; }
|
|
||||||
[JsonPropertyName("type")] public string Type { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetId()
|
|
||||||
{
|
|
||||||
return Head + Tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is AmiiboApi amiibo)
|
|
||||||
{
|
|
||||||
return amiibo.Head + amiibo.Tail == Head + Tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return base.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AmiiboApiGamesSwitch
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gameID")] public List<string> GameId { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gameName")] public string GameName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AmiiboApiUsage
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Usage")] public string Usage { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("write")] public bool Write { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,250 +0,0 @@
|
|||||||
using Avalonia.Collections;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
using LibHac.Common;
|
|
||||||
using LibHac.Fs;
|
|
||||||
using LibHac.Fs.Fsa;
|
|
||||||
using LibHac.FsSystem;
|
|
||||||
using LibHac.Ns;
|
|
||||||
using LibHac.Tools.FsSystem;
|
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
|
||||||
using Ryujinx.HLE.HOS;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Path = System.IO.Path;
|
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels;
|
|
||||||
|
|
||||||
public class TitleUpdateViewModel : BaseModel
|
|
||||||
{
|
|
||||||
public TitleUpdateMetadata _titleUpdateWindowData;
|
|
||||||
public readonly string _titleUpdateJsonPath;
|
|
||||||
private VirtualFileSystem _virtualFileSystem { get; }
|
|
||||||
private ulong _titleId { get; }
|
|
||||||
private string _titleName { get; }
|
|
||||||
|
|
||||||
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
|
||||||
private AvaloniaList<object> _views = new();
|
|
||||||
private object _selectedUpdate;
|
|
||||||
|
|
||||||
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
|
||||||
{
|
|
||||||
get => _titleUpdates;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_titleUpdates = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AvaloniaList<object> Views
|
|
||||||
{
|
|
||||||
get => _views;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_views = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public object SelectedUpdate
|
|
||||||
{
|
|
||||||
get => _selectedUpdate;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_selectedUpdate = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
|
||||||
{
|
|
||||||
_virtualFileSystem = virtualFileSystem;
|
|
||||||
|
|
||||||
_titleId = titleId;
|
|
||||||
_titleName = titleName;
|
|
||||||
|
|
||||||
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
|
||||||
|
|
||||||
_titleUpdateWindowData = new TitleUpdateMetadata
|
|
||||||
{
|
|
||||||
Selected = "",
|
|
||||||
Paths = new List<string>()
|
|
||||||
};
|
|
||||||
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadUpdates()
|
|
||||||
{
|
|
||||||
foreach (string path in _titleUpdateWindowData.Paths)
|
|
||||||
{
|
|
||||||
AddUpdate(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Save the list again to remove leftovers.
|
|
||||||
Save();
|
|
||||||
|
|
||||||
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
|
||||||
|
|
||||||
SelectedUpdate = selected;
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SortUpdates()
|
|
||||||
{
|
|
||||||
var list = TitleUpdates.ToList();
|
|
||||||
|
|
||||||
list.Sort((first, second) =>
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
Views.Clear();
|
|
||||||
Views.Add(new BaseModel());
|
|
||||||
Views.AddRange(list);
|
|
||||||
|
|
||||||
if (SelectedUpdate == null)
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[0];
|
|
||||||
}
|
|
||||||
else if (!TitleUpdates.Contains(SelectedUpdate))
|
|
||||||
{
|
|
||||||
if (Views.Count > 1)
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddUpdate(string path)
|
|
||||||
{
|
|
||||||
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
|
||||||
{
|
|
||||||
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
|
||||||
|
|
||||||
if (controlNca != null && patchNca != null)
|
|
||||||
{
|
|
||||||
ApplicationControlProperty controlData = new();
|
|
||||||
|
|
||||||
using UniqueRef<IFile> nacpFile = new();
|
|
||||||
|
|
||||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
|
||||||
|
|
||||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveUpdate(TitleUpdateModel update)
|
|
||||||
{
|
|
||||||
TitleUpdates.Remove(update);
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Add()
|
|
||||||
{
|
|
||||||
OpenFileDialog dialog = new()
|
|
||||||
{
|
|
||||||
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
|
||||||
AllowMultiple = true
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.Filters.Add(new FileDialogFilter
|
|
||||||
{
|
|
||||||
Name = "NSP",
|
|
||||||
Extensions = { "nsp" }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
||||||
{
|
|
||||||
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
|
||||||
|
|
||||||
if (files != null)
|
|
||||||
{
|
|
||||||
foreach (string file in files)
|
|
||||||
{
|
|
||||||
AddUpdate(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData.Paths.Clear();
|
|
||||||
_titleUpdateWindowData.Selected = "";
|
|
||||||
|
|
||||||
foreach (TitleUpdateModel update in TitleUpdates)
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData.Paths.Add(update.Path);
|
|
||||||
|
|
||||||
if (update == SelectedUpdate)
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData.Selected = update.Path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<UserControl
|
|
||||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView"
|
|
||||||
xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
|
||||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
x:CompileBindings="True"
|
|
||||||
x:DataType="viewModels:SettingsViewModel">
|
|
||||||
<Design.DataContext>
|
|
||||||
<viewModels:SettingsViewModel />
|
|
||||||
</Design.DataContext>
|
|
||||||
<ScrollViewer
|
|
||||||
Name="InputPage"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
HorizontalScrollBarVisibility="Disabled"
|
|
||||||
VerticalScrollBarVisibility="Auto">
|
|
||||||
<Border Classes="settings">
|
|
||||||
<StackPanel Margin="4" Orientation="Vertical">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<CheckBox Margin="5,0"
|
|
||||||
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
|
|
||||||
IsChecked="{Binding EnableDockedMode}">
|
|
||||||
<TextBlock VerticalAlignment="Center"
|
|
||||||
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
|
|
||||||
</CheckBox>
|
|
||||||
<CheckBox Margin="5,0"
|
|
||||||
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
|
|
||||||
IsChecked="{Binding EnableKeyboard}">
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
|
|
||||||
</CheckBox>
|
|
||||||
<CheckBox Margin="5,0"
|
|
||||||
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
|
|
||||||
IsChecked="{Binding EnableMouse}">
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
|
|
||||||
</CheckBox>
|
|
||||||
</StackPanel>
|
|
||||||
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0" MinHeight="600" />
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</ScrollViewer>
|
|
||||||
</UserControl>
|
|
@ -1,172 +0,0 @@
|
|||||||
<window:StyleableWindow
|
|
||||||
x:Class="Ryujinx.Ava.UI.Windows.DownloadableContentManagerWindow"
|
|
||||||
xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
|
||||||
Width="800"
|
|
||||||
Height="500"
|
|
||||||
MinWidth="800"
|
|
||||||
MinHeight="500"
|
|
||||||
MaxWidth="800"
|
|
||||||
MaxHeight="500"
|
|
||||||
SizeToContent="Height"
|
|
||||||
WindowStartupLocation="CenterOwner"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Focusable="True">
|
|
||||||
<Grid Name="DownloadableContentGrid" Margin="15">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock
|
|
||||||
Name="Heading"
|
|
||||||
Grid.Row="1"
|
|
||||||
MaxWidth="500"
|
|
||||||
Margin="20,15,20,20"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
LineHeight="18"
|
|
||||||
TextAlignment="Center"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
<DockPanel
|
|
||||||
Grid.Row="2"
|
|
||||||
Margin="0"
|
|
||||||
HorizontalAlignment="Left">
|
|
||||||
<Button
|
|
||||||
Name="EnableAllButton"
|
|
||||||
MinWidth="90"
|
|
||||||
Margin="5"
|
|
||||||
Command="{Binding EnableAll}">
|
|
||||||
<TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
Name="DisableAllButton"
|
|
||||||
MinWidth="90"
|
|
||||||
Margin="5"
|
|
||||||
Command="{Binding DisableAll}">
|
|
||||||
<TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" />
|
|
||||||
</Button>
|
|
||||||
</DockPanel>
|
|
||||||
<Border
|
|
||||||
Grid.Row="3"
|
|
||||||
Margin="5"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
BorderBrush="Gray"
|
|
||||||
BorderThickness="1">
|
|
||||||
<ScrollViewer
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto">
|
|
||||||
<DataGrid
|
|
||||||
Name="DlcDataGrid"
|
|
||||||
MinHeight="200"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="True"
|
|
||||||
CanUserSortColumns="True"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
Items="{Binding _downloadableContents}"
|
|
||||||
SelectionMode="Extended"
|
|
||||||
VerticalScrollBarVisibility="Auto">
|
|
||||||
<DataGrid.Styles>
|
|
||||||
<Styles>
|
|
||||||
<Style Selector="DataGridCell:nth-child(3), DataGridCell:nth-child(4)">
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
|
||||||
</Style>
|
|
||||||
</Styles>
|
|
||||||
<Styles>
|
|
||||||
<Style Selector="DataGridCell:nth-child(1)">
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Right" />
|
|
||||||
</Style>
|
|
||||||
</Styles>
|
|
||||||
</DataGrid.Styles>
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Width="90">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<CheckBox
|
|
||||||
Width="50"
|
|
||||||
MinWidth="40"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
IsChecked="{Binding Enabled}" />
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataGridTemplateColumn.Header>
|
|
||||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" />
|
|
||||||
</DataGridTemplateColumn.Header>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
<DataGridTextColumn Width="140" Binding="{Binding TitleId}">
|
|
||||||
<DataGridTextColumn.Header>
|
|
||||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingTitleIdLabel}" />
|
|
||||||
</DataGridTextColumn.Header>
|
|
||||||
</DataGridTextColumn>
|
|
||||||
<DataGridTextColumn Width="280" Binding="{Binding FullPath}">
|
|
||||||
<DataGridTextColumn.Header>
|
|
||||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingFullPathLabel}" />
|
|
||||||
</DataGridTextColumn.Header>
|
|
||||||
</DataGridTextColumn>
|
|
||||||
<DataGridTextColumn Binding="{Binding ContainerPath}">
|
|
||||||
<DataGridTextColumn.Header>
|
|
||||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingContainerPathLabel}" />
|
|
||||||
</DataGridTextColumn.Header>
|
|
||||||
</DataGridTextColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Border>
|
|
||||||
<DockPanel
|
|
||||||
Grid.Row="4"
|
|
||||||
Margin="0"
|
|
||||||
HorizontalAlignment="Stretch">
|
|
||||||
<DockPanel Margin="0" HorizontalAlignment="Left">
|
|
||||||
<Button
|
|
||||||
Name="AddButton"
|
|
||||||
MinWidth="90"
|
|
||||||
Margin="5"
|
|
||||||
Command="{Binding Add}">
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
Name="RemoveButton"
|
|
||||||
MinWidth="90"
|
|
||||||
Margin="5"
|
|
||||||
Command="{Binding RemoveSelected}">
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabGeneralRemove}" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
Name="RemoveAllButton"
|
|
||||||
MinWidth="90"
|
|
||||||
Margin="5"
|
|
||||||
Command="{Binding RemoveAll}">
|
|
||||||
<TextBlock Text="{locale:Locale DlcManagerRemoveAllButton}" />
|
|
||||||
</Button>
|
|
||||||
</DockPanel>
|
|
||||||
<DockPanel Margin="0" HorizontalAlignment="Right">
|
|
||||||
<Button
|
|
||||||
Name="SaveButton"
|
|
||||||
MinWidth="90"
|
|
||||||
Margin="5"
|
|
||||||
Command="{Binding SaveAndClose}">
|
|
||||||
<TextBlock Text="{locale:Locale SettingsButtonSave}" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
Name="CancelButton"
|
|
||||||
MinWidth="90"
|
|
||||||
Margin="5"
|
|
||||||
Command="{Binding Close}">
|
|
||||||
<TextBlock Text="{locale:Locale InputDialogCancel}" />
|
|
||||||
</Button>
|
|
||||||
</DockPanel>
|
|
||||||
</DockPanel>
|
|
||||||
</Grid>
|
|
||||||
</window:StyleableWindow>
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
|
||||||
{
|
|
||||||
public enum BackendThreading
|
|
||||||
{
|
|
||||||
Auto,
|
|
||||||
Off,
|
|
||||||
On
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
|
||||||
{
|
|
||||||
public enum GraphicsBackend
|
|
||||||
{
|
|
||||||
Vulkan,
|
|
||||||
OpenGl
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|
||||||
{
|
|
||||||
public enum MotionInputBackendType : byte
|
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
GamepadDriver,
|
|
||||||
CemuHook
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
|
||||||
{
|
|
||||||
public enum StickInputId : byte
|
|
||||||
{
|
|
||||||
Unbound,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
|
|
||||||
Count
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
|
||||||
{
|
|
||||||
public enum InputBackendType
|
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
WindowKeyboard,
|
|
||||||
GamepadSDL2,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
|
||||||
{
|
|
||||||
public enum MemoryManagerMode : byte
|
|
||||||
{
|
|
||||||
SoftwarePageTable,
|
|
||||||
HostMapped,
|
|
||||||
HostMappedUnsafe
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
|
||||||
{
|
|
||||||
internal class DefaultLogFormatter : ILogFormatter
|
|
||||||
{
|
|
||||||
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>();
|
|
||||||
|
|
||||||
public string Format(LogEventArgs args)
|
|
||||||
{
|
|
||||||
StringBuilder sb = _stringBuilderPool.Allocate();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sb.Clear();
|
|
||||||
|
|
||||||
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time);
|
|
||||||
sb.Append($" |{args.Level.ToString()[0]}| ");
|
|
||||||
|
|
||||||
if (args.ThreadName != null)
|
|
||||||
{
|
|
||||||
sb.Append(args.ThreadName);
|
|
||||||
sb.Append(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(args.Message);
|
|
||||||
|
|
||||||
if (args.Data != null)
|
|
||||||
{
|
|
||||||
PropertyInfo[] props = args.Data.GetType().GetProperties();
|
|
||||||
|
|
||||||
sb.Append(" {");
|
|
||||||
|
|
||||||
foreach (var prop in props)
|
|
||||||
{
|
|
||||||
sb.Append(prop.Name);
|
|
||||||
sb.Append(": ");
|
|
||||||
|
|
||||||
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
|
||||||
{
|
|
||||||
Array array = (Array)prop.GetValue(args.Data);
|
|
||||||
foreach (var item in array)
|
|
||||||
{
|
|
||||||
sb.Append(item.ToString());
|
|
||||||
sb.Append(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array.Length > 0)
|
|
||||||
{
|
|
||||||
sb.Remove(sb.Length - 2, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.Append(prop.GetValue(args.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(" ; ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We remove the final ';' from the string
|
|
||||||
if (props.Length > 0)
|
|
||||||
{
|
|
||||||
sb.Remove(sb.Length - 3, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append('}');
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_stringBuilderPool.Release(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
|
||||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
|
||||||
{
|
|
||||||
public class JsonHelper
|
|
||||||
{
|
|
||||||
public static JsonNamingPolicy SnakeCase { get; }
|
|
||||||
|
|
||||||
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
|
||||||
{
|
|
||||||
public override string ConvertName(string name)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
for (int i = 0; i < name.Length; i++)
|
|
||||||
{
|
|
||||||
char c = name[i];
|
|
||||||
|
|
||||||
if (char.IsUpper(c))
|
|
||||||
{
|
|
||||||
if (i == 0 || char.IsUpper(name[i - 1]))
|
|
||||||
{
|
|
||||||
builder.Append(char.ToLowerInvariant(c));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.Append("_");
|
|
||||||
builder.Append(char.ToLowerInvariant(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.Append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static JsonHelper()
|
|
||||||
{
|
|
||||||
SnakeCase = new SnakeCaseNamingPolicy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
JsonSerializerOptions options = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
DictionaryKeyPolicy = SnakeCase,
|
|
||||||
PropertyNamingPolicy = SnakeCase,
|
|
||||||
WriteIndented = prettyPrint,
|
|
||||||
AllowTrailingCommas = true,
|
|
||||||
ReadCommentHandling = JsonCommentHandling.Skip
|
|
||||||
};
|
|
||||||
|
|
||||||
options.Converters.Add(new JsonStringEnumConverter());
|
|
||||||
options.Converters.Add(new JsonInputConfigConverter());
|
|
||||||
options.Converters.Add(new JsonMotionConfigControllerConverter());
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(Stream stream)
|
|
||||||
{
|
|
||||||
using (BinaryReader reader = new BinaryReader(stream))
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T DeserializeFromFile<T>(string path)
|
|
||||||
{
|
|
||||||
return Deserialize<T>(File.ReadAllText(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(string json)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
|
||||||
{
|
|
||||||
writer.Write(SerializeToUtf8Bytes(obj, prettyPrint));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Serialize<TValue>(TValue obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
|
||||||
{
|
|
||||||
public static class StreamUtils
|
|
||||||
{
|
|
||||||
public static byte[] StreamToBytes(Stream input)
|
|
||||||
{
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
|
||||||
{
|
|
||||||
input.CopyTo(stream);
|
|
||||||
|
|
||||||
return stream.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
using Ryujinx.Memory.Tracking;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Tracking
|
|
||||||
{
|
|
||||||
public class CpuMultiRegionHandle : IMultiRegionHandle
|
|
||||||
{
|
|
||||||
private readonly MultiRegionHandle _impl;
|
|
||||||
|
|
||||||
public bool Dirty => _impl.Dirty;
|
|
||||||
|
|
||||||
internal CpuMultiRegionHandle(MultiRegionHandle impl)
|
|
||||||
{
|
|
||||||
_impl = impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => _impl.Dispose();
|
|
||||||
public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size);
|
|
||||||
public IEnumerable<IRegionHandle> GetHandles() => _impl.GetHandles();
|
|
||||||
public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction);
|
|
||||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction);
|
|
||||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber);
|
|
||||||
public void RegisterAction(ulong address, ulong size, RegionSignal action) => _impl.RegisterAction(address, size, action);
|
|
||||||
public void RegisterPreciseAction(ulong address, ulong size, PreciseRegionSignal action) => _impl.RegisterPreciseAction(address, size, action);
|
|
||||||
public void SignalWrite() => _impl.SignalWrite();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
using Ryujinx.Memory.Tracking;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Tracking
|
|
||||||
{
|
|
||||||
public class CpuRegionHandle : IRegionHandle
|
|
||||||
{
|
|
||||||
private readonly RegionHandle _impl;
|
|
||||||
|
|
||||||
public bool Dirty => _impl.Dirty;
|
|
||||||
public bool Unmapped => _impl.Unmapped;
|
|
||||||
public ulong Address => _impl.Address;
|
|
||||||
public ulong Size => _impl.Size;
|
|
||||||
public ulong EndAddress => _impl.EndAddress;
|
|
||||||
|
|
||||||
internal CpuRegionHandle(RegionHandle impl)
|
|
||||||
{
|
|
||||||
_impl = impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => _impl.Dispose();
|
|
||||||
public bool DirtyOrVolatile() => _impl.DirtyOrVolatile();
|
|
||||||
public void ForceDirty() => _impl.ForceDirty();
|
|
||||||
public IRegionHandle GetHandle() => _impl;
|
|
||||||
public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action);
|
|
||||||
public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action);
|
|
||||||
public void RegisterDirtyEvent(Action action) => _impl.RegisterDirtyEvent(action);
|
|
||||||
public void Reprotect(bool asDirty = false) => _impl.Reprotect(asDirty);
|
|
||||||
|
|
||||||
public bool OverlapsWith(ulong address, ulong size) => _impl.OverlapsWith(address, size);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
using Ryujinx.Memory.Tracking;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Tracking
|
|
||||||
{
|
|
||||||
public class CpuSmartMultiRegionHandle : IMultiRegionHandle
|
|
||||||
{
|
|
||||||
private readonly SmartMultiRegionHandle _impl;
|
|
||||||
|
|
||||||
public bool Dirty => _impl.Dirty;
|
|
||||||
|
|
||||||
internal CpuSmartMultiRegionHandle(SmartMultiRegionHandle impl)
|
|
||||||
{
|
|
||||||
_impl = impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => _impl.Dispose();
|
|
||||||
public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size);
|
|
||||||
public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action);
|
|
||||||
public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action);
|
|
||||||
public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction);
|
|
||||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction);
|
|
||||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber);
|
|
||||||
public void SignalWrite() => _impl.SignalWrite();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Model
|
|
||||||
{
|
|
||||||
unsafe struct PinnedSpan<T> where T : unmanaged
|
|
||||||
{
|
|
||||||
private void* _ptr;
|
|
||||||
private int _size;
|
|
||||||
|
|
||||||
public PinnedSpan(ReadOnlySpan<T> span)
|
|
||||||
{
|
|
||||||
_ptr = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
|
|
||||||
_size = span.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<T> Get()
|
|
||||||
{
|
|
||||||
return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
|
||||||
{
|
|
||||||
public readonly struct ShaderBindings
|
|
||||||
{
|
|
||||||
public IReadOnlyCollection<int> UniformBufferBindings { get; }
|
|
||||||
public IReadOnlyCollection<int> StorageBufferBindings { get; }
|
|
||||||
public IReadOnlyCollection<int> TextureBindings { get; }
|
|
||||||
public IReadOnlyCollection<int> ImageBindings { get; }
|
|
||||||
|
|
||||||
public ShaderBindings(
|
|
||||||
IReadOnlyCollection<int> uniformBufferBindings,
|
|
||||||
IReadOnlyCollection<int> storageBufferBindings,
|
|
||||||
IReadOnlyCollection<int> textureBindings,
|
|
||||||
IReadOnlyCollection<int> imageBindings)
|
|
||||||
{
|
|
||||||
UniformBufferBindings = uniformBufferBindings;
|
|
||||||
StorageBufferBindings = storageBufferBindings;
|
|
||||||
TextureBindings = textureBindings;
|
|
||||||
ImageBindings = imageBindings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.Gpu.Memory
|
|
||||||
{
|
|
||||||
public class UnmapEventArgs
|
|
||||||
{
|
|
||||||
public ulong Address { get; }
|
|
||||||
public ulong Size { get; }
|
|
||||||
|
|
||||||
public UnmapEventArgs(ulong address, ulong size)
|
|
||||||
{
|
|
||||||
Address = address;
|
|
||||||
Size = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="OpenTK.Graphics" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
@ -1,214 +0,0 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using Ryujinx.Graphics.OpenGL.Image;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL
|
|
||||||
{
|
|
||||||
class Window : IWindow, IDisposable
|
|
||||||
{
|
|
||||||
private const int TextureCount = 3;
|
|
||||||
private readonly OpenGLRenderer _renderer;
|
|
||||||
|
|
||||||
private bool _initialized;
|
|
||||||
|
|
||||||
private int _width;
|
|
||||||
private int _height;
|
|
||||||
private int _copyFramebufferHandle;
|
|
||||||
|
|
||||||
internal BackgroundContextWorker BackgroundContext { get; private set; }
|
|
||||||
|
|
||||||
internal bool ScreenCaptureRequested { get; set; }
|
|
||||||
|
|
||||||
public Window(OpenGLRenderer renderer)
|
|
||||||
{
|
|
||||||
_renderer = renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
|
|
||||||
{
|
|
||||||
GL.Disable(EnableCap.FramebufferSrgb);
|
|
||||||
|
|
||||||
(int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers();
|
|
||||||
|
|
||||||
CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback);
|
|
||||||
|
|
||||||
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
|
|
||||||
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
|
|
||||||
|
|
||||||
GL.Enable(EnableCap.FramebufferSrgb);
|
|
||||||
|
|
||||||
// Restore unpack alignment to 4, as performance overlays such as RTSS may change this to load their resources.
|
|
||||||
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
|
||||||
|
|
||||||
public void SetSize(int width, int height)
|
|
||||||
{
|
|
||||||
_width = width;
|
|
||||||
_height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback)
|
|
||||||
{
|
|
||||||
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer);
|
|
||||||
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer);
|
|
||||||
|
|
||||||
TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view;
|
|
||||||
|
|
||||||
GL.FramebufferTexture(
|
|
||||||
FramebufferTarget.ReadFramebuffer,
|
|
||||||
FramebufferAttachment.ColorAttachment0,
|
|
||||||
viewConverted.Handle,
|
|
||||||
0);
|
|
||||||
|
|
||||||
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
|
|
||||||
|
|
||||||
GL.Disable(EnableCap.RasterizerDiscard);
|
|
||||||
GL.Disable(IndexedEnableCap.ScissorTest, 0);
|
|
||||||
|
|
||||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
|
||||||
|
|
||||||
int srcX0, srcX1, srcY0, srcY1;
|
|
||||||
float scale = view.ScaleFactor;
|
|
||||||
|
|
||||||
if (crop.Left == 0 && crop.Right == 0)
|
|
||||||
{
|
|
||||||
srcX0 = 0;
|
|
||||||
srcX1 = (int)(view.Width / scale);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
srcX0 = crop.Left;
|
|
||||||
srcX1 = crop.Right;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crop.Top == 0 && crop.Bottom == 0)
|
|
||||||
{
|
|
||||||
srcY0 = 0;
|
|
||||||
srcY1 = (int)(view.Height / scale);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
srcY0 = crop.Top;
|
|
||||||
srcY1 = crop.Bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scale != 1f)
|
|
||||||
{
|
|
||||||
srcX0 = (int)(srcX0 * scale);
|
|
||||||
srcY0 = (int)(srcY0 * scale);
|
|
||||||
srcX1 = (int)Math.Ceiling(srcX1 * scale);
|
|
||||||
srcY1 = (int)Math.Ceiling(srcY1 * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY));
|
|
||||||
float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX));
|
|
||||||
|
|
||||||
int dstWidth = (int)(_width * ratioX);
|
|
||||||
int dstHeight = (int)(_height * ratioY);
|
|
||||||
|
|
||||||
int dstPaddingX = (_width - dstWidth) / 2;
|
|
||||||
int dstPaddingY = (_height - dstHeight) / 2;
|
|
||||||
|
|
||||||
int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX;
|
|
||||||
int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX;
|
|
||||||
|
|
||||||
int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
|
|
||||||
int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
|
|
||||||
|
|
||||||
if (ScreenCaptureRequested)
|
|
||||||
{
|
|
||||||
CaptureFrame(srcX0, srcY0, srcX1, srcY1, view.Format.IsBgr(), crop.FlipX, crop.FlipY);
|
|
||||||
|
|
||||||
ScreenCaptureRequested = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GL.BlitFramebuffer(
|
|
||||||
srcX0,
|
|
||||||
srcY0,
|
|
||||||
srcX1,
|
|
||||||
srcY1,
|
|
||||||
dstX0,
|
|
||||||
dstY0,
|
|
||||||
dstX1,
|
|
||||||
dstY1,
|
|
||||||
ClearBufferMask.ColorBufferBit,
|
|
||||||
BlitFramebufferFilter.Linear);
|
|
||||||
|
|
||||||
// Remove Alpha channel
|
|
||||||
GL.ColorMask(false, false, false, true);
|
|
||||||
GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
|
||||||
|
|
||||||
for (int i = 0; i < Constants.MaxRenderTargets; i++)
|
|
||||||
{
|
|
||||||
((Pipeline)_renderer.Pipeline).RestoreComponentMask(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture.
|
|
||||||
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne);
|
|
||||||
GL.Viewport(0, 0, _width, _height);
|
|
||||||
|
|
||||||
swapBuffersCallback();
|
|
||||||
|
|
||||||
((Pipeline)_renderer.Pipeline).RestoreClipControl();
|
|
||||||
((Pipeline)_renderer.Pipeline).RestoreScissor0Enable();
|
|
||||||
((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard();
|
|
||||||
((Pipeline)_renderer.Pipeline).RestoreViewport0();
|
|
||||||
|
|
||||||
if (viewConverted != view)
|
|
||||||
{
|
|
||||||
viewConverted.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetCopyFramebufferHandleLazy()
|
|
||||||
{
|
|
||||||
int handle = _copyFramebufferHandle;
|
|
||||||
|
|
||||||
if (handle == 0)
|
|
||||||
{
|
|
||||||
handle = GL.GenFramebuffer();
|
|
||||||
|
|
||||||
_copyFramebufferHandle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InitializeBackgroundContext(IOpenGLContext baseContext)
|
|
||||||
{
|
|
||||||
BackgroundContext = new BackgroundContextWorker(baseContext);
|
|
||||||
_initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CaptureFrame(int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
|
|
||||||
{
|
|
||||||
long size = Math.Abs(4 * width * height);
|
|
||||||
byte[] bitmap = new byte[size];
|
|
||||||
|
|
||||||
GL.ReadPixels(x, y, width, height, isBgra ? PixelFormat.Bgra : PixelFormat.Rgba, PixelType.UnsignedByte, bitmap);
|
|
||||||
|
|
||||||
_renderer.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!_initialized)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BackgroundContext.Dispose();
|
|
||||||
|
|
||||||
if (_copyFramebufferHandle != 0)
|
|
||||||
{
|
|
||||||
GL.DeleteFramebuffer(_copyFramebufferHandle);
|
|
||||||
|
|
||||||
_copyFramebufferHandle = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|
||||||
{
|
|
||||||
static class DefaultNames
|
|
||||||
{
|
|
||||||
public const string LocalNamePrefix = "temp";
|
|
||||||
|
|
||||||
public const string SamplerNamePrefix = "tex";
|
|
||||||
public const string ImageNamePrefix = "img";
|
|
||||||
|
|
||||||
public const string PerPatchAttributePrefix = "patch_attr_";
|
|
||||||
public const string IAttributePrefix = "in_attr";
|
|
||||||
public const string OAttributePrefix = "out_attr";
|
|
||||||
|
|
||||||
public const string StorageNamePrefix = "s";
|
|
||||||
|
|
||||||
public const string DataName = "data";
|
|
||||||
|
|
||||||
public const string SupportBlockName = "support_block";
|
|
||||||
public const string SupportBlockAlphaTestName = "s_alpha_test";
|
|
||||||
public const string SupportBlockIsBgraName = "s_is_bgra";
|
|
||||||
public const string SupportBlockViewportInverse = "s_viewport_inverse";
|
|
||||||
public const string SupportBlockFragmentScaleCount = "s_frag_scale_count";
|
|
||||||
public const string SupportBlockRenderScaleName = "s_render_scale";
|
|
||||||
|
|
||||||
public const string BlockSuffix = "block";
|
|
||||||
|
|
||||||
public const string UniformNamePrefix = "c";
|
|
||||||
public const string UniformNameSuffix = "data";
|
|
||||||
|
|
||||||
public const string LocalMemoryName = "local_mem";
|
|
||||||
public const string SharedMemoryName = "shared_mem";
|
|
||||||
|
|
||||||
public const string ArgumentNamePrefix = "a";
|
|
||||||
|
|
||||||
public const string UndefinedName = "undef";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
int Helper_AtomicMaxS32(int index, int offset, int value)
|
|
||||||
{
|
|
||||||
uint oldValue, newValue;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
oldValue = $STORAGE_MEM$[index].data[offset];
|
|
||||||
newValue = uint(max(int(oldValue), value));
|
|
||||||
} while (atomicCompSwap($STORAGE_MEM$[index].data[offset], oldValue, newValue) != oldValue);
|
|
||||||
return int(oldValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_AtomicMinS32(int index, int offset, int value)
|
|
||||||
{
|
|
||||||
uint oldValue, newValue;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
oldValue = $STORAGE_MEM$[index].data[offset];
|
|
||||||
newValue = uint(min(int(oldValue), value));
|
|
||||||
} while (atomicCompSwap($STORAGE_MEM$[index].data[offset], oldValue, newValue) != oldValue);
|
|
||||||
return int(oldValue);
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
void Helper_StoreStorage16(int index, int offset, uint value)
|
|
||||||
{
|
|
||||||
int wordOffset = offset >> 2;
|
|
||||||
int bitOffset = (offset & 3) * 8;
|
|
||||||
uint oldValue, newValue;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
oldValue = $STORAGE_MEM$[index].data[wordOffset];
|
|
||||||
newValue = bitfieldInsert(oldValue, value, bitOffset, 16);
|
|
||||||
} while (atomicCompSwap($STORAGE_MEM$[index].data[wordOffset], oldValue, newValue) != oldValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Helper_StoreStorage8(int index, int offset, uint value)
|
|
||||||
{
|
|
||||||
int wordOffset = offset >> 2;
|
|
||||||
int bitOffset = (offset & 3) * 8;
|
|
||||||
uint oldValue, newValue;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
oldValue = $STORAGE_MEM$[index].data[wordOffset];
|
|
||||||
newValue = bitfieldInsert(oldValue, value, bitOffset, 8);
|
|
||||||
} while (atomicCompSwap($STORAGE_MEM$[index].data[wordOffset], oldValue, newValue) != oldValue);
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = s_render_scale[samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = s_render_scale[samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = s_render_scale[1 + samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
if (scale < 0.0) // If less than 0, try interpolate between texels by using the screen position.
|
|
||||||
{
|
|
||||||
return ivec2(vec2(inputVec) * (-scale) + mod(gl_FragCoord.xy, 0.0 - scale));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(s_render_scale[1 + samplerIndex]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@ -1,495 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|
||||||
{
|
|
||||||
class OperandManager
|
|
||||||
{
|
|
||||||
private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
|
||||||
|
|
||||||
private readonly struct BuiltInAttribute
|
|
||||||
{
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public AggregateType Type { get; }
|
|
||||||
|
|
||||||
public BuiltInAttribute(string name, AggregateType type)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Type = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>()
|
|
||||||
{
|
|
||||||
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", AggregateType.Bool) },
|
|
||||||
|
|
||||||
// Special.
|
|
||||||
{ AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ThreadKill, new BuiltInAttribute("gl_HelperInvocation", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LaneId, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", AggregateType.S32) },
|
|
||||||
{ AttributeConsts.EqMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.GeMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.GtMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LeMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LtMask, new BuiltInAttribute(null, AggregateType.U32) },
|
|
||||||
|
|
||||||
// Support uniforms.
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[0]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[1]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[2]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[3]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", AggregateType.Bool) },
|
|
||||||
|
|
||||||
{ AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", AggregateType.FP32) }
|
|
||||||
};
|
|
||||||
|
|
||||||
private Dictionary<AstOperand, string> _locals;
|
|
||||||
|
|
||||||
public OperandManager()
|
|
||||||
{
|
|
||||||
_locals = new Dictionary<AstOperand, string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string DeclareLocal(AstOperand operand)
|
|
||||||
{
|
|
||||||
string name = $"{DefaultNames.LocalNamePrefix}_{_locals.Count}";
|
|
||||||
|
|
||||||
_locals.Add(operand, name);
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetExpression(CodeGenContext context, AstOperand operand)
|
|
||||||
{
|
|
||||||
return operand.Type switch
|
|
||||||
{
|
|
||||||
OperandType.Argument => GetArgumentName(operand.Value),
|
|
||||||
OperandType.Attribute => GetAttributeName(context, operand.Value, perPatch: false),
|
|
||||||
OperandType.AttributePerPatch => GetAttributeName(context, operand.Value, perPatch: true),
|
|
||||||
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
|
||||||
OperandType.ConstantBuffer => GetConstantBufferName(operand, context.Config),
|
|
||||||
OperandType.LocalVariable => _locals[operand],
|
|
||||||
OperandType.Undefined => DefaultNames.UndefinedName,
|
|
||||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetConstantBufferName(AstOperand operand, ShaderConfig config)
|
|
||||||
{
|
|
||||||
return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable)
|
|
||||||
{
|
|
||||||
return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetVec4Indexed(string vectorName, string indexExpr, bool indexElement)
|
|
||||||
{
|
|
||||||
if (indexElement)
|
|
||||||
{
|
|
||||||
return $"{vectorName}[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
string result = $"{vectorName}.x";
|
|
||||||
for (int i = 1; i < 4; i++)
|
|
||||||
{
|
|
||||||
result = $"(({indexExpr}) == {i}) ? ({vectorName}.{GetSwizzleMask(i)}) : ({result})";
|
|
||||||
}
|
|
||||||
return $"({result})";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetConstantBufferName(int slot, string offsetExpr, ShaderStage stage, bool cbIndexable, bool indexElement)
|
|
||||||
{
|
|
||||||
return GetVec4Indexed(GetUbName(stage, slot, cbIndexable) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetConstantBufferName(string slotExpr, string offsetExpr, ShaderStage stage, bool indexElement)
|
|
||||||
{
|
|
||||||
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetOutAttributeName(CodeGenContext context, int value, bool perPatch)
|
|
||||||
{
|
|
||||||
return GetAttributeName(context, value, perPatch, isOutAttr: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetAttributeName(CodeGenContext context, int value, bool perPatch, bool isOutAttr = false, string indexExpr = "0")
|
|
||||||
{
|
|
||||||
ShaderConfig config = context.Config;
|
|
||||||
|
|
||||||
if ((value & AttributeConsts.LoadOutputMask) != 0)
|
|
||||||
{
|
|
||||||
isOutAttr = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value &= AttributeConsts.Mask & ~3;
|
|
||||||
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
|
||||||
|
|
||||||
if (perPatch)
|
|
||||||
{
|
|
||||||
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
|
|
||||||
{
|
|
||||||
value -= AttributeConsts.UserAttributePerPatchBase;
|
|
||||||
|
|
||||||
return $"{DefaultNames.PerPatchAttributePrefix}{(value >> 4)}.{swzMask}";
|
|
||||||
}
|
|
||||||
else if (value < AttributeConsts.UserAttributePerPatchBase)
|
|
||||||
{
|
|
||||||
return value switch
|
|
||||||
{
|
|
||||||
AttributeConsts.TessLevelOuter0 => "gl_TessLevelOuter[0]",
|
|
||||||
AttributeConsts.TessLevelOuter1 => "gl_TessLevelOuter[1]",
|
|
||||||
AttributeConsts.TessLevelOuter2 => "gl_TessLevelOuter[2]",
|
|
||||||
AttributeConsts.TessLevelOuter3 => "gl_TessLevelOuter[3]",
|
|
||||||
AttributeConsts.TessLevelInner0 => "gl_TessLevelInner[0]",
|
|
||||||
AttributeConsts.TessLevelInner1 => "gl_TessLevelInner[1]",
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
|
||||||
{
|
|
||||||
int attrOffset = value;
|
|
||||||
value -= AttributeConsts.UserAttributeBase;
|
|
||||||
|
|
||||||
string prefix = isOutAttr
|
|
||||||
? DefaultNames.OAttributePrefix
|
|
||||||
: DefaultNames.IAttributePrefix;
|
|
||||||
|
|
||||||
bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing);
|
|
||||||
|
|
||||||
if (indexable)
|
|
||||||
{
|
|
||||||
string name = prefix;
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Geometry && !isOutAttr)
|
|
||||||
{
|
|
||||||
name += $"[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name + $"[{(value >> 4)}]." + swzMask;
|
|
||||||
}
|
|
||||||
else if (config.TransformFeedbackEnabled &&
|
|
||||||
((config.LastInVertexPipeline && isOutAttr) ||
|
|
||||||
(config.Stage == ShaderStage.Fragment && !isOutAttr)))
|
|
||||||
{
|
|
||||||
int components = config.LastInPipeline ? context.Info.GetTransformFeedbackOutputComponents(attrOffset) : 1;
|
|
||||||
string name = components > 1 ? $"{prefix}{(value >> 4)}" : $"{prefix}{(value >> 4)}_{swzMask}";
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
|
||||||
{
|
|
||||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return components > 1 ? name + '.' + swzMask : name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string name = $"{prefix}{(value >> 4)}";
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr))
|
|
||||||
{
|
|
||||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name + '.' + swzMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
|
|
||||||
{
|
|
||||||
value -= AttributeConsts.FragmentOutputColorBase;
|
|
||||||
|
|
||||||
return $"{DefaultNames.OAttributePrefix}{(value >> 4)}.{swzMask}";
|
|
||||||
}
|
|
||||||
else if (_builtInAttributes.TryGetValue(value, out BuiltInAttribute builtInAttr))
|
|
||||||
{
|
|
||||||
string subgroupMask = value switch
|
|
||||||
{
|
|
||||||
AttributeConsts.EqMask => "Eq",
|
|
||||||
AttributeConsts.GeMask => "Ge",
|
|
||||||
AttributeConsts.GtMask => "Gt",
|
|
||||||
AttributeConsts.LeMask => "Le",
|
|
||||||
AttributeConsts.LtMask => "Lt",
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
|
|
||||||
if (subgroupMask != null)
|
|
||||||
{
|
|
||||||
return config.GpuAccessor.QueryHostSupportsShaderBallot()
|
|
||||||
? $"unpackUint2x32(gl_SubGroup{subgroupMask}MaskARB).x"
|
|
||||||
: $"gl_Subgroup{subgroupMask}Mask.x";
|
|
||||||
}
|
|
||||||
else if (value == AttributeConsts.LaneId)
|
|
||||||
{
|
|
||||||
return config.GpuAccessor.QueryHostSupportsShaderBallot()
|
|
||||||
? "gl_SubGroupInvocationARB"
|
|
||||||
: "gl_SubgroupInvocationID";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
// TODO: There must be a better way to handle this...
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case AttributeConsts.PositionX: return $"(gl_FragCoord.x / {DefaultNames.SupportBlockRenderScaleName}[0])";
|
|
||||||
case AttributeConsts.PositionY: return $"(gl_FragCoord.y / {DefaultNames.SupportBlockRenderScaleName}[0])";
|
|
||||||
case AttributeConsts.PositionZ: return "gl_FragCoord.z";
|
|
||||||
case AttributeConsts.PositionW: return "gl_FragCoord.w";
|
|
||||||
|
|
||||||
case AttributeConsts.FrontFacing:
|
|
||||||
if (config.GpuAccessor.QueryHostHasFrontFacingBug())
|
|
||||||
{
|
|
||||||
// This is required for Intel on Windows, gl_FrontFacing sometimes returns incorrect
|
|
||||||
// (flipped) values. Doing this seems to fix it.
|
|
||||||
return "(-floatBitsToInt(float(gl_FrontFacing)) < 0)";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string name = builtInAttr.Name;
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeGlsl(config.Stage, isOutAttr) && AttributeInfo.IsArrayBuiltIn(value))
|
|
||||||
{
|
|
||||||
name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Warn about unknown built-in attribute.
|
|
||||||
|
|
||||||
return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetAttributeName(string attrExpr, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0")
|
|
||||||
{
|
|
||||||
string name = isOutAttr
|
|
||||||
? DefaultNames.OAttributePrefix
|
|
||||||
: DefaultNames.IAttributePrefix;
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Geometry && !isOutAttr)
|
|
||||||
{
|
|
||||||
name += $"[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{name}[{attrExpr} >> 2][{attrExpr} & 3]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
|
|
||||||
{
|
|
||||||
if (cbIndexable)
|
|
||||||
{
|
|
||||||
return GetUbName(stage, NumberFormatter.FormatInt(slot, AggregateType.S32));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}{slot}_{DefaultNames.UniformNameSuffix}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetUbName(ShaderStage stage, string slotExpr)
|
|
||||||
{
|
|
||||||
return $"{GetShaderStagePrefix(stage)}_{DefaultNames.UniformNamePrefix}[{slotExpr}].{DefaultNames.DataName}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
|
|
||||||
{
|
|
||||||
return GetSamplerName(stage, texOp.CbufSlot, texOp.Handle, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetSamplerName(ShaderStage stage, int cbufSlot, int handle, bool indexed, string indexExpr)
|
|
||||||
{
|
|
||||||
string suffix = cbufSlot < 0 ? $"_tcb_{handle:X}" : $"_cb{cbufSlot}_{handle:X}";
|
|
||||||
|
|
||||||
if (indexed)
|
|
||||||
{
|
|
||||||
suffix += $"a[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetShaderStagePrefix(stage) + "_" + DefaultNames.SamplerNamePrefix + suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetImageName(ShaderStage stage, AstTextureOperation texOp, string indexExpr)
|
|
||||||
{
|
|
||||||
return GetImageName(stage, texOp.CbufSlot, texOp.Handle, texOp.Format, texOp.Type.HasFlag(SamplerType.Indexed), indexExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetImageName(
|
|
||||||
ShaderStage stage,
|
|
||||||
int cbufSlot,
|
|
||||||
int handle,
|
|
||||||
TextureFormat format,
|
|
||||||
bool indexed,
|
|
||||||
string indexExpr)
|
|
||||||
{
|
|
||||||
string suffix = cbufSlot < 0
|
|
||||||
? $"_tcb_{handle:X}_{format.ToGlslFormat()}"
|
|
||||||
: $"_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
|
|
||||||
|
|
||||||
if (indexed)
|
|
||||||
{
|
|
||||||
suffix += $"a[{indexExpr}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetShaderStagePrefix(stage) + "_" + DefaultNames.ImageNamePrefix + suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetShaderStagePrefix(ShaderStage stage)
|
|
||||||
{
|
|
||||||
int index = (int)stage;
|
|
||||||
|
|
||||||
if ((uint)index >= StagePrefixes.Length)
|
|
||||||
{
|
|
||||||
return "invalid";
|
|
||||||
}
|
|
||||||
|
|
||||||
return StagePrefixes[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static char GetSwizzleMask(int value)
|
|
||||||
{
|
|
||||||
return "xyzw"[value];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetArgumentName(int argIndex)
|
|
||||||
{
|
|
||||||
return $"{DefaultNames.ArgumentNamePrefix}{argIndex}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false)
|
|
||||||
{
|
|
||||||
if (node is AstOperation operation)
|
|
||||||
{
|
|
||||||
if (operation.Inst == Instruction.LoadAttribute)
|
|
||||||
{
|
|
||||||
// Load attribute basically just returns the attribute value.
|
|
||||||
// Some built-in attributes may have different types, so we need
|
|
||||||
// to return the type based on the attribute that is being read.
|
|
||||||
if (operation.GetSource(0) is AstOperand operand && operand.Type == OperandType.Constant)
|
|
||||||
{
|
|
||||||
if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr))
|
|
||||||
{
|
|
||||||
return builtInAttr.Type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return OperandInfo.GetVarType(OperandType.Attribute);
|
|
||||||
}
|
|
||||||
else if (operation.Inst == Instruction.Call)
|
|
||||||
{
|
|
||||||
AstOperand funcId = (AstOperand)operation.GetSource(0);
|
|
||||||
|
|
||||||
Debug.Assert(funcId.Type == OperandType.Constant);
|
|
||||||
|
|
||||||
return context.GetFunction(funcId.Value).ReturnType;
|
|
||||||
}
|
|
||||||
else if (operation.Inst == Instruction.VectorExtract)
|
|
||||||
{
|
|
||||||
return GetNodeDestType(context, operation.GetSource(0)) & ~AggregateType.ElementCountMask;
|
|
||||||
}
|
|
||||||
else if (operation is AstTextureOperation texOp)
|
|
||||||
{
|
|
||||||
if (texOp.Inst == Instruction.ImageLoad ||
|
|
||||||
texOp.Inst == Instruction.ImageStore ||
|
|
||||||
texOp.Inst == Instruction.ImageAtomic)
|
|
||||||
{
|
|
||||||
return texOp.GetVectorType(texOp.Format.GetComponentType());
|
|
||||||
}
|
|
||||||
else if (texOp.Inst == Instruction.TextureSample)
|
|
||||||
{
|
|
||||||
return texOp.GetVectorType(GetDestVarType(operation.Inst));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetDestVarType(operation.Inst);
|
|
||||||
}
|
|
||||||
else if (node is AstOperand operand)
|
|
||||||
{
|
|
||||||
if (operand.Type == OperandType.Argument)
|
|
||||||
{
|
|
||||||
int argIndex = operand.Value;
|
|
||||||
|
|
||||||
return context.CurrentFunction.GetArgumentType(argIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetOperandVarType(context, operand, isAsgDest);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AggregateType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false)
|
|
||||||
{
|
|
||||||
if (operand.Type == OperandType.Attribute)
|
|
||||||
{
|
|
||||||
if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr))
|
|
||||||
{
|
|
||||||
return builtInAttr.Type;
|
|
||||||
}
|
|
||||||
else if (context.Config.Stage == ShaderStage.Vertex && !isAsgDest &&
|
|
||||||
operand.Value >= AttributeConsts.UserAttributeBase &&
|
|
||||||
operand.Value < AttributeConsts.UserAttributeEnd)
|
|
||||||
{
|
|
||||||
int location = (operand.Value - AttributeConsts.UserAttributeBase) / 16;
|
|
||||||
|
|
||||||
AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location);
|
|
||||||
|
|
||||||
return type.ToAggregateType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return OperandInfo.GetVarType(operand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,782 +0,0 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
|
||||||
using Spv.Generator;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using static Spv.Specification;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|
||||||
{
|
|
||||||
static class Declarations
|
|
||||||
{
|
|
||||||
// At least 16 attributes are guaranteed by the spec.
|
|
||||||
public const int MaxAttributes = 16;
|
|
||||||
|
|
||||||
private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
|
||||||
|
|
||||||
public static void DeclareParameters(CodeGenContext context, StructuredFunction function)
|
|
||||||
{
|
|
||||||
DeclareParameters(context, function.InArguments, 0);
|
|
||||||
DeclareParameters(context, function.OutArguments, function.InArguments.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareParameters(CodeGenContext context, IEnumerable<AggregateType> argTypes, int argIndex)
|
|
||||||
{
|
|
||||||
foreach (var argType in argTypes)
|
|
||||||
{
|
|
||||||
var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType));
|
|
||||||
var spvArg = context.FunctionParameter(argPointerType);
|
|
||||||
|
|
||||||
context.DeclareArgument(argIndex++, spvArg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeclareLocals(CodeGenContext context, StructuredFunction function)
|
|
||||||
{
|
|
||||||
foreach (AstOperand local in function.Locals)
|
|
||||||
{
|
|
||||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType));
|
|
||||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
|
||||||
|
|
||||||
context.AddLocalVariable(spvLocal);
|
|
||||||
context.DeclareLocal(local, spvLocal);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
|
|
||||||
var coordTempPointerType = context.TypePointer(StorageClass.Function, ivector2Type);
|
|
||||||
var coordTemp = context.Variable(coordTempPointerType, StorageClass.Function);
|
|
||||||
|
|
||||||
context.AddLocalVariable(coordTemp);
|
|
||||||
context.CoordTemp = coordTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
|
|
||||||
{
|
|
||||||
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
|
||||||
{
|
|
||||||
StructuredFunction function = functions[funcIndex];
|
|
||||||
Instruction[] locals = new Instruction[function.InArguments.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < function.InArguments.Length; i++)
|
|
||||||
{
|
|
||||||
var type = function.GetArgumentType(i);
|
|
||||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
|
|
||||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
|
||||||
|
|
||||||
context.AddLocalVariable(spvLocal);
|
|
||||||
|
|
||||||
locals[i] = spvLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.DeclareLocalForArgs(funcIndex, locals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage == ShaderStage.Compute)
|
|
||||||
{
|
|
||||||
int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
|
|
||||||
|
|
||||||
if (localMemorySize != 0)
|
|
||||||
{
|
|
||||||
DeclareLocalMemory(context, localMemorySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4);
|
|
||||||
|
|
||||||
if (sharedMemorySize != 0)
|
|
||||||
{
|
|
||||||
DeclareSharedMemory(context, sharedMemorySize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (context.Config.LocalMemorySize != 0)
|
|
||||||
{
|
|
||||||
int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4);
|
|
||||||
DeclareLocalMemory(context, localMemorySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeclareSupportBuffer(context);
|
|
||||||
DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
|
|
||||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
|
||||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
|
||||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
|
||||||
DeclareInputAttributes(context, info, perPatch: false);
|
|
||||||
DeclareOutputAttributes(context, info, perPatch: false);
|
|
||||||
DeclareInputAttributes(context, info, perPatch: true);
|
|
||||||
DeclareOutputAttributes(context, info, perPatch: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareLocalMemory(CodeGenContext context, int size)
|
|
||||||
{
|
|
||||||
context.LocalMemory = DeclareMemory(context, StorageClass.Private, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareSharedMemory(CodeGenContext context, int size)
|
|
||||||
{
|
|
||||||
context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Instruction DeclareMemory(CodeGenContext context, StorageClass storage, int size)
|
|
||||||
{
|
|
||||||
var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size));
|
|
||||||
var pointerType = context.TypePointer(storage, arrayType);
|
|
||||||
var variable = context.Variable(pointerType, storage);
|
|
||||||
|
|
||||||
context.AddGlobalVariable(variable);
|
|
||||||
|
|
||||||
return variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareSupportBuffer(CodeGenContext context)
|
|
||||||
{
|
|
||||||
if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
|
|
||||||
var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
|
|
||||||
var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
|
|
||||||
|
|
||||||
context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
|
|
||||||
context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
|
|
||||||
|
|
||||||
var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, viewportInverseVectorType, context.TypeS32(), renderScaleArrayType);
|
|
||||||
|
|
||||||
context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset);
|
|
||||||
context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
|
|
||||||
context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset);
|
|
||||||
context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
|
|
||||||
context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
|
|
||||||
context.Decorate(supportBufferStructType, Decoration.Block);
|
|
||||||
|
|
||||||
var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
|
|
||||||
var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform);
|
|
||||||
|
|
||||||
context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
|
||||||
context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0);
|
|
||||||
|
|
||||||
context.AddGlobalVariable(supportBufferVariable);
|
|
||||||
|
|
||||||
context.SupportBuffer = supportBufferVariable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
|
|
||||||
{
|
|
||||||
if (descriptors.Length == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint ubSize = Constants.ConstantBufferSize / 16;
|
|
||||||
|
|
||||||
var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true);
|
|
||||||
context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16);
|
|
||||||
var ubStructType = context.TypeStruct(true, ubArrayType);
|
|
||||||
context.Decorate(ubStructType, Decoration.Block);
|
|
||||||
context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0);
|
|
||||||
|
|
||||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
|
|
||||||
{
|
|
||||||
int count = descriptors.Max(x => x.Slot) + 1;
|
|
||||||
|
|
||||||
var ubStructArrayType = context.TypeArray(ubStructType, context.Constant(context.TypeU32(), count));
|
|
||||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructArrayType);
|
|
||||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
|
||||||
|
|
||||||
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u");
|
|
||||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
|
||||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding);
|
|
||||||
context.AddGlobalVariable(ubVariable);
|
|
||||||
|
|
||||||
context.UniformBuffersArray = ubVariable;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
|
|
||||||
|
|
||||||
foreach (var descriptor in descriptors)
|
|
||||||
{
|
|
||||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
|
||||||
|
|
||||||
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}");
|
|
||||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
|
||||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
|
||||||
context.AddGlobalVariable(ubVariable);
|
|
||||||
context.UniformBuffers.Add(descriptor.Slot, ubVariable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
|
|
||||||
{
|
|
||||||
if (descriptors.Length == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 1 : 0;
|
|
||||||
int count = descriptors.Max(x => x.Slot) + 1;
|
|
||||||
|
|
||||||
var sbArrayType = context.TypeRuntimeArray(context.TypeU32());
|
|
||||||
context.Decorate(sbArrayType, Decoration.ArrayStride, (LiteralInteger)4);
|
|
||||||
var sbStructType = context.TypeStruct(true, sbArrayType);
|
|
||||||
context.Decorate(sbStructType, Decoration.BufferBlock);
|
|
||||||
context.MemberDecorate(sbStructType, 0, Decoration.Offset, (LiteralInteger)0);
|
|
||||||
var sbStructArrayType = context.TypeArray(sbStructType, context.Constant(context.TypeU32(), count));
|
|
||||||
var sbPointerType = context.TypePointer(StorageClass.Uniform, sbStructArrayType);
|
|
||||||
var sbVariable = context.Variable(sbPointerType, StorageClass.Uniform);
|
|
||||||
|
|
||||||
context.Name(sbVariable, $"{GetStagePrefix(context.Config.Stage)}_s");
|
|
||||||
context.Decorate(sbVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
|
||||||
context.Decorate(sbVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstStorageBufferBinding);
|
|
||||||
context.AddGlobalVariable(sbVariable);
|
|
||||||
|
|
||||||
context.StorageBuffersArray = sbVariable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
|
||||||
{
|
|
||||||
foreach (var descriptor in descriptors)
|
|
||||||
{
|
|
||||||
var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format);
|
|
||||||
|
|
||||||
if (context.Samplers.ContainsKey(meta))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 2 : 0;
|
|
||||||
|
|
||||||
var dim = (descriptor.Type & SamplerType.Mask) switch
|
|
||||||
{
|
|
||||||
SamplerType.Texture1D => Dim.Dim1D,
|
|
||||||
SamplerType.Texture2D => Dim.Dim2D,
|
|
||||||
SamplerType.Texture3D => Dim.Dim3D,
|
|
||||||
SamplerType.TextureCube => Dim.Cube,
|
|
||||||
SamplerType.TextureBuffer => Dim.Buffer,
|
|
||||||
_ => throw new InvalidOperationException($"Invalid sampler type \"{descriptor.Type & SamplerType.Mask}\".")
|
|
||||||
};
|
|
||||||
|
|
||||||
var imageType = context.TypeImage(
|
|
||||||
context.TypeFP32(),
|
|
||||||
dim,
|
|
||||||
descriptor.Type.HasFlag(SamplerType.Shadow),
|
|
||||||
descriptor.Type.HasFlag(SamplerType.Array),
|
|
||||||
descriptor.Type.HasFlag(SamplerType.Multisample),
|
|
||||||
1,
|
|
||||||
ImageFormat.Unknown);
|
|
||||||
|
|
||||||
var nameSuffix = meta.CbufSlot < 0 ? $"_tcb_{meta.Handle:X}" : $"_cb{meta.CbufSlot}_{meta.Handle:X}";
|
|
||||||
|
|
||||||
var sampledImageType = context.TypeSampledImage(imageType);
|
|
||||||
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
|
|
||||||
var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant);
|
|
||||||
|
|
||||||
context.Samplers.Add(meta, (imageType, sampledImageType, sampledImageVariable));
|
|
||||||
context.SamplersTypes.Add(meta, descriptor.Type);
|
|
||||||
|
|
||||||
context.Name(sampledImageVariable, $"{GetStagePrefix(context.Config.Stage)}_tex{nameSuffix}");
|
|
||||||
context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
|
||||||
context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
|
||||||
context.AddGlobalVariable(sampledImageVariable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors)
|
|
||||||
{
|
|
||||||
foreach (var descriptor in descriptors)
|
|
||||||
{
|
|
||||||
var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format);
|
|
||||||
|
|
||||||
if (context.Images.ContainsKey(meta))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 3 : 0;
|
|
||||||
|
|
||||||
var dim = GetDim(descriptor.Type);
|
|
||||||
|
|
||||||
var imageType = context.TypeImage(
|
|
||||||
context.GetType(meta.Format.GetComponentType()),
|
|
||||||
dim,
|
|
||||||
descriptor.Type.HasFlag(SamplerType.Shadow),
|
|
||||||
descriptor.Type.HasFlag(SamplerType.Array),
|
|
||||||
descriptor.Type.HasFlag(SamplerType.Multisample),
|
|
||||||
AccessQualifier.ReadWrite,
|
|
||||||
GetImageFormat(meta.Format));
|
|
||||||
|
|
||||||
var nameSuffix = meta.CbufSlot < 0 ?
|
|
||||||
$"_tcb_{meta.Handle:X}_{meta.Format.ToGlslFormat()}" :
|
|
||||||
$"_cb{meta.CbufSlot}_{meta.Handle:X}_{meta.Format.ToGlslFormat()}";
|
|
||||||
|
|
||||||
var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType);
|
|
||||||
var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant);
|
|
||||||
|
|
||||||
context.Images.Add(meta, (imageType, imageVariable));
|
|
||||||
|
|
||||||
context.Name(imageVariable, $"{GetStagePrefix(context.Config.Stage)}_img{nameSuffix}");
|
|
||||||
context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
|
||||||
context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
|
||||||
|
|
||||||
if (descriptor.Flags.HasFlag(TextureUsageFlags.ImageCoherent))
|
|
||||||
{
|
|
||||||
context.Decorate(imageVariable, Decoration.Coherent);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.AddGlobalVariable(imageVariable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dim GetDim(SamplerType type)
|
|
||||||
{
|
|
||||||
return (type & SamplerType.Mask) switch
|
|
||||||
{
|
|
||||||
SamplerType.Texture1D => Dim.Dim1D,
|
|
||||||
SamplerType.Texture2D => Dim.Dim2D,
|
|
||||||
SamplerType.Texture3D => Dim.Dim3D,
|
|
||||||
SamplerType.TextureCube => Dim.Cube,
|
|
||||||
SamplerType.TextureBuffer => Dim.Buffer,
|
|
||||||
_ => throw new ArgumentException($"Invalid sampler type \"{type & SamplerType.Mask}\".")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ImageFormat GetImageFormat(TextureFormat format)
|
|
||||||
{
|
|
||||||
return format switch
|
|
||||||
{
|
|
||||||
TextureFormat.Unknown => ImageFormat.Unknown,
|
|
||||||
TextureFormat.R8Unorm => ImageFormat.R8,
|
|
||||||
TextureFormat.R8Snorm => ImageFormat.R8Snorm,
|
|
||||||
TextureFormat.R8Uint => ImageFormat.R8ui,
|
|
||||||
TextureFormat.R8Sint => ImageFormat.R8i,
|
|
||||||
TextureFormat.R16Float => ImageFormat.R16f,
|
|
||||||
TextureFormat.R16Unorm => ImageFormat.R16,
|
|
||||||
TextureFormat.R16Snorm => ImageFormat.R16Snorm,
|
|
||||||
TextureFormat.R16Uint => ImageFormat.R16ui,
|
|
||||||
TextureFormat.R16Sint => ImageFormat.R16i,
|
|
||||||
TextureFormat.R32Float => ImageFormat.R32f,
|
|
||||||
TextureFormat.R32Uint => ImageFormat.R32ui,
|
|
||||||
TextureFormat.R32Sint => ImageFormat.R32i,
|
|
||||||
TextureFormat.R8G8Unorm => ImageFormat.Rg8,
|
|
||||||
TextureFormat.R8G8Snorm => ImageFormat.Rg8Snorm,
|
|
||||||
TextureFormat.R8G8Uint => ImageFormat.Rg8ui,
|
|
||||||
TextureFormat.R8G8Sint => ImageFormat.Rg8i,
|
|
||||||
TextureFormat.R16G16Float => ImageFormat.Rg16f,
|
|
||||||
TextureFormat.R16G16Unorm => ImageFormat.Rg16,
|
|
||||||
TextureFormat.R16G16Snorm => ImageFormat.Rg16Snorm,
|
|
||||||
TextureFormat.R16G16Uint => ImageFormat.Rg16ui,
|
|
||||||
TextureFormat.R16G16Sint => ImageFormat.Rg16i,
|
|
||||||
TextureFormat.R32G32Float => ImageFormat.Rg32f,
|
|
||||||
TextureFormat.R32G32Uint => ImageFormat.Rg32ui,
|
|
||||||
TextureFormat.R32G32Sint => ImageFormat.Rg32i,
|
|
||||||
TextureFormat.R8G8B8A8Unorm => ImageFormat.Rgba8,
|
|
||||||
TextureFormat.R8G8B8A8Snorm => ImageFormat.Rgba8Snorm,
|
|
||||||
TextureFormat.R8G8B8A8Uint => ImageFormat.Rgba8ui,
|
|
||||||
TextureFormat.R8G8B8A8Sint => ImageFormat.Rgba8i,
|
|
||||||
TextureFormat.R16G16B16A16Float => ImageFormat.Rgba16f,
|
|
||||||
TextureFormat.R16G16B16A16Unorm => ImageFormat.Rgba16,
|
|
||||||
TextureFormat.R16G16B16A16Snorm => ImageFormat.Rgba16Snorm,
|
|
||||||
TextureFormat.R16G16B16A16Uint => ImageFormat.Rgba16ui,
|
|
||||||
TextureFormat.R16G16B16A16Sint => ImageFormat.Rgba16i,
|
|
||||||
TextureFormat.R32G32B32A32Float => ImageFormat.Rgba32f,
|
|
||||||
TextureFormat.R32G32B32A32Uint => ImageFormat.Rgba32ui,
|
|
||||||
TextureFormat.R32G32B32A32Sint => ImageFormat.Rgba32i,
|
|
||||||
TextureFormat.R10G10B10A2Unorm => ImageFormat.Rgb10A2,
|
|
||||||
TextureFormat.R10G10B10A2Uint => ImageFormat.Rgb10a2ui,
|
|
||||||
TextureFormat.R11G11B10Float => ImageFormat.R11fG11fB10f,
|
|
||||||
_ => throw new ArgumentException($"Invalid texture format \"{format}\".")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
|
|
||||||
{
|
|
||||||
bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
|
|
||||||
var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
|
|
||||||
|
|
||||||
foreach (int attr in inputs)
|
|
||||||
{
|
|
||||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
|
||||||
|
|
||||||
if (iaIndexing && isUserAttr && !perPatch)
|
|
||||||
{
|
|
||||||
if (context.InputsArray == null)
|
|
||||||
{
|
|
||||||
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Geometry)
|
|
||||||
{
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices));
|
|
||||||
}
|
|
||||||
|
|
||||||
var spvType = context.TypePointer(StorageClass.Input, attrType);
|
|
||||||
var spvVar = context.Variable(spvType, StorageClass.Input);
|
|
||||||
|
|
||||||
if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
|
|
||||||
|
|
||||||
context.AddGlobalVariable(spvVar);
|
|
||||||
context.InputsArray = spvVar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PixelImap iq = PixelImap.Unused;
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
|
|
||||||
{
|
|
||||||
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false);
|
|
||||||
AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask;
|
|
||||||
|
|
||||||
if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32))
|
|
||||||
{
|
|
||||||
iq = PixelImap.Constant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
|
|
||||||
{
|
|
||||||
bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
|
|
||||||
var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
|
|
||||||
|
|
||||||
foreach (int attr in outputs)
|
|
||||||
{
|
|
||||||
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
|
||||||
|
|
||||||
if (oaIndexing && isUserAttr && !perPatch)
|
|
||||||
{
|
|
||||||
if (context.OutputsArray == null)
|
|
||||||
{
|
|
||||||
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.TessellationControl)
|
|
||||||
{
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
|
||||||
}
|
|
||||||
|
|
||||||
var spvType = context.TypePointer(StorageClass.Output, attrType);
|
|
||||||
var spvVar = context.Variable(spvType, StorageClass.Output);
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
|
|
||||||
|
|
||||||
context.AddGlobalVariable(spvVar);
|
|
||||||
context.OutputsArray = spvVar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DeclareOutputAttribute(context, attr, perPatch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
DeclareOutputAttribute(context, AttributeConsts.PositionX, perPatch: false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareOutputAttribute(CodeGenContext context, int attr, bool perPatch)
|
|
||||||
{
|
|
||||||
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeclareInvocationId(CodeGenContext context)
|
|
||||||
{
|
|
||||||
DeclareInputOrOutput(context, AttributeConsts.LaneId, perPatch: false, isOutAttr: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool perPatch, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
|
||||||
{
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
|
||||||
if (isUserAttr && context.Config.TransformFeedbackEnabled && !perPatch &&
|
|
||||||
((isOutAttr && context.Config.LastInVertexPipeline) ||
|
|
||||||
(!isOutAttr && context.Config.Stage == ShaderStage.Fragment)))
|
|
||||||
{
|
|
||||||
DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dict = perPatch
|
|
||||||
? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
|
|
||||||
: (isOutAttr ? context.Outputs : context.Inputs);
|
|
||||||
|
|
||||||
var attrInfo = perPatch
|
|
||||||
? AttributeInfo.FromPatch(context.Config, attr, isOutAttr)
|
|
||||||
: AttributeInfo.From(context.Config, attr, isOutAttr);
|
|
||||||
|
|
||||||
if (dict.ContainsKey(attrInfo.BaseValue))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
|
||||||
var attrType = context.GetType(attrInfo.Type, attrInfo.Length);
|
|
||||||
bool builtInPassthrough = false;
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && !perPatch && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
|
|
||||||
{
|
|
||||||
int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
|
||||||
|
|
||||||
if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
|
||||||
{
|
|
||||||
builtInPassthrough = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch)
|
|
||||||
{
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
|
||||||
}
|
|
||||||
|
|
||||||
var spvType = context.TypePointer(storageClass, attrType);
|
|
||||||
var spvVar = context.Variable(spvType, storageClass);
|
|
||||||
|
|
||||||
if (builtInPassthrough)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attrInfo.IsBuiltin)
|
|
||||||
{
|
|
||||||
if (perPatch)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.Patch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.Invariant);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
|
|
||||||
|
|
||||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
|
|
||||||
{
|
|
||||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue);
|
|
||||||
if (tfOutput.Valid)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
|
|
||||||
context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride);
|
|
||||||
context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (perPatch)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.Patch);
|
|
||||||
|
|
||||||
int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16);
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
|
||||||
}
|
|
||||||
else if (isUserAttr)
|
|
||||||
{
|
|
||||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
|
||||||
|
|
||||||
if (!isOutAttr &&
|
|
||||||
!perPatch &&
|
|
||||||
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
|
|
||||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
|
||||||
{
|
|
||||||
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isOutAttr)
|
|
||||||
{
|
|
||||||
switch (iq)
|
|
||||||
{
|
|
||||||
case PixelImap.Constant:
|
|
||||||
context.Decorate(spvVar, Decoration.Flat);
|
|
||||||
break;
|
|
||||||
case PixelImap.ScreenLinear:
|
|
||||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.AddGlobalVariable(spvVar);
|
|
||||||
dict.Add(attrInfo.BaseValue, spvVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
|
||||||
{
|
|
||||||
var dict = isOutAttr ? context.Outputs : context.Inputs;
|
|
||||||
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
|
|
||||||
|
|
||||||
bool hasComponent = true;
|
|
||||||
int component = (attr >> 2) & 3;
|
|
||||||
int components = 1;
|
|
||||||
var type = attrInfo.Type & AggregateType.ElementTypeMask;
|
|
||||||
|
|
||||||
if (context.Config.LastInPipeline && isOutAttr)
|
|
||||||
{
|
|
||||||
components = context.Info.GetTransformFeedbackOutputComponents(attr);
|
|
||||||
|
|
||||||
if (components > 1)
|
|
||||||
{
|
|
||||||
attr &= ~0xf;
|
|
||||||
type = components switch
|
|
||||||
{
|
|
||||||
2 => AggregateType.Vector2 | AggregateType.FP32,
|
|
||||||
3 => AggregateType.Vector3 | AggregateType.FP32,
|
|
||||||
4 => AggregateType.Vector4 | AggregateType.FP32,
|
|
||||||
_ => AggregateType.FP32
|
|
||||||
};
|
|
||||||
|
|
||||||
hasComponent = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dict.ContainsKey(attr))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
|
||||||
var attrType = context.GetType(type, components);
|
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
|
|
||||||
{
|
|
||||||
int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr)
|
|
||||||
{
|
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
|
||||||
}
|
|
||||||
|
|
||||||
var spvType = context.TypePointer(storageClass, attrType);
|
|
||||||
var spvVar = context.Variable(spvType, storageClass);
|
|
||||||
|
|
||||||
Debug.Assert(attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd);
|
|
||||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
|
||||||
|
|
||||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
|
||||||
|
|
||||||
if (hasComponent)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOutAttr)
|
|
||||||
{
|
|
||||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attr);
|
|
||||||
if (tfOutput.Valid)
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
|
|
||||||
context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride);
|
|
||||||
context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((context.Config.PassthroughAttributes & (1 << location)) != 0 &&
|
|
||||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
|
||||||
{
|
|
||||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (iq)
|
|
||||||
{
|
|
||||||
case PixelImap.Constant:
|
|
||||||
context.Decorate(spvVar, Decoration.Flat);
|
|
||||||
break;
|
|
||||||
case PixelImap.ScreenLinear:
|
|
||||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.AddGlobalVariable(spvVar);
|
|
||||||
dict.Add(attr, spvVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BuiltIn GetBuiltIn(CodeGenContext context, int attr)
|
|
||||||
{
|
|
||||||
return attr switch
|
|
||||||
{
|
|
||||||
AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter,
|
|
||||||
AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner,
|
|
||||||
AttributeConsts.Layer => BuiltIn.Layer,
|
|
||||||
AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex,
|
|
||||||
AttributeConsts.PointSize => BuiltIn.PointSize,
|
|
||||||
AttributeConsts.PositionX => context.Config.Stage == ShaderStage.Fragment ? BuiltIn.FragCoord : BuiltIn.Position,
|
|
||||||
AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance,
|
|
||||||
AttributeConsts.PointCoordX => BuiltIn.PointCoord,
|
|
||||||
AttributeConsts.TessCoordX => BuiltIn.TessCoord,
|
|
||||||
AttributeConsts.InstanceId => BuiltIn.InstanceId,
|
|
||||||
AttributeConsts.VertexId => BuiltIn.VertexId,
|
|
||||||
AttributeConsts.BaseInstance => BuiltIn.BaseInstance,
|
|
||||||
AttributeConsts.BaseVertex => BuiltIn.BaseVertex,
|
|
||||||
AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex,
|
|
||||||
AttributeConsts.VertexIndex => BuiltIn.VertexIndex,
|
|
||||||
AttributeConsts.DrawIndex => BuiltIn.DrawIndex,
|
|
||||||
AttributeConsts.FrontFacing => BuiltIn.FrontFacing,
|
|
||||||
AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth,
|
|
||||||
AttributeConsts.ThreadKill => BuiltIn.HelperInvocation,
|
|
||||||
AttributeConsts.ThreadIdX => BuiltIn.LocalInvocationId,
|
|
||||||
AttributeConsts.CtaIdX => BuiltIn.WorkgroupId,
|
|
||||||
AttributeConsts.LaneId => BuiltIn.SubgroupLocalInvocationId,
|
|
||||||
AttributeConsts.InvocationId => BuiltIn.InvocationId,
|
|
||||||
AttributeConsts.PrimitiveId => BuiltIn.PrimitiveId,
|
|
||||||
AttributeConsts.PatchVerticesIn => BuiltIn.PatchVertices,
|
|
||||||
AttributeConsts.EqMask => BuiltIn.SubgroupEqMask,
|
|
||||||
AttributeConsts.GeMask => BuiltIn.SubgroupGeMask,
|
|
||||||
AttributeConsts.GtMask => BuiltIn.SubgroupGtMask,
|
|
||||||
AttributeConsts.LeMask => BuiltIn.SubgroupLeMask,
|
|
||||||
AttributeConsts.LtMask => BuiltIn.SubgroupLtMask,
|
|
||||||
AttributeConsts.SupportBlockViewInverseX => BuiltIn.Position,
|
|
||||||
AttributeConsts.SupportBlockViewInverseY => BuiltIn.Position,
|
|
||||||
_ => throw new ArgumentException($"Invalid attribute number 0x{attr:X}.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetStagePrefix(ShaderStage stage)
|
|
||||||
{
|
|
||||||
return StagePrefixes[(int)stage];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,227 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
|
||||||
using static Spv.Specification;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|
||||||
{
|
|
||||||
using SpvInstruction = Spv.Generator.Instruction;
|
|
||||||
|
|
||||||
static class ScalingHelpers
|
|
||||||
{
|
|
||||||
public static SpvInstruction ApplyScaling(
|
|
||||||
CodeGenContext context,
|
|
||||||
AstTextureOperation texOp,
|
|
||||||
SpvInstruction vector,
|
|
||||||
bool intCoords,
|
|
||||||
bool isBindless,
|
|
||||||
bool isIndexed,
|
|
||||||
bool isArray,
|
|
||||||
int pCount)
|
|
||||||
{
|
|
||||||
if (intCoords)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
int index = texOp.Inst == Instruction.ImageLoad
|
|
||||||
? context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp)
|
|
||||||
: context.Config.FindTextureDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
if (pCount == 3 && isArray)
|
|
||||||
{
|
|
||||||
return ApplyScaling2DArray(context, vector, index);
|
|
||||||
}
|
|
||||||
else if (pCount == 2 && !isArray)
|
|
||||||
{
|
|
||||||
return ApplyScaling2D(context, vector, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SpvInstruction ApplyScaling2DArray(CodeGenContext context, SpvInstruction vector, int index)
|
|
||||||
{
|
|
||||||
// The array index is not scaled, just x and y.
|
|
||||||
var vectorXY = context.VectorShuffle(context.TypeVector(context.TypeS32(), 2), vector, vector, 0, 1);
|
|
||||||
var vectorZ = context.CompositeExtract(context.TypeS32(), vector, 2);
|
|
||||||
var vectorXYScaled = ApplyScaling2D(context, vectorXY, index);
|
|
||||||
var vectorScaled = context.CompositeConstruct(context.TypeVector(context.TypeS32(), 3), vectorXYScaled, vectorZ);
|
|
||||||
|
|
||||||
return vectorScaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SpvInstruction ApplyScaling2D(CodeGenContext context, SpvInstruction vector, int index)
|
|
||||||
{
|
|
||||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
|
||||||
var fieldIndex = context.Constant(context.TypeU32(), 4);
|
|
||||||
var scaleIndex = context.Constant(context.TypeU32(), index);
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
|
||||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
|
|
||||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
|
||||||
|
|
||||||
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
|
|
||||||
var scale = context.Load(context.TypeFP32(), scaleElemPointer);
|
|
||||||
|
|
||||||
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
|
|
||||||
var localVector = context.CoordTemp;
|
|
||||||
|
|
||||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
|
||||||
|
|
||||||
var mergeLabel = context.Label();
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
var scaledInterpolatedLabel = context.Label();
|
|
||||||
var scaledNoInterpolationLabel = context.Label();
|
|
||||||
|
|
||||||
var needsInterpolation = context.FOrdLessThan(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 0f));
|
|
||||||
|
|
||||||
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(needsInterpolation, scaledInterpolatedLabel, scaledNoInterpolationLabel);
|
|
||||||
|
|
||||||
// scale < 0.0
|
|
||||||
context.AddLabel(scaledInterpolatedLabel);
|
|
||||||
|
|
||||||
ApplyScalingInterpolated(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
// scale >= 0.0
|
|
||||||
context.AddLabel(scaledNoInterpolationLabel);
|
|
||||||
|
|
||||||
ApplyScalingNoInterpolation(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(mergeLabel);
|
|
||||||
|
|
||||||
var passthroughLabel = context.Label();
|
|
||||||
var finalMergeLabel = context.Label();
|
|
||||||
|
|
||||||
context.SelectionMerge(finalMergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(passthrough, passthroughLabel, finalMergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(passthroughLabel);
|
|
||||||
|
|
||||||
context.Store(localVector, vector);
|
|
||||||
context.Branch(finalMergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(finalMergeLabel);
|
|
||||||
|
|
||||||
return context.Load(ivector2Type, localVector);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var passthroughLabel = context.Label();
|
|
||||||
var scaledLabel = context.Label();
|
|
||||||
|
|
||||||
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(passthrough, passthroughLabel, scaledLabel);
|
|
||||||
|
|
||||||
// scale == 1.0
|
|
||||||
context.AddLabel(passthroughLabel);
|
|
||||||
|
|
||||||
context.Store(localVector, vector);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
// scale != 1.0
|
|
||||||
context.AddLabel(scaledLabel);
|
|
||||||
|
|
||||||
ApplyScalingNoInterpolation(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(mergeLabel);
|
|
||||||
|
|
||||||
return context.Load(ivector2Type, localVector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyScalingInterpolated(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
|
|
||||||
{
|
|
||||||
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
|
|
||||||
|
|
||||||
var scaleNegated = context.FNegate(context.TypeFP32(), scale);
|
|
||||||
var scaleVector = context.CompositeConstruct(vector2Type, scaleNegated, scaleNegated);
|
|
||||||
|
|
||||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
|
||||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated);
|
|
||||||
|
|
||||||
var fragCoordPointer = context.Inputs[AttributeConsts.PositionX];
|
|
||||||
var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer);
|
|
||||||
var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1);
|
|
||||||
|
|
||||||
var scaleMod = context.FMod(vector2Type, fragCoordXY, scaleVector);
|
|
||||||
var vectorInterpolated = context.FAdd(vector2Type, vectorScaled, scaleMod);
|
|
||||||
|
|
||||||
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorInterpolated));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyScalingNoInterpolation(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
scale = context.GlslFAbs(context.TypeFP32(), scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
|
|
||||||
|
|
||||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
|
||||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scale);
|
|
||||||
|
|
||||||
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorScaled));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SpvInstruction ApplyUnscaling(
|
|
||||||
CodeGenContext context,
|
|
||||||
AstTextureOperation texOp,
|
|
||||||
SpvInstruction size,
|
|
||||||
bool isBindless,
|
|
||||||
bool isIndexed)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
int index = context.Config.FindTextureDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
|
||||||
var fieldIndex = context.Constant(context.TypeU32(), 4);
|
|
||||||
var scaleIndex = context.Constant(context.TypeU32(), index);
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
|
||||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
|
|
||||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
|
||||||
|
|
||||||
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
|
|
||||||
var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer));
|
|
||||||
|
|
||||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
|
||||||
|
|
||||||
var sizeFloat = context.ConvertSToF(context.TypeFP32(), size);
|
|
||||||
var sizeUnscaled = context.FDiv(context.TypeFP32(), sizeFloat, scale);
|
|
||||||
var sizeUnscaledInt = context.ConvertFToS(context.TypeS32(), sizeUnscaled);
|
|
||||||
|
|
||||||
return context.Select(context.TypeS32(), passthrough, size, sizeUnscaledInt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Shader.Decoders;
|
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
|
|
||||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Instructions
|
|
||||||
{
|
|
||||||
static partial class InstEmit
|
|
||||||
{
|
|
||||||
public static void Pset(EmitterContext context)
|
|
||||||
{
|
|
||||||
InstPset op = context.GetOp<InstPset>();
|
|
||||||
|
|
||||||
Operand srcA = context.BitwiseNot(Register(op.Src2Pred, RegisterType.Predicate), op.Src2PredInv);
|
|
||||||
Operand srcB = context.BitwiseNot(Register(op.Src1Pred, RegisterType.Predicate), op.Src1PredInv);
|
|
||||||
Operand srcC = context.BitwiseNot(Register(op.SrcPred, RegisterType.Predicate), op.SrcPredInv);
|
|
||||||
|
|
||||||
Operand res = GetPredLogicalOp(context, op.BoolOpAB, srcA, srcB);
|
|
||||||
res = GetPredLogicalOp(context, op.BoolOpC, res, srcC);
|
|
||||||
|
|
||||||
Operand dest = GetDest(op.Dest);
|
|
||||||
|
|
||||||
if (op.BVal)
|
|
||||||
{
|
|
||||||
context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.Copy(dest, res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Psetp(EmitterContext context)
|
|
||||||
{
|
|
||||||
InstPsetp op = context.GetOp<InstPsetp>();
|
|
||||||
|
|
||||||
Operand srcA = context.BitwiseNot(Register(op.Src2Pred, RegisterType.Predicate), op.Src2PredInv);
|
|
||||||
Operand srcB = context.BitwiseNot(Register(op.Src1Pred, RegisterType.Predicate), op.Src1PredInv);
|
|
||||||
|
|
||||||
Operand p0Res = GetPredLogicalOp(context, op.BoolOpAB, srcA, srcB);
|
|
||||||
Operand p1Res = context.BitwiseNot(p0Res);
|
|
||||||
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
|
|
||||||
|
|
||||||
p0Res = GetPredLogicalOp(context, op.BoolOpC, p0Res, srcPred);
|
|
||||||
p1Res = GetPredLogicalOp(context, op.BoolOpC, p1Res, srcPred);
|
|
||||||
|
|
||||||
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
|
|
||||||
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|
||||||
{
|
|
||||||
enum OperandType
|
|
||||||
{
|
|
||||||
Argument,
|
|
||||||
Attribute,
|
|
||||||
AttributePerPatch,
|
|
||||||
Constant,
|
|
||||||
ConstantBuffer,
|
|
||||||
Label,
|
|
||||||
LocalVariable,
|
|
||||||
Register,
|
|
||||||
Undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
static class OperandTypeExtensions
|
|
||||||
{
|
|
||||||
public static bool IsAttribute(this OperandType type)
|
|
||||||
{
|
|
||||||
return type == OperandType.Attribute || type == OperandType.AttributePerPatch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
|
||||||
{
|
|
||||||
readonly struct TransformFeedbackOutput
|
|
||||||
{
|
|
||||||
public readonly bool Valid;
|
|
||||||
public readonly int Buffer;
|
|
||||||
public readonly int Offset;
|
|
||||||
public readonly int Stride;
|
|
||||||
|
|
||||||
public TransformFeedbackOutput(int buffer, int offset, int stride)
|
|
||||||
{
|
|
||||||
Valid = true;
|
|
||||||
Buffer = buffer;
|
|
||||||
Offset = offset;
|
|
||||||
Stride = stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StructuredProgramInfo
|
|
||||||
{
|
|
||||||
public List<StructuredFunction> Functions { get; }
|
|
||||||
|
|
||||||
public HashSet<int> Inputs { get; }
|
|
||||||
public HashSet<int> Outputs { get; }
|
|
||||||
public HashSet<int> InputsPerPatch { get; }
|
|
||||||
public HashSet<int> OutputsPerPatch { get; }
|
|
||||||
|
|
||||||
public HelperFunctionsMask HelperFunctionsMask { get; set; }
|
|
||||||
|
|
||||||
public TransformFeedbackOutput[] TransformFeedbackOutputs { get; }
|
|
||||||
|
|
||||||
public StructuredProgramInfo()
|
|
||||||
{
|
|
||||||
Functions = new List<StructuredFunction>();
|
|
||||||
|
|
||||||
Inputs = new HashSet<int>();
|
|
||||||
Outputs = new HashSet<int>();
|
|
||||||
InputsPerPatch = new HashSet<int>();
|
|
||||||
OutputsPerPatch = new HashSet<int>();
|
|
||||||
|
|
||||||
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransformFeedbackOutput GetTransformFeedbackOutput(int attr)
|
|
||||||
{
|
|
||||||
int index = attr / 4;
|
|
||||||
return TransformFeedbackOutputs[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetTransformFeedbackOutputComponents(int attr)
|
|
||||||
{
|
|
||||||
int index = attr / 4;
|
|
||||||
int baseIndex = index & ~3;
|
|
||||||
|
|
||||||
int count = 1;
|
|
||||||
|
|
||||||
for (; count < 4; count++)
|
|
||||||
{
|
|
||||||
ref var prev = ref TransformFeedbackOutputs[baseIndex + count - 1];
|
|
||||||
ref var curr = ref TransformFeedbackOutputs[baseIndex + count];
|
|
||||||
|
|
||||||
int prevOffset = prev.Offset;
|
|
||||||
int currOffset = curr.Offset;
|
|
||||||
|
|
||||||
if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseIndex + count <= index)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.Shader.Translation
|
|
||||||
{
|
|
||||||
enum AggregateType
|
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
Void,
|
|
||||||
Bool,
|
|
||||||
FP32,
|
|
||||||
FP64,
|
|
||||||
S32,
|
|
||||||
U32,
|
|
||||||
|
|
||||||
ElementTypeMask = 0xff,
|
|
||||||
|
|
||||||
ElementCountShift = 8,
|
|
||||||
ElementCountMask = 3 << ElementCountShift,
|
|
||||||
|
|
||||||
Scalar = 0 << ElementCountShift,
|
|
||||||
Vector2 = 1 << ElementCountShift,
|
|
||||||
Vector3 = 2 << ElementCountShift,
|
|
||||||
Vector4 = 3 << ElementCountShift,
|
|
||||||
|
|
||||||
Array = 1 << 10
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.Shader.Translation
|
|
||||||
{
|
|
||||||
static class AttributeConsts
|
|
||||||
{
|
|
||||||
public const int TessLevelOuter0 = 0x000;
|
|
||||||
public const int TessLevelOuter1 = 0x004;
|
|
||||||
public const int TessLevelOuter2 = 0x008;
|
|
||||||
public const int TessLevelOuter3 = 0x00c;
|
|
||||||
public const int TessLevelInner0 = 0x010;
|
|
||||||
public const int TessLevelInner1 = 0x014;
|
|
||||||
public const int PrimitiveId = 0x060;
|
|
||||||
public const int Layer = 0x064;
|
|
||||||
public const int ViewportIndex = 0x068;
|
|
||||||
public const int PointSize = 0x06c;
|
|
||||||
public const int PositionX = 0x070;
|
|
||||||
public const int PositionY = 0x074;
|
|
||||||
public const int PositionZ = 0x078;
|
|
||||||
public const int PositionW = 0x07c;
|
|
||||||
public const int FrontColorDiffuseR = 0x280;
|
|
||||||
public const int FrontColorDiffuseG = 0x284;
|
|
||||||
public const int FrontColorDiffuseB = 0x288;
|
|
||||||
public const int FrontColorDiffuseA = 0x28c;
|
|
||||||
public const int FrontColorSpecularR = 0x290;
|
|
||||||
public const int FrontColorSpecularG = 0x294;
|
|
||||||
public const int FrontColorSpecularB = 0x298;
|
|
||||||
public const int FrontColorSpecularA = 0x29c;
|
|
||||||
public const int BackColorDiffuseR = 0x2a0;
|
|
||||||
public const int BackColorDiffuseG = 0x2a4;
|
|
||||||
public const int BackColorDiffuseB = 0x2a8;
|
|
||||||
public const int BackColorDiffuseA = 0x2ac;
|
|
||||||
public const int BackColorSpecularR = 0x2b0;
|
|
||||||
public const int BackColorSpecularG = 0x2b4;
|
|
||||||
public const int BackColorSpecularB = 0x2b8;
|
|
||||||
public const int BackColorSpecularA = 0x2bc;
|
|
||||||
public const int ClipDistance0 = 0x2c0;
|
|
||||||
public const int ClipDistance1 = 0x2c4;
|
|
||||||
public const int ClipDistance2 = 0x2c8;
|
|
||||||
public const int ClipDistance3 = 0x2cc;
|
|
||||||
public const int ClipDistance4 = 0x2d0;
|
|
||||||
public const int ClipDistance5 = 0x2d4;
|
|
||||||
public const int ClipDistance6 = 0x2d8;
|
|
||||||
public const int ClipDistance7 = 0x2dc;
|
|
||||||
public const int PointCoordX = 0x2e0;
|
|
||||||
public const int PointCoordY = 0x2e4;
|
|
||||||
public const int TessCoordX = 0x2f0;
|
|
||||||
public const int TessCoordY = 0x2f4;
|
|
||||||
public const int InstanceId = 0x2f8;
|
|
||||||
public const int VertexId = 0x2fc;
|
|
||||||
public const int TexCoordCount = 10;
|
|
||||||
public const int TexCoordBase = 0x300;
|
|
||||||
public const int TexCoordEnd = TexCoordBase + TexCoordCount * 16;
|
|
||||||
public const int FrontFacing = 0x3fc;
|
|
||||||
|
|
||||||
public const int UserAttributesCount = 32;
|
|
||||||
public const int UserAttributeBase = 0x80;
|
|
||||||
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
|
|
||||||
|
|
||||||
public const int UserAttributePerPatchBase = 0x18;
|
|
||||||
public const int UserAttributePerPatchEnd = 0x200;
|
|
||||||
|
|
||||||
public const int LoadOutputMask = 1 << 30;
|
|
||||||
public const int Mask = 0x3fffffff;
|
|
||||||
|
|
||||||
|
|
||||||
// Note: Those attributes are used internally by the translator
|
|
||||||
// only, they don't exist on Maxwell.
|
|
||||||
public const int SpecialMask = 0xf << 24;
|
|
||||||
public const int FragmentOutputDepth = 0x1000000;
|
|
||||||
public const int FragmentOutputColorBase = 0x1000010;
|
|
||||||
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
|
||||||
|
|
||||||
public const int FragmentOutputIsBgraBase = 0x1000100;
|
|
||||||
public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4;
|
|
||||||
|
|
||||||
public const int SupportBlockViewInverseX = 0x1000200;
|
|
||||||
public const int SupportBlockViewInverseY = 0x1000204;
|
|
||||||
|
|
||||||
public const int ThreadIdX = 0x2000000;
|
|
||||||
public const int ThreadIdY = 0x2000004;
|
|
||||||
public const int ThreadIdZ = 0x2000008;
|
|
||||||
|
|
||||||
public const int CtaIdX = 0x2000010;
|
|
||||||
public const int CtaIdY = 0x2000014;
|
|
||||||
public const int CtaIdZ = 0x2000018;
|
|
||||||
|
|
||||||
public const int LaneId = 0x2000020;
|
|
||||||
|
|
||||||
public const int InvocationId = 0x2000024;
|
|
||||||
public const int PatchVerticesIn = 0x2000028;
|
|
||||||
|
|
||||||
public const int EqMask = 0x2000030;
|
|
||||||
public const int GeMask = 0x2000034;
|
|
||||||
public const int GtMask = 0x2000038;
|
|
||||||
public const int LeMask = 0x200003c;
|
|
||||||
public const int LtMask = 0x2000040;
|
|
||||||
|
|
||||||
public const int ThreadKill = 0x2000044;
|
|
||||||
|
|
||||||
public const int BaseInstance = 0x2000050;
|
|
||||||
public const int BaseVertex = 0x2000054;
|
|
||||||
public const int InstanceIndex = 0x2000058;
|
|
||||||
public const int VertexIndex = 0x200005c;
|
|
||||||
public const int DrawIndex = 0x2000060;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,210 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation
|
|
||||||
{
|
|
||||||
readonly struct AttributeInfo
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = new Dictionary<int, AttributeInfo>()
|
|
||||||
{
|
|
||||||
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector4 | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
|
|
||||||
|
|
||||||
// Special.
|
|
||||||
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 0, 1, AggregateType.Bool) },
|
|
||||||
{ AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 0, 1, AggregateType.U32) },
|
|
||||||
{ AttributeConsts.InvocationId, new AttributeInfo(AttributeConsts.InvocationId, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PrimitiveId, new AttributeInfo(AttributeConsts.PrimitiveId, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.PatchVerticesIn, new AttributeInfo(AttributeConsts.PatchVerticesIn, 0, 1, AggregateType.S32) },
|
|
||||||
{ AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly Dictionary<int, AttributeInfo> _builtInAttributesPerPatch = new Dictionary<int, AttributeInfo>()
|
|
||||||
{
|
|
||||||
{ AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
{ AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
|
|
||||||
};
|
|
||||||
|
|
||||||
public int BaseValue { get; }
|
|
||||||
public int Value { get; }
|
|
||||||
public int Length { get; }
|
|
||||||
public AggregateType Type { get; }
|
|
||||||
public bool IsBuiltin { get; }
|
|
||||||
public bool IsValid => Type != AggregateType.Invalid;
|
|
||||||
|
|
||||||
public AttributeInfo(int baseValue, int index, int length, AggregateType type, bool isBuiltin = true)
|
|
||||||
{
|
|
||||||
BaseValue = baseValue;
|
|
||||||
Value = baseValue + index * 4;
|
|
||||||
Length = length;
|
|
||||||
Type = type;
|
|
||||||
IsBuiltin = isBuiltin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetInnermostIndex()
|
|
||||||
{
|
|
||||||
return (Value - BaseValue) / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr, bool perPatch)
|
|
||||||
{
|
|
||||||
return perPatch ? ValidatePerPatch(config, value, isOutAttr) : Validate(config, value, isOutAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr)
|
|
||||||
{
|
|
||||||
if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return From(config, value, isOutAttr).IsValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ValidatePerPatch(ShaderConfig config, int value, bool isOutAttr)
|
|
||||||
{
|
|
||||||
return FromPatch(config, value, isOutAttr).IsValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr)
|
|
||||||
{
|
|
||||||
value &= ~3;
|
|
||||||
|
|
||||||
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
|
||||||
{
|
|
||||||
int location = (value - AttributeConsts.UserAttributeBase) / 16;
|
|
||||||
|
|
||||||
AggregateType elemType;
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Vertex && !isOutAttr)
|
|
||||||
{
|
|
||||||
elemType = config.GpuAccessor.QueryAttributeType(location).ToAggregateType();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
elemType = AggregateType.FP32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
|
|
||||||
}
|
|
||||||
else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
|
|
||||||
{
|
|
||||||
int location = (value - AttributeConsts.FragmentOutputColorBase) / 16;
|
|
||||||
var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch
|
|
||||||
{
|
|
||||||
AttributeType.Sint => AggregateType.S32,
|
|
||||||
AttributeType.Uint => AggregateType.U32,
|
|
||||||
_ => AggregateType.FP32
|
|
||||||
};
|
|
||||||
|
|
||||||
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
|
|
||||||
}
|
|
||||||
else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY)
|
|
||||||
{
|
|
||||||
return new AttributeInfo(value, 0, 1, AggregateType.FP32);
|
|
||||||
}
|
|
||||||
else if (_builtInAttributes.TryGetValue(value, out AttributeInfo info))
|
|
||||||
{
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AttributeInfo FromPatch(ShaderConfig config, int value, bool isOutAttr)
|
|
||||||
{
|
|
||||||
value &= ~3;
|
|
||||||
|
|
||||||
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
|
|
||||||
{
|
|
||||||
int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf;
|
|
||||||
return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector4 | AggregateType.FP32, false);
|
|
||||||
}
|
|
||||||
else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info))
|
|
||||||
{
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsArrayBuiltIn(int attr)
|
|
||||||
{
|
|
||||||
if (attr <= AttributeConsts.TessLevelInner1 ||
|
|
||||||
attr == AttributeConsts.TessCoordX ||
|
|
||||||
attr == AttributeConsts.TessCoordY)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (attr & AttributeConsts.SpecialMask) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr)
|
|
||||||
{
|
|
||||||
if (isOutAttr)
|
|
||||||
{
|
|
||||||
return stage == ShaderStage.TessellationControl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return stage == ShaderStage.TessellationControl ||
|
|
||||||
stage == ShaderStage.TessellationEvaluation ||
|
|
||||||
stage == ShaderStage.Geometry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsArrayAttributeSpirv(ShaderStage stage, bool isOutAttr)
|
|
||||||
{
|
|
||||||
if (isOutAttr)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return stage == ShaderStage.TessellationControl ||
|
|
||||||
stage == ShaderStage.TessellationEvaluation ||
|
|
||||||
stage == ShaderStage.Geometry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation
|
|
||||||
{
|
|
||||||
static class GlobalMemory
|
|
||||||
{
|
|
||||||
private const int StorageDescsBaseOffset = 0x44; // In words.
|
|
||||||
|
|
||||||
public const int StorageDescSize = 4; // In words.
|
|
||||||
public const int StorageMaxCount = 16;
|
|
||||||
|
|
||||||
public const int StorageDescsSize = StorageDescSize * StorageMaxCount;
|
|
||||||
|
|
||||||
public const int UbeBaseOffset = 0x98; // In words.
|
|
||||||
public const int UbeMaxCount = 9;
|
|
||||||
public const int UbeDescsSize = StorageDescSize * UbeMaxCount;
|
|
||||||
public const int UbeFirstCbuf = 8;
|
|
||||||
|
|
||||||
public static bool UsesGlobalMemory(Instruction inst)
|
|
||||||
{
|
|
||||||
return (inst.IsAtomic() && IsGlobalMr(inst)) ||
|
|
||||||
inst == Instruction.LoadGlobal ||
|
|
||||||
inst == Instruction.StoreGlobal ||
|
|
||||||
inst == Instruction.StoreGlobal16 ||
|
|
||||||
inst == Instruction.StoreGlobal8;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsGlobalMr(Instruction inst)
|
|
||||||
{
|
|
||||||
return (inst & Instruction.MrMask) == Instruction.MrGlobal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetStorageCbOffset(ShaderStage stage, int slot)
|
|
||||||
{
|
|
||||||
return GetStorageBaseCbOffset(stage) + slot * StorageDescSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetStorageBaseCbOffset(ShaderStage stage)
|
|
||||||
{
|
|
||||||
return stage switch
|
|
||||||
{
|
|
||||||
ShaderStage.Compute => StorageDescsBaseOffset + 2 * StorageDescsSize,
|
|
||||||
ShaderStage.Vertex => StorageDescsBaseOffset,
|
|
||||||
ShaderStage.TessellationControl => StorageDescsBaseOffset + 1 * StorageDescsSize,
|
|
||||||
ShaderStage.TessellationEvaluation => StorageDescsBaseOffset + 2 * StorageDescsSize,
|
|
||||||
ShaderStage.Geometry => StorageDescsBaseOffset + 3 * StorageDescsSize,
|
|
||||||
ShaderStage.Fragment => StorageDescsBaseOffset + 4 * StorageDescsSize,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetConstantUbeOffset(int slot)
|
|
||||||
{
|
|
||||||
return UbeBaseOffset + slot * StorageDescSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,435 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
|
||||||
using static Ryujinx.Graphics.Shader.Translation.GlobalMemory;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|
||||||
{
|
|
||||||
static class GlobalToStorage
|
|
||||||
{
|
|
||||||
public static void RunPass(BasicBlock block, ShaderConfig config, ref int sbUseMask, ref int ubeUseMask)
|
|
||||||
{
|
|
||||||
int sbStart = GetStorageBaseCbOffset(config.Stage);
|
|
||||||
int sbEnd = sbStart + StorageDescsSize;
|
|
||||||
|
|
||||||
int ubeStart = UbeBaseOffset;
|
|
||||||
int ubeEnd = UbeBaseOffset + UbeDescsSize;
|
|
||||||
|
|
||||||
for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < node.Value.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
Operand src = node.Value.GetSource(index);
|
|
||||||
|
|
||||||
int storageIndex = GetStorageIndex(src, sbStart, sbEnd);
|
|
||||||
|
|
||||||
if (storageIndex >= 0)
|
|
||||||
{
|
|
||||||
sbUseMask |= 1 << storageIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Compute)
|
|
||||||
{
|
|
||||||
int constantIndex = GetStorageIndex(src, ubeStart, ubeEnd);
|
|
||||||
|
|
||||||
if (constantIndex >= 0)
|
|
||||||
{
|
|
||||||
ubeUseMask |= 1 << constantIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(node.Value is Operation operation))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UsesGlobalMemory(operation.Inst))
|
|
||||||
{
|
|
||||||
Operand source = operation.GetSource(0);
|
|
||||||
|
|
||||||
int storageIndex = SearchForStorageBase(block, source, sbStart, sbEnd);
|
|
||||||
|
|
||||||
if (storageIndex >= 0)
|
|
||||||
{
|
|
||||||
// Storage buffers are implemented using global memory access.
|
|
||||||
// If we know from where the base address of the access is loaded,
|
|
||||||
// we can guess which storage buffer it is accessing.
|
|
||||||
// We can then replace the global memory access with a storage
|
|
||||||
// buffer access.
|
|
||||||
node = ReplaceGlobalWithStorage(block, node, config, storageIndex);
|
|
||||||
}
|
|
||||||
else if (config.Stage == ShaderStage.Compute && operation.Inst == Instruction.LoadGlobal)
|
|
||||||
{
|
|
||||||
// Here we effectively try to replace a LDG instruction with LDC.
|
|
||||||
// The hardware only supports a limited amount of constant buffers
|
|
||||||
// so NVN "emulates" more constant buffers using global memory access.
|
|
||||||
// Here we try to replace the global access back to a constant buffer
|
|
||||||
// load.
|
|
||||||
storageIndex = SearchForStorageBase(block, source, ubeStart, ubeStart + ubeEnd);
|
|
||||||
|
|
||||||
if (storageIndex >= 0)
|
|
||||||
{
|
|
||||||
node = ReplaceLdgWithLdc(node, config, storageIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LinkedListNode<INode> ReplaceGlobalWithStorage(BasicBlock block, LinkedListNode<INode> node, ShaderConfig config, int storageIndex)
|
|
||||||
{
|
|
||||||
Operation operation = (Operation)node.Value;
|
|
||||||
|
|
||||||
bool isAtomic = operation.Inst.IsAtomic();
|
|
||||||
bool isStg16Or8 = operation.Inst == Instruction.StoreGlobal16 || operation.Inst == Instruction.StoreGlobal8;
|
|
||||||
bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal || isStg16Or8;
|
|
||||||
|
|
||||||
config.SetUsedStorageBuffer(storageIndex, isWrite);
|
|
||||||
|
|
||||||
Operand[] sources = new Operand[operation.SourcesCount];
|
|
||||||
|
|
||||||
sources[0] = Const(storageIndex);
|
|
||||||
sources[1] = GetStorageOffset(block, node, config, storageIndex, operation.GetSource(0), isStg16Or8);
|
|
||||||
|
|
||||||
for (int index = 2; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
sources[index] = operation.GetSource(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operation storageOp;
|
|
||||||
|
|
||||||
if (isAtomic)
|
|
||||||
{
|
|
||||||
Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage;
|
|
||||||
|
|
||||||
storageOp = new Operation(inst, operation.Dest, sources);
|
|
||||||
}
|
|
||||||
else if (operation.Inst == Instruction.LoadGlobal)
|
|
||||||
{
|
|
||||||
storageOp = new Operation(Instruction.LoadStorage, operation.Dest, sources);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Instruction storeInst = operation.Inst switch
|
|
||||||
{
|
|
||||||
Instruction.StoreGlobal16 => Instruction.StoreStorage16,
|
|
||||||
Instruction.StoreGlobal8 => Instruction.StoreStorage8,
|
|
||||||
_ => Instruction.StoreStorage
|
|
||||||
};
|
|
||||||
|
|
||||||
storageOp = new Operation(storeInst, null, sources);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
operation.SetSource(index, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkedListNode<INode> oldNode = node;
|
|
||||||
|
|
||||||
node = node.List.AddBefore(node, storageOp);
|
|
||||||
|
|
||||||
node.List.Remove(oldNode);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand GetStorageOffset(
|
|
||||||
BasicBlock block,
|
|
||||||
LinkedListNode<INode> node,
|
|
||||||
ShaderConfig config,
|
|
||||||
int storageIndex,
|
|
||||||
Operand addrLow,
|
|
||||||
bool isStg16Or8)
|
|
||||||
{
|
|
||||||
int baseAddressCbOffset = GetStorageCbOffset(config.Stage, storageIndex);
|
|
||||||
|
|
||||||
bool storageAligned = !(config.GpuAccessor.QueryHasUnalignedStorageBuffer() || config.GpuAccessor.QueryHostStorageBufferOffsetAlignment() > Constants.StorageAlignment);
|
|
||||||
|
|
||||||
(Operand byteOffset, int constantOffset) = storageAligned ?
|
|
||||||
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) :
|
|
||||||
(null, 0);
|
|
||||||
|
|
||||||
if (byteOffset != null)
|
|
||||||
{
|
|
||||||
ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byteOffset == null)
|
|
||||||
{
|
|
||||||
Operand baseAddrLow = Cbuf(0, baseAddressCbOffset);
|
|
||||||
Operand baseAddrTrunc = Local();
|
|
||||||
|
|
||||||
Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
|
|
||||||
|
|
||||||
Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask);
|
|
||||||
|
|
||||||
node.List.AddBefore(node, andOp);
|
|
||||||
|
|
||||||
Operand offset = Local();
|
|
||||||
Operation subOp = new Operation(Instruction.Subtract, offset, addrLow, baseAddrTrunc);
|
|
||||||
|
|
||||||
node.List.AddBefore(node, subOp);
|
|
||||||
|
|
||||||
byteOffset = offset;
|
|
||||||
}
|
|
||||||
else if (constantOffset != 0)
|
|
||||||
{
|
|
||||||
Operand offset = Local();
|
|
||||||
Operation addOp = new Operation(Instruction.Add, offset, byteOffset, Const(constantOffset));
|
|
||||||
|
|
||||||
node.List.AddBefore(node, addOp);
|
|
||||||
|
|
||||||
byteOffset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStg16Or8)
|
|
||||||
{
|
|
||||||
return byteOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand wordOffset = Local();
|
|
||||||
Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2));
|
|
||||||
|
|
||||||
node.List.AddBefore(node, shrOp);
|
|
||||||
|
|
||||||
return wordOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsCb0Offset(Operand operand, int offset)
|
|
||||||
{
|
|
||||||
return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == 0 && operand.GetCbufOffset() == offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReplaceAddressAlignment(LinkedList<INode> list, Operand address, Operand byteOffset, int constantOffset)
|
|
||||||
{
|
|
||||||
// When we emit 16/8-bit LDG, we add extra code to determine the address alignment.
|
|
||||||
// Eliminate the storage buffer base address from this too, leaving only the byte offset.
|
|
||||||
|
|
||||||
foreach (INode useNode in address.UseOps)
|
|
||||||
{
|
|
||||||
if (useNode is Operation op && op.Inst == Instruction.BitwiseAnd)
|
|
||||||
{
|
|
||||||
Operand src1 = op.GetSource(0);
|
|
||||||
Operand src2 = op.GetSource(1);
|
|
||||||
|
|
||||||
int addressIndex = -1;
|
|
||||||
|
|
||||||
if (src1 == address && src2.Type == OperandType.Constant && src2.Value == 3)
|
|
||||||
{
|
|
||||||
addressIndex = 0;
|
|
||||||
}
|
|
||||||
else if (src2 == address && src1.Type == OperandType.Constant && src1.Value == 3)
|
|
||||||
{
|
|
||||||
addressIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addressIndex != -1)
|
|
||||||
{
|
|
||||||
LinkedListNode<INode> node = list.Find(op);
|
|
||||||
|
|
||||||
// Add offset calculation before the use. Needs to be on the same block.
|
|
||||||
if (node != null)
|
|
||||||
{
|
|
||||||
Operand offset = Local();
|
|
||||||
Operation addOp = new Operation(Instruction.Add, offset, byteOffset, Const(constantOffset));
|
|
||||||
list.AddBefore(node, addOp);
|
|
||||||
|
|
||||||
op.SetSource(addressIndex, offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int baseAddressCbOffset)
|
|
||||||
{
|
|
||||||
if (IsCb0Offset(address, baseAddressCbOffset))
|
|
||||||
{
|
|
||||||
// Direct offset: zero.
|
|
||||||
return (Const(0), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
(address, int constantOffset) = GetStorageConstantOffset(block, address);
|
|
||||||
|
|
||||||
address = Utils.FindLastOperation(address, block);
|
|
||||||
|
|
||||||
if (IsCb0Offset(address, baseAddressCbOffset))
|
|
||||||
{
|
|
||||||
// Only constant offset
|
|
||||||
return (Const(0), constantOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(address.AsgOp is Operation offsetAdd) || offsetAdd.Inst != Instruction.Add)
|
|
||||||
{
|
|
||||||
return (null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand src1 = offsetAdd.GetSource(0);
|
|
||||||
Operand src2 = Utils.FindLastOperation(offsetAdd.GetSource(1), block);
|
|
||||||
|
|
||||||
if (IsCb0Offset(src2, baseAddressCbOffset))
|
|
||||||
{
|
|
||||||
return (src1, constantOffset);
|
|
||||||
}
|
|
||||||
else if (IsCb0Offset(src1, baseAddressCbOffset))
|
|
||||||
{
|
|
||||||
return (src2, constantOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (Operand, int) GetStorageConstantOffset(BasicBlock block, Operand address)
|
|
||||||
{
|
|
||||||
if (!(address.AsgOp is Operation offsetAdd) || offsetAdd.Inst != Instruction.Add)
|
|
||||||
{
|
|
||||||
return (address, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand src1 = offsetAdd.GetSource(0);
|
|
||||||
Operand src2 = offsetAdd.GetSource(1);
|
|
||||||
|
|
||||||
if (src2.Type != OperandType.Constant)
|
|
||||||
{
|
|
||||||
return (address, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (src1, src2.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LinkedListNode<INode> ReplaceLdgWithLdc(LinkedListNode<INode> node, ShaderConfig config, int storageIndex)
|
|
||||||
{
|
|
||||||
Operation operation = (Operation)node.Value;
|
|
||||||
|
|
||||||
Operand GetCbufOffset()
|
|
||||||
{
|
|
||||||
Operand addrLow = operation.GetSource(0);
|
|
||||||
|
|
||||||
Operand baseAddrLow = Cbuf(0, UbeBaseOffset + storageIndex * StorageDescSize);
|
|
||||||
|
|
||||||
Operand baseAddrTrunc = Local();
|
|
||||||
|
|
||||||
Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
|
|
||||||
|
|
||||||
Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask);
|
|
||||||
|
|
||||||
node.List.AddBefore(node, andOp);
|
|
||||||
|
|
||||||
Operand byteOffset = Local();
|
|
||||||
Operand wordOffset = Local();
|
|
||||||
|
|
||||||
Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc);
|
|
||||||
Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2));
|
|
||||||
|
|
||||||
node.List.AddBefore(node, subOp);
|
|
||||||
node.List.AddBefore(node, shrOp);
|
|
||||||
|
|
||||||
return wordOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] sources = new Operand[operation.SourcesCount];
|
|
||||||
|
|
||||||
int cbSlot = UbeFirstCbuf + storageIndex;
|
|
||||||
|
|
||||||
sources[0] = Const(cbSlot);
|
|
||||||
sources[1] = GetCbufOffset();
|
|
||||||
|
|
||||||
config.SetUsedConstantBuffer(cbSlot);
|
|
||||||
|
|
||||||
for (int index = 2; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
sources[index] = operation.GetSource(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operation ldcOp = new Operation(Instruction.LoadConstant, operation.Dest, sources);
|
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
operation.SetSource(index, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkedListNode<INode> oldNode = node;
|
|
||||||
|
|
||||||
node = node.List.AddBefore(node, ldcOp);
|
|
||||||
|
|
||||||
node.List.Remove(oldNode);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int SearchForStorageBase(BasicBlock block, Operand globalAddress, int sbStart, int sbEnd)
|
|
||||||
{
|
|
||||||
globalAddress = Utils.FindLastOperation(globalAddress, block);
|
|
||||||
|
|
||||||
if (globalAddress.Type == OperandType.ConstantBuffer)
|
|
||||||
{
|
|
||||||
return GetStorageIndex(globalAddress, sbStart, sbEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operation operation = globalAddress.AsgOp as Operation;
|
|
||||||
|
|
||||||
if (operation == null || operation.Inst != Instruction.Add)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand src1 = operation.GetSource(0);
|
|
||||||
Operand src2 = operation.GetSource(1);
|
|
||||||
|
|
||||||
if ((src1.Type == OperandType.LocalVariable && src2.Type == OperandType.Constant) ||
|
|
||||||
(src2.Type == OperandType.LocalVariable && src1.Type == OperandType.Constant))
|
|
||||||
{
|
|
||||||
if (src1.Type == OperandType.LocalVariable)
|
|
||||||
{
|
|
||||||
operation = Utils.FindLastOperation(src1, block).AsgOp as Operation;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
operation = Utils.FindLastOperation(src2, block).AsgOp as Operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operation == null || operation.Inst != Instruction.Add)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
Operand source = operation.GetSource(index);
|
|
||||||
|
|
||||||
int storageIndex = GetStorageIndex(source, sbStart, sbEnd);
|
|
||||||
|
|
||||||
if (storageIndex != -1)
|
|
||||||
{
|
|
||||||
return storageIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetStorageIndex(Operand operand, int sbStart, int sbEnd)
|
|
||||||
{
|
|
||||||
if (operand.Type == OperandType.ConstantBuffer)
|
|
||||||
{
|
|
||||||
int slot = operand.GetCbufSlot();
|
|
||||||
int offset = operand.GetCbufOffset();
|
|
||||||
|
|
||||||
if (slot == 0 && offset >= sbStart && offset < sbEnd)
|
|
||||||
{
|
|
||||||
int storageIndex = (offset - sbStart) / StorageDescSize;
|
|
||||||
|
|
||||||
return storageIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,763 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Shader.Decoders;
|
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
|
||||||
using static Ryujinx.Graphics.Shader.Translation.GlobalMemory;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation
|
|
||||||
{
|
|
||||||
static class Rewriter
|
|
||||||
{
|
|
||||||
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
|
|
||||||
{
|
|
||||||
bool isVertexShader = config.Stage == ShaderStage.Vertex;
|
|
||||||
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
|
|
||||||
bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat();
|
|
||||||
|
|
||||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
|
||||||
{
|
|
||||||
BasicBlock block = blocks[blkIndex];
|
|
||||||
|
|
||||||
for (LinkedListNode<INode> node = block.Operations.First; node != null;)
|
|
||||||
{
|
|
||||||
if (node.Value is not Operation operation)
|
|
||||||
{
|
|
||||||
node = node.Next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVertexShader)
|
|
||||||
{
|
|
||||||
if (hasConstantBufferDrawParameters)
|
|
||||||
{
|
|
||||||
if (ReplaceConstantBufferWithDrawParameters(operation))
|
|
||||||
{
|
|
||||||
config.SetUsedFeature(FeatureFlags.DrawParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (HasConstantBufferDrawParameters(operation))
|
|
||||||
{
|
|
||||||
config.SetUsedFeature(FeatureFlags.DrawParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkedListNode<INode> nextNode = node.Next;
|
|
||||||
|
|
||||||
if (operation is TextureOperation texOp)
|
|
||||||
{
|
|
||||||
if (texOp.Inst == Instruction.TextureSample)
|
|
||||||
{
|
|
||||||
node = RewriteTextureSample(node, config);
|
|
||||||
|
|
||||||
if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat)
|
|
||||||
{
|
|
||||||
node = InsertSnormNormalization(node, config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nextNode = node.Next;
|
|
||||||
}
|
|
||||||
else if (UsesGlobalMemory(operation.Inst))
|
|
||||||
{
|
|
||||||
nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = nextNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LinkedListNode<INode> RewriteGlobalAccess(LinkedListNode<INode> node, ShaderConfig config)
|
|
||||||
{
|
|
||||||
Operation operation = (Operation)node.Value;
|
|
||||||
|
|
||||||
bool isAtomic = operation.Inst.IsAtomic();
|
|
||||||
bool isStg16Or8 = operation.Inst == Instruction.StoreGlobal16 || operation.Inst == Instruction.StoreGlobal8;
|
|
||||||
bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal || isStg16Or8;
|
|
||||||
|
|
||||||
Operation storageOp = null;
|
|
||||||
|
|
||||||
Operand PrependOperation(Instruction inst, params Operand[] sources)
|
|
||||||
{
|
|
||||||
Operand local = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(inst, local, sources));
|
|
||||||
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand PrependExistingOperation(Operation operation)
|
|
||||||
{
|
|
||||||
Operand local = Local();
|
|
||||||
|
|
||||||
operation.Dest = local;
|
|
||||||
node.List.AddBefore(node, operation);
|
|
||||||
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand addrLow = operation.GetSource(0);
|
|
||||||
Operand addrHigh = operation.GetSource(1);
|
|
||||||
|
|
||||||
Operand sbBaseAddrLow = Const(0);
|
|
||||||
Operand sbSlot = Const(0);
|
|
||||||
|
|
||||||
Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
|
|
||||||
|
|
||||||
Operand BindingRangeCheck(int cbOffset, out Operand baseAddrLow)
|
|
||||||
{
|
|
||||||
baseAddrLow = Cbuf(0, cbOffset);
|
|
||||||
Operand baseAddrHigh = Cbuf(0, cbOffset + 1);
|
|
||||||
Operand size = Cbuf(0, cbOffset + 2);
|
|
||||||
|
|
||||||
Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow);
|
|
||||||
Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow);
|
|
||||||
|
|
||||||
Operand inRangeLow = PrependOperation(Instruction.CompareLessU32, offset, size);
|
|
||||||
|
|
||||||
Operand addrHighBorrowed = PrependOperation(Instruction.Add, addrHigh, borrow);
|
|
||||||
|
|
||||||
Operand inRangeHigh = PrependOperation(Instruction.CompareEqual, addrHighBorrowed, baseAddrHigh);
|
|
||||||
|
|
||||||
return PrependOperation(Instruction.BitwiseAnd, inRangeLow, inRangeHigh);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sbUseMask = config.AccessibleStorageBuffersMask;
|
|
||||||
|
|
||||||
while (sbUseMask != 0)
|
|
||||||
{
|
|
||||||
int slot = BitOperations.TrailingZeroCount(sbUseMask);
|
|
||||||
|
|
||||||
sbUseMask &= ~(1 << slot);
|
|
||||||
|
|
||||||
config.SetUsedStorageBuffer(slot, isWrite);
|
|
||||||
|
|
||||||
int cbOffset = GetStorageCbOffset(config.Stage, slot);
|
|
||||||
|
|
||||||
Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow);
|
|
||||||
|
|
||||||
sbBaseAddrLow = PrependOperation(Instruction.ConditionalSelect, inRange, baseAddrLow, sbBaseAddrLow);
|
|
||||||
sbSlot = PrependOperation(Instruction.ConditionalSelect, inRange, Const(slot), sbSlot);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.AccessibleStorageBuffersMask != 0)
|
|
||||||
{
|
|
||||||
Operand baseAddrTrunc = PrependOperation(Instruction.BitwiseAnd, sbBaseAddrLow, alignMask);
|
|
||||||
Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc);
|
|
||||||
|
|
||||||
Operand[] sources = new Operand[operation.SourcesCount];
|
|
||||||
|
|
||||||
sources[0] = sbSlot;
|
|
||||||
|
|
||||||
if (isStg16Or8)
|
|
||||||
{
|
|
||||||
sources[1] = byteOffset;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sources[1] = PrependOperation(Instruction.ShiftRightU32, byteOffset, Const(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 2; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
sources[index] = operation.GetSource(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAtomic)
|
|
||||||
{
|
|
||||||
Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage;
|
|
||||||
|
|
||||||
storageOp = new Operation(inst, operation.Dest, sources);
|
|
||||||
}
|
|
||||||
else if (operation.Inst == Instruction.LoadGlobal)
|
|
||||||
{
|
|
||||||
storageOp = new Operation(Instruction.LoadStorage, operation.Dest, sources);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Instruction storeInst = operation.Inst switch
|
|
||||||
{
|
|
||||||
Instruction.StoreGlobal16 => Instruction.StoreStorage16,
|
|
||||||
Instruction.StoreGlobal8 => Instruction.StoreStorage8,
|
|
||||||
_ => Instruction.StoreStorage
|
|
||||||
};
|
|
||||||
|
|
||||||
storageOp = new Operation(storeInst, null, sources);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (operation.Dest != null)
|
|
||||||
{
|
|
||||||
storageOp = new Operation(Instruction.Copy, operation.Dest, Const(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operation.Inst == Instruction.LoadGlobal)
|
|
||||||
{
|
|
||||||
int cbeUseMask = config.AccessibleConstantBuffersMask;
|
|
||||||
|
|
||||||
while (cbeUseMask != 0)
|
|
||||||
{
|
|
||||||
int slot = BitOperations.TrailingZeroCount(cbeUseMask);
|
|
||||||
int cbSlot = UbeFirstCbuf + slot;
|
|
||||||
|
|
||||||
cbeUseMask &= ~(1 << slot);
|
|
||||||
|
|
||||||
config.SetUsedConstantBuffer(cbSlot);
|
|
||||||
|
|
||||||
Operand previousResult = PrependExistingOperation(storageOp);
|
|
||||||
|
|
||||||
int cbOffset = GetConstantUbeOffset(slot);
|
|
||||||
|
|
||||||
Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow);
|
|
||||||
|
|
||||||
Operand baseAddrTruncConst = PrependOperation(Instruction.BitwiseAnd, baseAddrLow, alignMask);
|
|
||||||
Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst);
|
|
||||||
|
|
||||||
Operand cbIndex = PrependOperation(Instruction.ShiftRightU32, byteOffsetConst, Const(2));
|
|
||||||
|
|
||||||
Operand[] sourcesCb = new Operand[operation.SourcesCount];
|
|
||||||
|
|
||||||
sourcesCb[0] = Const(cbSlot);
|
|
||||||
sourcesCb[1] = cbIndex;
|
|
||||||
|
|
||||||
for (int index = 2; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
sourcesCb[index] = operation.GetSource(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand ldcResult = PrependOperation(Instruction.LoadConstant, sourcesCb);
|
|
||||||
|
|
||||||
storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
operation.SetSource(index, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkedListNode<INode> oldNode = node;
|
|
||||||
LinkedList<INode> oldNodeList = oldNode.List;
|
|
||||||
|
|
||||||
if (storageOp != null)
|
|
||||||
{
|
|
||||||
node = node.List.AddBefore(node, storageOp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
node = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
oldNodeList.Remove(oldNode);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LinkedListNode<INode> RewriteTextureSample(LinkedListNode<INode> node, ShaderConfig config)
|
|
||||||
{
|
|
||||||
TextureOperation texOp = (TextureOperation)node.Value;
|
|
||||||
|
|
||||||
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
|
|
||||||
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
|
|
||||||
|
|
||||||
bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.GpuAccessor.QueryHostSupportsNonConstantTextureOffset();
|
|
||||||
|
|
||||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
|
||||||
|
|
||||||
bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot);
|
|
||||||
|
|
||||||
if (!hasInvalidOffset && isCoordNormalized)
|
|
||||||
{
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
|
|
||||||
bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0;
|
|
||||||
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
|
||||||
bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0;
|
|
||||||
bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0;
|
|
||||||
|
|
||||||
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
|
||||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
|
||||||
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
|
|
||||||
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
|
|
||||||
|
|
||||||
int coordsCount = texOp.Type.GetDimensions();
|
|
||||||
|
|
||||||
int offsetsCount;
|
|
||||||
|
|
||||||
if (hasOffsets)
|
|
||||||
{
|
|
||||||
offsetsCount = coordsCount * 4;
|
|
||||||
}
|
|
||||||
else if (hasOffset)
|
|
||||||
{
|
|
||||||
offsetsCount = coordsCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
offsetsCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] offsets = new Operand[offsetsCount];
|
|
||||||
Operand[] sources = new Operand[texOp.SourcesCount - offsetsCount];
|
|
||||||
|
|
||||||
int copyCount = 0;
|
|
||||||
|
|
||||||
if (isBindless || isIndexed)
|
|
||||||
{
|
|
||||||
copyCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] lodSources = new Operand[copyCount + coordsCount];
|
|
||||||
|
|
||||||
for (int index = 0; index < lodSources.Length; index++)
|
|
||||||
{
|
|
||||||
lodSources[index] = texOp.GetSource(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
copyCount += coordsCount;
|
|
||||||
|
|
||||||
if (isArray)
|
|
||||||
{
|
|
||||||
copyCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isShadow)
|
|
||||||
{
|
|
||||||
copyCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasDerivatives)
|
|
||||||
{
|
|
||||||
copyCount += coordsCount * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMultisample)
|
|
||||||
{
|
|
||||||
copyCount++;
|
|
||||||
}
|
|
||||||
else if (hasLodLevel)
|
|
||||||
{
|
|
||||||
copyCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int srcIndex = 0;
|
|
||||||
int dstIndex = 0;
|
|
||||||
|
|
||||||
for (int index = 0; index < copyCount; index++)
|
|
||||||
{
|
|
||||||
sources[dstIndex++] = texOp.GetSource(srcIndex++);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool areAllOffsetsConstant = true;
|
|
||||||
|
|
||||||
for (int index = 0; index < offsetsCount; index++)
|
|
||||||
{
|
|
||||||
Operand offset = texOp.GetSource(srcIndex++);
|
|
||||||
|
|
||||||
areAllOffsetsConstant &= offset.Type == OperandType.Constant;
|
|
||||||
|
|
||||||
offsets[index] = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasInvalidOffset &= !areAllOffsetsConstant;
|
|
||||||
|
|
||||||
if (!hasInvalidOffset && isCoordNormalized)
|
|
||||||
{
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasLodBias)
|
|
||||||
{
|
|
||||||
sources[dstIndex++] = texOp.GetSource(srcIndex++);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isGather && !isShadow)
|
|
||||||
{
|
|
||||||
sources[dstIndex++] = texOp.GetSource(srcIndex++);
|
|
||||||
}
|
|
||||||
|
|
||||||
int coordsIndex = isBindless || isIndexed ? 1 : 0;
|
|
||||||
|
|
||||||
int componentIndex = texOp.Index;
|
|
||||||
|
|
||||||
Operand Float(Operand value)
|
|
||||||
{
|
|
||||||
Operand res = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value));
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emulate non-normalized coordinates by normalizing the coordinates on the shader.
|
|
||||||
// Without normalization, the coordinates are expected to the in the [0, W or H] range,
|
|
||||||
// and otherwise, it is expected to be in the [0, 1] range.
|
|
||||||
// We normalize by dividing the coords by the texture size.
|
|
||||||
if (!isCoordNormalized && !intCoords)
|
|
||||||
{
|
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
|
||||||
|
|
||||||
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
|
|
||||||
|
|
||||||
for (int index = 0; index < normCoordsCount; index++)
|
|
||||||
{
|
|
||||||
Operand coordSize = Local();
|
|
||||||
|
|
||||||
Operand[] texSizeSources;
|
|
||||||
|
|
||||||
if (isBindless || isIndexed)
|
|
||||||
{
|
|
||||||
texSizeSources = new Operand[] { sources[0], Const(0) };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
texSizeSources = new Operand[] { Const(0) };
|
|
||||||
}
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
|
||||||
Instruction.TextureSize,
|
|
||||||
texOp.Type,
|
|
||||||
texOp.Format,
|
|
||||||
texOp.Flags,
|
|
||||||
texOp.CbufSlot,
|
|
||||||
texOp.Handle,
|
|
||||||
index,
|
|
||||||
new[] { coordSize },
|
|
||||||
texSizeSources));
|
|
||||||
|
|
||||||
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
|
||||||
|
|
||||||
Operand coordNormalized = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, Float(coordSize)));
|
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordNormalized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] dests = new Operand[texOp.DestsCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < texOp.DestsCount; i++)
|
|
||||||
{
|
|
||||||
dests[i] = texOp.GetDest(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand bindlessHandle = isBindless || isIndexed ? sources[0] : null;
|
|
||||||
|
|
||||||
LinkedListNode<INode> oldNode = node;
|
|
||||||
|
|
||||||
// Technically, non-constant texture offsets are not allowed (according to the spec),
|
|
||||||
// however some GPUs does support that.
|
|
||||||
// For GPUs where it is not supported, we can replace the instruction with the following:
|
|
||||||
// For texture*Offset, we replace it by texture*, and add the offset to the P coords.
|
|
||||||
// The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords).
|
|
||||||
// For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly.
|
|
||||||
// For textureGatherOffset, we split the operation into up to 4 operations, one for each component
|
|
||||||
// that is accessed, where each textureGather operation has a different offset for each pixel.
|
|
||||||
if (hasInvalidOffset && isGather && !isShadow)
|
|
||||||
{
|
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
|
||||||
|
|
||||||
Operand[] newSources = new Operand[sources.Length];
|
|
||||||
|
|
||||||
sources.CopyTo(newSources, 0);
|
|
||||||
|
|
||||||
Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
|
|
||||||
|
|
||||||
int destIndex = 0;
|
|
||||||
|
|
||||||
for (int compIndex = 0; compIndex < 4; compIndex++)
|
|
||||||
{
|
|
||||||
if (((texOp.Index >> compIndex) & 1) == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
|
||||||
{
|
|
||||||
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
|
||||||
|
|
||||||
Operand offset = Local();
|
|
||||||
|
|
||||||
Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
|
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
|
||||||
|
|
||||||
Operand coordPlusOffset = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
|
|
||||||
|
|
||||||
newSources[coordsIndex + index] = coordPlusOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureOperation newTexOp = new TextureOperation(
|
|
||||||
Instruction.TextureSample,
|
|
||||||
texOp.Type,
|
|
||||||
texOp.Format,
|
|
||||||
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
|
|
||||||
texOp.CbufSlot,
|
|
||||||
texOp.Handle,
|
|
||||||
1,
|
|
||||||
new[] { dests[destIndex++] },
|
|
||||||
newSources);
|
|
||||||
|
|
||||||
node = node.List.AddBefore(node, newTexOp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (hasInvalidOffset)
|
|
||||||
{
|
|
||||||
if (intCoords)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
|
||||||
{
|
|
||||||
Operand source = sources[coordsIndex + index];
|
|
||||||
|
|
||||||
Operand coordPlusOffset = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
|
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordPlusOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
|
||||||
|
|
||||||
Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
|
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
|
||||||
{
|
|
||||||
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
|
||||||
|
|
||||||
Operand offset = Local();
|
|
||||||
|
|
||||||
Operand intOffset = offsets[index];
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
|
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
|
||||||
|
|
||||||
Operand coordPlusOffset = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
|
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordPlusOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureOperation newTexOp = new TextureOperation(
|
|
||||||
Instruction.TextureSample,
|
|
||||||
texOp.Type,
|
|
||||||
texOp.Format,
|
|
||||||
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
|
|
||||||
texOp.CbufSlot,
|
|
||||||
texOp.Handle,
|
|
||||||
componentIndex,
|
|
||||||
dests,
|
|
||||||
sources);
|
|
||||||
|
|
||||||
node = node.List.AddBefore(node, newTexOp);
|
|
||||||
}
|
|
||||||
|
|
||||||
node.List.Remove(oldNode);
|
|
||||||
|
|
||||||
for (int index = 0; index < texOp.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
texOp.SetSource(index, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand[] InsertTextureSize(
|
|
||||||
LinkedListNode<INode> node,
|
|
||||||
TextureOperation texOp,
|
|
||||||
Operand[] lodSources,
|
|
||||||
Operand bindlessHandle,
|
|
||||||
int coordsCount)
|
|
||||||
{
|
|
||||||
Operand Int(Operand value)
|
|
||||||
{
|
|
||||||
Operand res = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value));
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] texSizes = new Operand[coordsCount];
|
|
||||||
|
|
||||||
Operand lod = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
|
||||||
Instruction.Lod,
|
|
||||||
texOp.Type,
|
|
||||||
texOp.Format,
|
|
||||||
texOp.Flags,
|
|
||||||
texOp.CbufSlot,
|
|
||||||
texOp.Handle,
|
|
||||||
0,
|
|
||||||
new[] { lod },
|
|
||||||
lodSources));
|
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
|
||||||
{
|
|
||||||
texSizes[index] = Local();
|
|
||||||
|
|
||||||
Operand[] texSizeSources;
|
|
||||||
|
|
||||||
if (bindlessHandle != null)
|
|
||||||
{
|
|
||||||
texSizeSources = new Operand[] { bindlessHandle, Int(lod) };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
texSizeSources = new Operand[] { Int(lod) };
|
|
||||||
}
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
|
||||||
Instruction.TextureSize,
|
|
||||||
texOp.Type,
|
|
||||||
texOp.Format,
|
|
||||||
texOp.Flags,
|
|
||||||
texOp.CbufSlot,
|
|
||||||
texOp.Handle,
|
|
||||||
index,
|
|
||||||
new[] { texSizes[index] },
|
|
||||||
texSizeSources));
|
|
||||||
}
|
|
||||||
|
|
||||||
return texSizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LinkedListNode<INode> InsertSnormNormalization(LinkedListNode<INode> node, ShaderConfig config)
|
|
||||||
{
|
|
||||||
TextureOperation texOp = (TextureOperation)node.Value;
|
|
||||||
|
|
||||||
// We can't query the format of a bindless texture,
|
|
||||||
// because the handle is unknown, it can have any format.
|
|
||||||
if (texOp.Flags.HasFlag(TextureFlags.Bindless))
|
|
||||||
{
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureFormat format = config.GpuAccessor.QueryTextureFormat(texOp.Handle, texOp.CbufSlot);
|
|
||||||
|
|
||||||
int maxPositive = format switch
|
|
||||||
{
|
|
||||||
TextureFormat.R8Snorm => sbyte.MaxValue,
|
|
||||||
TextureFormat.R8G8Snorm => sbyte.MaxValue,
|
|
||||||
TextureFormat.R8G8B8A8Snorm => sbyte.MaxValue,
|
|
||||||
TextureFormat.R16Snorm => short.MaxValue,
|
|
||||||
TextureFormat.R16G16Snorm => short.MaxValue,
|
|
||||||
TextureFormat.R16G16B16A16Snorm => short.MaxValue,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// The value being 0 means that the format is not a SNORM format,
|
|
||||||
// so there's nothing to do here.
|
|
||||||
if (maxPositive == 0)
|
|
||||||
{
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do normalization. We assume SINT formats are being used
|
|
||||||
// as replacement for SNORM (which is not supported).
|
|
||||||
for (int i = 0; i < texOp.DestsCount; i++)
|
|
||||||
{
|
|
||||||
Operand dest = texOp.GetDest(i);
|
|
||||||
|
|
||||||
INode[] uses = dest.UseOps.ToArray();
|
|
||||||
|
|
||||||
Operation convOp = new Operation(Instruction.ConvertS32ToFP32, Local(), dest);
|
|
||||||
Operation normOp = new Operation(Instruction.FP32 | Instruction.Multiply, Local(), convOp.Dest, ConstF(1f / maxPositive));
|
|
||||||
|
|
||||||
node = node.List.AddAfter(node, convOp);
|
|
||||||
node = node.List.AddAfter(node, normOp);
|
|
||||||
|
|
||||||
foreach (INode useOp in uses)
|
|
||||||
{
|
|
||||||
if (useOp is not Operation op)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace all uses of the texture pixel value with the normalized value.
|
|
||||||
for (int index = 0; index < op.SourcesCount; index++)
|
|
||||||
{
|
|
||||||
if (op.GetSource(index) == dest)
|
|
||||||
{
|
|
||||||
op.SetSource(index, normOp.Dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ReplaceConstantBufferWithDrawParameters(Operation operation)
|
|
||||||
{
|
|
||||||
bool modified = false;
|
|
||||||
|
|
||||||
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
|
|
||||||
{
|
|
||||||
Operand src = operation.GetSource(srcIndex);
|
|
||||||
|
|
||||||
if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0)
|
|
||||||
{
|
|
||||||
switch (src.GetCbufOffset())
|
|
||||||
{
|
|
||||||
case Constants.NvnBaseVertexByteOffset / 4:
|
|
||||||
operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseVertex));
|
|
||||||
modified = true;
|
|
||||||
break;
|
|
||||||
case Constants.NvnBaseInstanceByteOffset / 4:
|
|
||||||
operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseInstance));
|
|
||||||
modified = true;
|
|
||||||
break;
|
|
||||||
case Constants.NvnDrawIndexByteOffset / 4:
|
|
||||||
operation.SetSource(srcIndex, Attribute(AttributeConsts.DrawIndex));
|
|
||||||
modified = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool HasConstantBufferDrawParameters(Operation operation)
|
|
||||||
{
|
|
||||||
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
|
|
||||||
{
|
|
||||||
Operand src = operation.GetSource(srcIndex);
|
|
||||||
|
|
||||||
if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0)
|
|
||||||
{
|
|
||||||
switch (src.GetCbufOffset())
|
|
||||||
{
|
|
||||||
case Constants.NvnBaseVertexByteOffset / 4:
|
|
||||||
case Constants.NvnBaseInstanceByteOffset / 4:
|
|
||||||
case Constants.NvnDrawIndexByteOffset / 4:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
|
||||||
{
|
|
||||||
internal class AutoFlushCounter
|
|
||||||
{
|
|
||||||
// How often to flush on framebuffer change.
|
|
||||||
private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000;
|
|
||||||
|
|
||||||
private const int MinDrawCountForFlush = 10;
|
|
||||||
private const int InitialQueryCountForFlush = 32;
|
|
||||||
|
|
||||||
private long _lastFlush;
|
|
||||||
private ulong _lastDrawCount;
|
|
||||||
private bool _hasPendingQuery;
|
|
||||||
private int _queryCount;
|
|
||||||
|
|
||||||
public void RegisterFlush(ulong drawCount)
|
|
||||||
{
|
|
||||||
_lastFlush = Stopwatch.GetTimestamp();
|
|
||||||
_lastDrawCount = drawCount;
|
|
||||||
|
|
||||||
_hasPendingQuery = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RegisterPendingQuery()
|
|
||||||
{
|
|
||||||
_hasPendingQuery = true;
|
|
||||||
|
|
||||||
// Interrupt render passes to flush queries, so that early results arrive sooner.
|
|
||||||
if (++_queryCount == InitialQueryCountForFlush)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShouldFlushQuery()
|
|
||||||
{
|
|
||||||
return _hasPendingQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShouldFlush(ulong drawCount)
|
|
||||||
{
|
|
||||||
_queryCount = 0;
|
|
||||||
|
|
||||||
if (_hasPendingQuery)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
long draws = (long)(drawCount - _lastDrawCount);
|
|
||||||
|
|
||||||
if (draws < MinDrawCountForFlush)
|
|
||||||
{
|
|
||||||
if (draws == 0)
|
|
||||||
{
|
|
||||||
_lastFlush = Stopwatch.GetTimestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long flushTimeout = FramebufferFlushTimer;
|
|
||||||
|
|
||||||
long now = Stopwatch.GetTimestamp();
|
|
||||||
|
|
||||||
return now > _lastFlush + flushTimeout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,58 +0,0 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using Silk.NET.Vulkan;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
|
||||||
{
|
|
||||||
class PipelineLayoutCache
|
|
||||||
{
|
|
||||||
private readonly PipelineLayoutCacheEntry[] _plce;
|
|
||||||
private readonly List<PipelineLayoutCacheEntry> _plceMinimal;
|
|
||||||
|
|
||||||
public PipelineLayoutCache()
|
|
||||||
{
|
|
||||||
_plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages];
|
|
||||||
_plceMinimal = new List<PipelineLayoutCacheEntry>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry Create(VulkanRenderer gd, Device device, ShaderSource[] shaders)
|
|
||||||
{
|
|
||||||
var plce = new PipelineLayoutCacheEntry(gd, device, shaders);
|
|
||||||
_plceMinimal.Add(plce);
|
|
||||||
return plce;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PipelineLayoutCacheEntry GetOrCreate(VulkanRenderer gd, Device device, uint stages, bool usePd)
|
|
||||||
{
|
|
||||||
if (_plce[stages] == null)
|
|
||||||
{
|
|
||||||
_plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _plce[stages];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual unsafe void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _plce.Length; i++)
|
|
||||||
{
|
|
||||||
_plce[i]?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var plce in _plceMinimal)
|
|
||||||
{
|
|
||||||
plce.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_plceMinimal.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,244 +0,0 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using Silk.NET.Vulkan;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
|
||||||
{
|
|
||||||
static class PipelineLayoutFactory
|
|
||||||
{
|
|
||||||
private const ShaderStageFlags SupportBufferStages =
|
|
||||||
ShaderStageFlags.VertexBit |
|
|
||||||
ShaderStageFlags.FragmentBit |
|
|
||||||
ShaderStageFlags.ComputeBit;
|
|
||||||
|
|
||||||
public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
|
|
||||||
{
|
|
||||||
int stagesCount = BitOperations.PopCount(stages);
|
|
||||||
|
|
||||||
int uCount = Constants.MaxUniformBuffersPerStage * stagesCount + 1;
|
|
||||||
int tCount = Constants.MaxTexturesPerStage * 2 * stagesCount;
|
|
||||||
int iCount = Constants.MaxImagesPerStage * 2 * stagesCount;
|
|
||||||
|
|
||||||
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
|
|
||||||
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount];
|
|
||||||
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
|
|
||||||
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
|
|
||||||
|
|
||||||
uLayoutBindings[0] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = 0,
|
|
||||||
DescriptorType = DescriptorType.UniformBuffer,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = SupportBufferStages
|
|
||||||
};
|
|
||||||
|
|
||||||
int iter = 0;
|
|
||||||
|
|
||||||
while (stages != 0)
|
|
||||||
{
|
|
||||||
int stage = BitOperations.TrailingZeroCount(stages);
|
|
||||||
stages &= ~(1u << stage);
|
|
||||||
|
|
||||||
var stageFlags = stage switch
|
|
||||||
{
|
|
||||||
1 => ShaderStageFlags.FragmentBit,
|
|
||||||
2 => ShaderStageFlags.GeometryBit,
|
|
||||||
3 => ShaderStageFlags.TessellationControlBit,
|
|
||||||
4 => ShaderStageFlags.TessellationEvaluationBit,
|
|
||||||
_ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
|
|
||||||
};
|
|
||||||
|
|
||||||
void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip)
|
|
||||||
{
|
|
||||||
int totalPerStage = maxPerStage * skip;
|
|
||||||
|
|
||||||
for (int i = 0; i < maxPerStage; i++)
|
|
||||||
{
|
|
||||||
bindings[start + iter * totalPerStage + i] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = (uint)(start + stage * totalPerStage + i),
|
|
||||||
DescriptorType = type,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = stageFlags
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0)
|
|
||||||
{
|
|
||||||
bindings[start + iter] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = (uint)(start + stage * maxPerStage),
|
|
||||||
DescriptorType = DescriptorType.StorageBuffer,
|
|
||||||
DescriptorCount = (uint)maxPerStage,
|
|
||||||
StageFlags = stageFlags
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Set(uLayoutBindings, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer, 1, 1);
|
|
||||||
SetStorage(sLayoutBindings, Constants.MaxStorageBuffersPerStage);
|
|
||||||
Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.CombinedImageSampler, 0, 2);
|
|
||||||
Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.UniformTexelBuffer, Constants.MaxTexturesPerStage, 2);
|
|
||||||
Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageImage, 0, 2);
|
|
||||||
Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageTexelBuffer, Constants.MaxImagesPerStage, 2);
|
|
||||||
|
|
||||||
iter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
|
|
||||||
|
|
||||||
var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = uLayoutBindings,
|
|
||||||
BindingCount = (uint)uCount,
|
|
||||||
Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0
|
|
||||||
};
|
|
||||||
|
|
||||||
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = sLayoutBindings,
|
|
||||||
BindingCount = (uint)stagesCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = tLayoutBindings,
|
|
||||||
BindingCount = (uint)tCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = iLayoutBindings,
|
|
||||||
BindingCount = (uint)iCount
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
|
|
||||||
|
|
||||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
|
||||||
{
|
|
||||||
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
|
||||||
PSetLayouts = pLayouts,
|
|
||||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return layouts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanRenderer gd, Device device, ShaderSource[] shaders, out PipelineLayout layout)
|
|
||||||
{
|
|
||||||
int stagesCount = shaders.Length;
|
|
||||||
|
|
||||||
int uCount = 0;
|
|
||||||
int sCount = 0;
|
|
||||||
int tCount = 0;
|
|
||||||
int iCount = 0;
|
|
||||||
|
|
||||||
foreach (var shader in shaders)
|
|
||||||
{
|
|
||||||
uCount += shader.Bindings.UniformBufferBindings.Count;
|
|
||||||
sCount += shader.Bindings.StorageBufferBindings.Count;
|
|
||||||
tCount += shader.Bindings.TextureBindings.Count;
|
|
||||||
iCount += shader.Bindings.ImageBindings.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
|
|
||||||
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount];
|
|
||||||
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
|
|
||||||
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
|
|
||||||
|
|
||||||
int uIndex = 0;
|
|
||||||
int sIndex = 0;
|
|
||||||
int tIndex = 0;
|
|
||||||
int iIndex = 0;
|
|
||||||
|
|
||||||
foreach (var shader in shaders)
|
|
||||||
{
|
|
||||||
var stageFlags = shader.Stage.Convert();
|
|
||||||
|
|
||||||
void Set(DescriptorSetLayoutBinding* bindings, DescriptorType type, ref int start, IEnumerable<int> bds)
|
|
||||||
{
|
|
||||||
foreach (var b in bds)
|
|
||||||
{
|
|
||||||
bindings[start++] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = (uint)b,
|
|
||||||
DescriptorType = type,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = stageFlags
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Support buffer textures and images here.
|
|
||||||
// This is only used for the helper shaders on the backend, and we don't use buffer textures on them
|
|
||||||
// so far, so it's not really necessary right now.
|
|
||||||
Set(uLayoutBindings, DescriptorType.UniformBuffer, ref uIndex, shader.Bindings.UniformBufferBindings);
|
|
||||||
Set(sLayoutBindings, DescriptorType.StorageBuffer, ref sIndex, shader.Bindings.StorageBufferBindings);
|
|
||||||
Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings);
|
|
||||||
Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings);
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
|
|
||||||
|
|
||||||
var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = uLayoutBindings,
|
|
||||||
BindingCount = (uint)uCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = sLayoutBindings,
|
|
||||||
BindingCount = (uint)sCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = tLayoutBindings,
|
|
||||||
BindingCount = (uint)tCount
|
|
||||||
};
|
|
||||||
|
|
||||||
var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
PBindings = iLayoutBindings,
|
|
||||||
BindingCount = (uint)iCount
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
|
|
||||||
gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
|
|
||||||
|
|
||||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
|
||||||
{
|
|
||||||
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
|
||||||
PSetLayouts = pLayouts,
|
|
||||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
|
||||||
};
|
|
||||||
|
|
||||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return layouts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,651 +0,0 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using Silk.NET.Vulkan;
|
|
||||||
using Silk.NET.Vulkan.Extensions.EXT;
|
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
|
||||||
{
|
|
||||||
public unsafe static class VulkanInitialization
|
|
||||||
{
|
|
||||||
private const uint InvalidIndex = uint.MaxValue;
|
|
||||||
private const string AppName = "Ryujinx.Graphics.Vulkan";
|
|
||||||
private const int QueuesCount = 2;
|
|
||||||
|
|
||||||
public static string[] DesirableExtensions { get; } = new string[]
|
|
||||||
{
|
|
||||||
ExtConditionalRendering.ExtensionName,
|
|
||||||
ExtExtendedDynamicState.ExtensionName,
|
|
||||||
ExtTransformFeedback.ExtensionName,
|
|
||||||
KhrDrawIndirectCount.ExtensionName,
|
|
||||||
KhrPushDescriptor.ExtensionName,
|
|
||||||
"VK_EXT_custom_border_color",
|
|
||||||
"VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV.
|
|
||||||
"VK_EXT_fragment_shader_interlock",
|
|
||||||
"VK_EXT_index_type_uint8",
|
|
||||||
"VK_EXT_robustness2",
|
|
||||||
"VK_KHR_shader_float16_int8",
|
|
||||||
"VK_EXT_shader_subgroup_ballot",
|
|
||||||
"VK_EXT_subgroup_size_control",
|
|
||||||
"VK_NV_geometry_shader_passthrough"
|
|
||||||
};
|
|
||||||
|
|
||||||
public static string[] RequiredExtensions { get; } = new string[]
|
|
||||||
{
|
|
||||||
KhrSwapchain.ExtensionName
|
|
||||||
};
|
|
||||||
|
|
||||||
private static string[] _excludedMessages = new string[]
|
|
||||||
{
|
|
||||||
// NOTE: Done on purpose right now.
|
|
||||||
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
|
|
||||||
// TODO: Figure out if fixable
|
|
||||||
"VUID-vkCmdDrawIndexed-None-04584",
|
|
||||||
// TODO: Might be worth looking into making this happy to possibly optimize copies.
|
|
||||||
"UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout",
|
|
||||||
// TODO: Fix this, it's causing too much noise right now.
|
|
||||||
"VUID-VkSubpassDependency-srcSubpass-00867"
|
|
||||||
};
|
|
||||||
|
|
||||||
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugUtils debugUtils, out DebugUtilsMessengerEXT debugUtilsMessenger)
|
|
||||||
{
|
|
||||||
var enabledLayers = new List<string>();
|
|
||||||
|
|
||||||
void AddAvailableLayer(string layerName)
|
|
||||||
{
|
|
||||||
uint layerPropertiesCount;
|
|
||||||
|
|
||||||
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError();
|
|
||||||
|
|
||||||
LayerProperties[] layerProperties = new LayerProperties[layerPropertiesCount];
|
|
||||||
|
|
||||||
fixed (LayerProperties* pLayerProperties = layerProperties)
|
|
||||||
{
|
|
||||||
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError();
|
|
||||||
|
|
||||||
for (int i = 0; i < layerPropertiesCount; i++)
|
|
||||||
{
|
|
||||||
string currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName);
|
|
||||||
|
|
||||||
if (currentLayerName == layerName)
|
|
||||||
{
|
|
||||||
enabledLayers.Add(layerName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logLevel != GraphicsDebugLevel.None)
|
|
||||||
{
|
|
||||||
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
|
|
||||||
}
|
|
||||||
|
|
||||||
var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
|
|
||||||
|
|
||||||
var appName = Marshal.StringToHGlobalAnsi(AppName);
|
|
||||||
|
|
||||||
var applicationInfo = new ApplicationInfo
|
|
||||||
{
|
|
||||||
PApplicationName = (byte*)appName,
|
|
||||||
ApplicationVersion = 1,
|
|
||||||
PEngineName = (byte*)appName,
|
|
||||||
EngineVersion = 1,
|
|
||||||
ApiVersion = Vk.Version12.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
|
||||||
IntPtr* ppEnabledLayers = stackalloc IntPtr[enabledLayers.Count];
|
|
||||||
|
|
||||||
for (int i = 0; i < enabledExtensions.Length; i++)
|
|
||||||
{
|
|
||||||
ppEnabledExtensions[i] = Marshal.StringToHGlobalAnsi(enabledExtensions[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < enabledLayers.Count; i++)
|
|
||||||
{
|
|
||||||
ppEnabledLayers[i] = Marshal.StringToHGlobalAnsi(enabledLayers[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var instanceCreateInfo = new InstanceCreateInfo
|
|
||||||
{
|
|
||||||
SType = StructureType.InstanceCreateInfo,
|
|
||||||
PApplicationInfo = &applicationInfo,
|
|
||||||
PpEnabledExtensionNames = (byte**)ppEnabledExtensions,
|
|
||||||
PpEnabledLayerNames = (byte**)ppEnabledLayers,
|
|
||||||
EnabledExtensionCount = (uint)enabledExtensions.Length,
|
|
||||||
EnabledLayerCount = (uint)enabledLayers.Count
|
|
||||||
};
|
|
||||||
|
|
||||||
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
|
|
||||||
|
|
||||||
Marshal.FreeHGlobal(appName);
|
|
||||||
|
|
||||||
for (int i = 0; i < enabledExtensions.Length; i++)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ppEnabledExtensions[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < enabledLayers.Count; i++)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ppEnabledLayers[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger);
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe static uint DebugMessenger(
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
|
|
||||||
DebugUtilsMessageTypeFlagsEXT messageTypes,
|
|
||||||
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
||||||
void* pUserData)
|
|
||||||
{
|
|
||||||
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
|
|
||||||
|
|
||||||
foreach (string excludedMessagePart in _excludedMessages)
|
|
||||||
{
|
|
||||||
if (msg.Contains(excludedMessagePart))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Gpu, msg);
|
|
||||||
//throw new Exception(msg);
|
|
||||||
}
|
|
||||||
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
|
|
||||||
{
|
|
||||||
Logger.Info?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
|
|
||||||
{
|
|
||||||
Logger.Debug?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId)
|
|
||||||
{
|
|
||||||
uint physicalDeviceCount;
|
|
||||||
|
|
||||||
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError();
|
|
||||||
|
|
||||||
PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount];
|
|
||||||
|
|
||||||
fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
|
|
||||||
{
|
|
||||||
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
// First we try to pick the the user preferred GPU.
|
|
||||||
for (int i = 0; i < physicalDevices.Length; i++)
|
|
||||||
{
|
|
||||||
if (IsPreferredAndSuitableDevice(api, physicalDevices[i], surface, preferredGpuId))
|
|
||||||
{
|
|
||||||
return physicalDevices[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we fail to do that, just use the first compatible GPU.
|
|
||||||
for (int i = 0; i < physicalDevices.Length; i++)
|
|
||||||
{
|
|
||||||
if (IsSuitableDevice(api, physicalDevices[i], surface))
|
|
||||||
{
|
|
||||||
return physicalDevices[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new VulkanException("Initialization failed, none of the available GPUs meets the minimum requirements.");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static DeviceInfo[] GetSuitablePhysicalDevices(Vk api)
|
|
||||||
{
|
|
||||||
var appName = Marshal.StringToHGlobalAnsi(AppName);
|
|
||||||
|
|
||||||
var applicationInfo = new ApplicationInfo
|
|
||||||
{
|
|
||||||
PApplicationName = (byte*)appName,
|
|
||||||
ApplicationVersion = 1,
|
|
||||||
PEngineName = (byte*)appName,
|
|
||||||
EngineVersion = 1,
|
|
||||||
ApiVersion = Vk.Version12.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
var instanceCreateInfo = new InstanceCreateInfo
|
|
||||||
{
|
|
||||||
SType = StructureType.InstanceCreateInfo,
|
|
||||||
PApplicationInfo = &applicationInfo,
|
|
||||||
PpEnabledExtensionNames = null,
|
|
||||||
PpEnabledLayerNames = null,
|
|
||||||
EnabledExtensionCount = 0,
|
|
||||||
EnabledLayerCount = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
|
|
||||||
|
|
||||||
Marshal.FreeHGlobal(appName);
|
|
||||||
|
|
||||||
uint physicalDeviceCount;
|
|
||||||
|
|
||||||
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError();
|
|
||||||
|
|
||||||
PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount];
|
|
||||||
|
|
||||||
fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
|
|
||||||
{
|
|
||||||
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceInfo[] devices = new DeviceInfo[physicalDevices.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < physicalDevices.Length; i++)
|
|
||||||
{
|
|
||||||
var physicalDevice = physicalDevices[i];
|
|
||||||
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
|
|
||||||
|
|
||||||
devices[i] = new DeviceInfo(
|
|
||||||
StringFromIdPair(properties.VendorID, properties.DeviceID),
|
|
||||||
VendorUtils.GetNameFromId(properties.VendorID),
|
|
||||||
Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName),
|
|
||||||
properties.DeviceType == PhysicalDeviceType.DiscreteGpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
api.DestroyInstance(instance, null);
|
|
||||||
|
|
||||||
return devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string StringFromIdPair(uint vendorId, uint deviceId)
|
|
||||||
{
|
|
||||||
return $"0x{vendorId:X}_0x{deviceId:X}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsPreferredAndSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
|
|
||||||
{
|
|
||||||
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
|
|
||||||
|
|
||||||
if (StringFromIdPair(properties.VendorID, properties.DeviceID) != preferredGpuId)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IsSuitableDevice(api, physicalDevice, surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface)
|
|
||||||
{
|
|
||||||
int extensionMatches = 0;
|
|
||||||
uint propertiesCount;
|
|
||||||
|
|
||||||
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
|
|
||||||
|
|
||||||
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
|
|
||||||
|
|
||||||
fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
|
|
||||||
{
|
|
||||||
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError();
|
|
||||||
|
|
||||||
for (int i = 0; i < propertiesCount; i++)
|
|
||||||
{
|
|
||||||
string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName);
|
|
||||||
|
|
||||||
if (RequiredExtensions.Contains(extensionName))
|
|
||||||
{
|
|
||||||
extensionMatches++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return extensionMatches == RequiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
|
|
||||||
{
|
|
||||||
const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit;
|
|
||||||
|
|
||||||
var khrSurface = new KhrSurface(api.Context);
|
|
||||||
|
|
||||||
uint propertiesCount;
|
|
||||||
|
|
||||||
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null);
|
|
||||||
|
|
||||||
QueueFamilyProperties[] properties = new QueueFamilyProperties[propertiesCount];
|
|
||||||
|
|
||||||
fixed (QueueFamilyProperties* pProperties = properties)
|
|
||||||
{
|
|
||||||
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint index = 0; index < propertiesCount; index++)
|
|
||||||
{
|
|
||||||
var queueFlags = properties[index].QueueFlags;
|
|
||||||
|
|
||||||
khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported).ThrowOnError();
|
|
||||||
|
|
||||||
if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported)
|
|
||||||
{
|
|
||||||
queueCount = properties[index].QueueCount;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queueCount = 0;
|
|
||||||
return InvalidIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Device CreateDevice(Vk api, PhysicalDevice physicalDevice, uint queueFamilyIndex, string[] supportedExtensions, uint queueCount)
|
|
||||||
{
|
|
||||||
if (queueCount > QueuesCount)
|
|
||||||
{
|
|
||||||
queueCount = QueuesCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
float* queuePriorities = stackalloc float[(int)queueCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < queueCount; i++)
|
|
||||||
{
|
|
||||||
queuePriorities[i] = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
var queueCreateInfo = new DeviceQueueCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DeviceQueueCreateInfo,
|
|
||||||
QueueFamilyIndex = queueFamilyIndex,
|
|
||||||
QueueCount = queueCount,
|
|
||||||
PQueuePriorities = queuePriorities
|
|
||||||
};
|
|
||||||
|
|
||||||
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
|
|
||||||
bool useRobustBufferAccess = VendorUtils.FromId(properties.VendorID) == Vendor.Nvidia;
|
|
||||||
|
|
||||||
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceFeatures2
|
|
||||||
};
|
|
||||||
|
|
||||||
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColorSupported = new PhysicalDeviceCustomBorderColorFeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt
|
|
||||||
};
|
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
|
|
||||||
{
|
|
||||||
features2.PNext = &featuresCustomBorderColorSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
|
|
||||||
};
|
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
|
||||||
{
|
|
||||||
supportedFeaturesRobustness2.PNext = features2.PNext;
|
|
||||||
|
|
||||||
features2.PNext = &supportedFeaturesRobustness2;
|
|
||||||
}
|
|
||||||
|
|
||||||
api.GetPhysicalDeviceFeatures2(physicalDevice, &features2);
|
|
||||||
|
|
||||||
var supportedFeatures = features2.Features;
|
|
||||||
|
|
||||||
var features = new PhysicalDeviceFeatures()
|
|
||||||
{
|
|
||||||
DepthBiasClamp = true,
|
|
||||||
DepthClamp = true,
|
|
||||||
DualSrcBlend = true,
|
|
||||||
FragmentStoresAndAtomics = true,
|
|
||||||
GeometryShader = supportedFeatures.GeometryShader,
|
|
||||||
ImageCubeArray = true,
|
|
||||||
IndependentBlend = true,
|
|
||||||
LogicOp = supportedFeatures.LogicOp,
|
|
||||||
MultiViewport = true,
|
|
||||||
OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise,
|
|
||||||
PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery,
|
|
||||||
SamplerAnisotropy = true,
|
|
||||||
ShaderClipDistance = true,
|
|
||||||
ShaderFloat64 = supportedFeatures.ShaderFloat64,
|
|
||||||
ShaderImageGatherExtended = true,
|
|
||||||
ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample,
|
|
||||||
// ShaderStorageImageReadWithoutFormat = true,
|
|
||||||
// ShaderStorageImageWriteWithoutFormat = true,
|
|
||||||
TessellationShader = true,
|
|
||||||
VertexPipelineStoresAndAtomics = true,
|
|
||||||
RobustBufferAccess = useRobustBufferAccess
|
|
||||||
};
|
|
||||||
|
|
||||||
void* pExtendedFeatures = null;
|
|
||||||
|
|
||||||
var featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
TransformFeedback = true
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresTransformFeedback;
|
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
|
||||||
{
|
|
||||||
var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
NullDescriptor = supportedFeaturesRobustness2.NullDescriptor
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresRobustness2;
|
|
||||||
}
|
|
||||||
|
|
||||||
var featuresExtendedDynamicState = new PhysicalDeviceExtendedDynamicStateFeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
ExtendedDynamicState = supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName)
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresExtendedDynamicState;
|
|
||||||
|
|
||||||
var featuresVk11 = new PhysicalDeviceVulkan11Features()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceVulkan11Features,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
ShaderDrawParameters = true
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresVk11;
|
|
||||||
|
|
||||||
var featuresVk12 = new PhysicalDeviceVulkan12Features()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceVulkan12Features,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
DescriptorIndexing = supportedExtensions.Contains("VK_EXT_descriptor_indexing"),
|
|
||||||
DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
|
|
||||||
UniformBufferStandardLayout = supportedExtensions.Contains("VK_KHR_uniform_buffer_standard_layout")
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresVk12;
|
|
||||||
|
|
||||||
PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8;
|
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_index_type_uint8"))
|
|
||||||
{
|
|
||||||
featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceIndexTypeUint8FeaturesExt,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
IndexTypeUint8 = true
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresIndexU8;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock;
|
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"))
|
|
||||||
{
|
|
||||||
featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceFragmentShaderInterlockFeaturesExt,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
FragmentShaderPixelInterlock = true
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresFragmentShaderInterlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
|
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_subgroup_size_control"))
|
|
||||||
{
|
|
||||||
featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceSubgroupSizeControlFeaturesExt,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
SubgroupSizeControl = true
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresSubgroupSizeControl;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
|
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_custom_border_color") &&
|
|
||||||
featuresCustomBorderColorSupported.CustomBorderColors &&
|
|
||||||
featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat)
|
|
||||||
{
|
|
||||||
featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
CustomBorderColors = true,
|
|
||||||
CustomBorderColorWithoutFormat = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
pExtendedFeatures = &featuresCustomBorderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
var enabledExtensions = RequiredExtensions.Union(DesirableExtensions.Intersect(supportedExtensions)).ToArray();
|
|
||||||
|
|
||||||
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < enabledExtensions.Length; i++)
|
|
||||||
{
|
|
||||||
ppEnabledExtensions[i] = Marshal.StringToHGlobalAnsi(enabledExtensions[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var deviceCreateInfo = new DeviceCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.DeviceCreateInfo,
|
|
||||||
PNext = pExtendedFeatures,
|
|
||||||
QueueCreateInfoCount = 1,
|
|
||||||
PQueueCreateInfos = &queueCreateInfo,
|
|
||||||
PpEnabledExtensionNames = (byte**)ppEnabledExtensions,
|
|
||||||
EnabledExtensionCount = (uint)enabledExtensions.Length,
|
|
||||||
PEnabledFeatures = &features
|
|
||||||
};
|
|
||||||
|
|
||||||
api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError();
|
|
||||||
|
|
||||||
for (int i = 0; i < enabledExtensions.Length; i++)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ppEnabledExtensions[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string[] GetSupportedExtensions(Vk api, PhysicalDevice physicalDevice)
|
|
||||||
{
|
|
||||||
uint propertiesCount;
|
|
||||||
|
|
||||||
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
|
|
||||||
|
|
||||||
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
|
|
||||||
|
|
||||||
fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
|
|
||||||
{
|
|
||||||
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static CommandBufferPool CreateCommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex)
|
|
||||||
{
|
|
||||||
return new CommandBufferPool(api, device, queue, queueLock, queueFamilyIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal unsafe static void CreateDebugMessenger(
|
|
||||||
Vk api,
|
|
||||||
GraphicsDebugLevel logLevel,
|
|
||||||
Instance instance,
|
|
||||||
out ExtDebugUtils debugUtils,
|
|
||||||
out DebugUtilsMessengerEXT debugUtilsMessenger)
|
|
||||||
{
|
|
||||||
debugUtils = default;
|
|
||||||
|
|
||||||
if (logLevel != GraphicsDebugLevel.None)
|
|
||||||
{
|
|
||||||
if (!api.TryGetInstanceExtension(instance, out debugUtils))
|
|
||||||
{
|
|
||||||
debugUtilsMessenger = default;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var filterLogType = logLevel switch
|
|
||||||
{
|
|
||||||
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
|
|
||||||
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
|
||||||
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
|
|
||||||
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
|
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
|
||||||
};
|
|
||||||
|
|
||||||
var filterLogSeverity = logLevel switch
|
|
||||||
{
|
|
||||||
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
|
||||||
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
|
||||||
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
|
|
||||||
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
|
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
|
||||||
};
|
|
||||||
|
|
||||||
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
|
|
||||||
{
|
|
||||||
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
|
||||||
MessageType = filterLogType,
|
|
||||||
MessageSeverity = filterLogSeverity,
|
|
||||||
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugMessenger)
|
|
||||||
};
|
|
||||||
|
|
||||||
debugUtils.CreateDebugUtilsMessenger(instance, in debugUtilsMessengerCreateInfo, null, out debugUtilsMessenger).ThrowOnError();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
debugUtilsMessenger = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,908 +0,0 @@
|
|||||||
using LibHac;
|
|
||||||
using LibHac.Account;
|
|
||||||
using LibHac.Common;
|
|
||||||
using LibHac.Fs;
|
|
||||||
using LibHac.Fs.Fsa;
|
|
||||||
using LibHac.Fs.Shim;
|
|
||||||
using LibHac.FsSystem;
|
|
||||||
using LibHac.Loader;
|
|
||||||
using LibHac.Ncm;
|
|
||||||
using LibHac.Ns;
|
|
||||||
using LibHac.Tools.Fs;
|
|
||||||
using LibHac.Tools.FsSystem;
|
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
|
||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Cpu;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
|
||||||
using Ryujinx.Memory;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using static Ryujinx.HLE.HOS.ModLoader;
|
|
||||||
using ApplicationId = LibHac.Ncm.ApplicationId;
|
|
||||||
using Path = System.IO.Path;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
|
||||||
{
|
|
||||||
using JsonHelper = Common.Utilities.JsonHelper;
|
|
||||||
|
|
||||||
public class ApplicationLoader
|
|
||||||
{
|
|
||||||
// Binaries from exefs are loaded into mem in this order. Do not change.
|
|
||||||
internal static readonly string[] ExeFsPrefixes =
|
|
||||||
{
|
|
||||||
"rtld",
|
|
||||||
"main",
|
|
||||||
"subsdk0",
|
|
||||||
"subsdk1",
|
|
||||||
"subsdk2",
|
|
||||||
"subsdk3",
|
|
||||||
"subsdk4",
|
|
||||||
"subsdk5",
|
|
||||||
"subsdk6",
|
|
||||||
"subsdk7",
|
|
||||||
"subsdk8",
|
|
||||||
"subsdk9",
|
|
||||||
"sdk"
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly Switch _device;
|
|
||||||
private string _titleName;
|
|
||||||
private string _displayVersion;
|
|
||||||
private BlitStruct<ApplicationControlProperty> _controlData;
|
|
||||||
|
|
||||||
public BlitStruct<ApplicationControlProperty> ControlData => _controlData;
|
|
||||||
public string TitleName => _titleName;
|
|
||||||
public string DisplayVersion => _displayVersion;
|
|
||||||
|
|
||||||
public ulong TitleId { get; private set; }
|
|
||||||
public bool TitleIs64Bit { get; private set; }
|
|
||||||
|
|
||||||
public string TitleIdText => TitleId.ToString("x16");
|
|
||||||
|
|
||||||
public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
|
|
||||||
|
|
||||||
public ApplicationLoader(Switch device)
|
|
||||||
{
|
|
||||||
_device = device;
|
|
||||||
_controlData = new BlitStruct<ApplicationControlProperty>(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadCart(string exeFsDir, string romFsFile = null)
|
|
||||||
{
|
|
||||||
LocalFileSystem codeFs = new LocalFileSystem(exeFsDir);
|
|
||||||
|
|
||||||
MetaLoader metaData = ReadNpdm(codeFs);
|
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
|
|
||||||
new[] { TitleId },
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
|
|
||||||
|
|
||||||
if (TitleId != 0)
|
|
||||||
{
|
|
||||||
EnsureSaveData(new ApplicationId(TitleId));
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong pid = LoadExeFs(codeFs, string.Empty, metaData);
|
|
||||||
|
|
||||||
if (romFsFile != null)
|
|
||||||
{
|
|
||||||
_device.Configuration.VirtualFileSystem.LoadRomFs(pid, romFsFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex)
|
|
||||||
{
|
|
||||||
Nca mainNca = null;
|
|
||||||
Nca patchNca = null;
|
|
||||||
Nca controlNca = null;
|
|
||||||
|
|
||||||
fileSystem.ImportTickets(pfs);
|
|
||||||
|
|
||||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
|
||||||
{
|
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
|
|
||||||
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
|
||||||
|
|
||||||
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
|
|
||||||
|
|
||||||
if (ncaProgramIndex != programIndex)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nca.Header.ContentType == NcaContentType.Program)
|
|
||||||
{
|
|
||||||
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
|
|
||||||
|
|
||||||
if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
|
|
||||||
{
|
|
||||||
patchNca = nca;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mainNca = nca;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (nca.Header.ContentType == NcaContentType.Control)
|
|
||||||
{
|
|
||||||
controlNca = nca;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (mainNca, patchNca, controlNca);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex)
|
|
||||||
{
|
|
||||||
Nca patchNca = null;
|
|
||||||
Nca controlNca = null;
|
|
||||||
|
|
||||||
fileSystem.ImportTickets(pfs);
|
|
||||||
|
|
||||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
|
||||||
{
|
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
|
|
||||||
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
|
||||||
|
|
||||||
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
|
|
||||||
|
|
||||||
if (ncaProgramIndex != programIndex)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nca.Header.ContentType == NcaContentType.Program)
|
|
||||||
{
|
|
||||||
patchNca = nca;
|
|
||||||
}
|
|
||||||
else if (nca.Header.ContentType == NcaContentType.Control)
|
|
||||||
{
|
|
||||||
controlNca = nca;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (patchNca, controlNca);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath)
|
|
||||||
{
|
|
||||||
updatePath = null;
|
|
||||||
|
|
||||||
if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase))
|
|
||||||
{
|
|
||||||
// Clear the program index part.
|
|
||||||
titleIdBase &= 0xFFFFFFFFFFFFFFF0;
|
|
||||||
|
|
||||||
// Load update informations if existing.
|
|
||||||
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
|
|
||||||
|
|
||||||
if (File.Exists(titleUpdateMetadataPath))
|
|
||||||
{
|
|
||||||
updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
|
|
||||||
|
|
||||||
if (File.Exists(updatePath))
|
|
||||||
{
|
|
||||||
FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
|
|
||||||
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
|
|
||||||
|
|
||||||
return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadXci(string xciFile)
|
|
||||||
{
|
|
||||||
FileStream file = new FileStream(xciFile, FileMode.Open, FileAccess.Read);
|
|
||||||
Xci xci = new Xci(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage());
|
|
||||||
|
|
||||||
if (!xci.HasPartition(XciPartitionType.Secure))
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find XCI secure partition");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PartitionFileSystem securePartition = xci.OpenPartition(XciPartitionType.Secure);
|
|
||||||
|
|
||||||
Nca mainNca;
|
|
||||||
Nca patchNca;
|
|
||||||
Nca controlNca;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
(mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, securePartition, _device.Configuration.UserChannelPersistence.Index);
|
|
||||||
|
|
||||||
RegisterProgramMapInfo(securePartition).ThrowIfFailure();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, $"Unable to load XCI: {e.Message}");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainNca == null)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find Main NCA");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_device.Configuration.ContentManager.LoadEntries(_device);
|
|
||||||
_device.Configuration.ContentManager.ClearAocData();
|
|
||||||
_device.Configuration.ContentManager.AddAocData(securePartition, xciFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel);
|
|
||||||
|
|
||||||
LoadNca(mainNca, patchNca, controlNca);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadNsp(string nspFile)
|
|
||||||
{
|
|
||||||
FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read);
|
|
||||||
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
|
|
||||||
|
|
||||||
Nca mainNca;
|
|
||||||
Nca patchNca;
|
|
||||||
Nca controlNca;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
(mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, nsp, _device.Configuration.UserChannelPersistence.Index);
|
|
||||||
|
|
||||||
RegisterProgramMapInfo(nsp).ThrowIfFailure();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, $"Unable to load NSP: {e.Message}");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainNca != null)
|
|
||||||
{
|
|
||||||
_device.Configuration.ContentManager.ClearAocData();
|
|
||||||
_device.Configuration.ContentManager.AddAocData(nsp, nspFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel);
|
|
||||||
|
|
||||||
LoadNca(mainNca, patchNca, controlNca);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is not a normal NSP, it's actually a ExeFS as a NSP
|
|
||||||
LoadExeFs(nsp, null, isHomebrew: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadNca(string ncaFile)
|
|
||||||
{
|
|
||||||
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
|
||||||
Nca nca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
|
||||||
|
|
||||||
LoadNca(nca, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadServiceNca(string ncaFile)
|
|
||||||
{
|
|
||||||
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
|
||||||
Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
|
||||||
|
|
||||||
if (mainNca.Header.ContentType != NcaContentType.Program)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IFileSystem codeFs = null;
|
|
||||||
|
|
||||||
if (mainNca.CanOpenSection(NcaSectionType.Code))
|
|
||||||
{
|
|
||||||
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codeFs == null)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var npdmFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
Result result = codeFs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read);
|
|
||||||
|
|
||||||
MetaLoader metaData;
|
|
||||||
|
|
||||||
npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure();
|
|
||||||
|
|
||||||
var npdmBuffer = new byte[fileSize];
|
|
||||||
npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure();
|
|
||||||
|
|
||||||
metaData = new MetaLoader();
|
|
||||||
metaData.Load(npdmBuffer).ThrowIfFailure();
|
|
||||||
|
|
||||||
NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < nsos.Length; i++)
|
|
||||||
{
|
|
||||||
string name = ExeFsPrefixes[i];
|
|
||||||
|
|
||||||
if (!codeFs.FileExists($"/{name}"))
|
|
||||||
{
|
|
||||||
continue; // File doesn't exist, skip.
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
|
|
||||||
|
|
||||||
using var nsoFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
codeFs.OpenFile(ref nsoFile.Ref(), $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
|
|
||||||
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect the nsos, ignoring ones that aren't used.
|
|
||||||
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
|
|
||||||
|
|
||||||
string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString;
|
|
||||||
bool usePtc = _device.System.EnablePtc;
|
|
||||||
|
|
||||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
|
||||||
ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false);
|
|
||||||
ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
|
|
||||||
|
|
||||||
string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16");
|
|
||||||
bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
|
|
||||||
|
|
||||||
string programName = Encoding.ASCII.GetString(npdm.Meta.Value.ProgramName).TrimEnd('\0');
|
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
|
|
||||||
{
|
|
||||||
if (mainNca.Header.ContentType != NcaContentType.Program)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IStorage dataStorage = null;
|
|
||||||
IFileSystem codeFs = null;
|
|
||||||
|
|
||||||
(Nca updatePatchNca, Nca updateControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), _device.Configuration.UserChannelPersistence.Index, out _);
|
|
||||||
|
|
||||||
if (updatePatchNca != null)
|
|
||||||
{
|
|
||||||
patchNca = updatePatchNca;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateControlNca != null)
|
|
||||||
{
|
|
||||||
controlNca = updateControlNca;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load program 0 control NCA as we are going to need it for display version.
|
|
||||||
(_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
|
|
||||||
|
|
||||||
// Load Aoc
|
|
||||||
string titleAocMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
|
|
||||||
|
|
||||||
if (File.Exists(titleAocMetadataPath))
|
|
||||||
{
|
|
||||||
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(titleAocMetadataPath);
|
|
||||||
|
|
||||||
foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
|
|
||||||
{
|
|
||||||
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
|
|
||||||
{
|
|
||||||
if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled)
|
|
||||||
{
|
|
||||||
_device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Cannot find AddOnContent file {downloadableContentContainer.ContainerPath}. It may have been moved or renamed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (patchNca == null)
|
|
||||||
{
|
|
||||||
if (mainNca.CanOpenSection(NcaSectionType.Data))
|
|
||||||
{
|
|
||||||
dataStorage = mainNca.OpenStorage(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainNca.CanOpenSection(NcaSectionType.Code))
|
|
||||||
{
|
|
||||||
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (patchNca.CanOpenSection(NcaSectionType.Data))
|
|
||||||
{
|
|
||||||
dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (patchNca.CanOpenSection(NcaSectionType.Code))
|
|
||||||
{
|
|
||||||
codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codeFs == null)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetaLoader metaData = ReadNpdm(codeFs);
|
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
|
|
||||||
_device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId),
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
|
|
||||||
|
|
||||||
string displayVersion = string.Empty;
|
|
||||||
|
|
||||||
if (controlNca != null)
|
|
||||||
{
|
|
||||||
ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ControlData.ByteSpan.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Nintendo doesn't guarantee that the display version will be updated on sub programs when updating a multi program application.
|
|
||||||
// BODY: As such, to avoid PTC cache confusion, we only trust the the program 0 display version when launching a sub program.
|
|
||||||
if (updateProgram0ControlNca != null && _device.Configuration.UserChannelPersistence.Index != 0)
|
|
||||||
{
|
|
||||||
string dummyTitleName = "";
|
|
||||||
BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1);
|
|
||||||
|
|
||||||
ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
_displayVersion = displayVersion;
|
|
||||||
|
|
||||||
ulong pid = LoadExeFs(codeFs, displayVersion, metaData);
|
|
||||||
|
|
||||||
if (dataStorage == null)
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IStorage newStorage = _device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage);
|
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.SetRomFs(pid, newStorage.AsStream(FileAccess.Read));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't create save data for system programs.
|
|
||||||
if (TitleId != 0 && (TitleId < SystemProgramId.Start.Value || TitleId > SystemAppletId.End.Value))
|
|
||||||
{
|
|
||||||
// Multi-program applications can technically use any program ID for the main program, but in practice they always use 0 in the low nibble.
|
|
||||||
// We'll know if this changes in the future because stuff will get errors when trying to mount the correct save.
|
|
||||||
EnsureSaveData(new ApplicationId(TitleId & ~0xFul));
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets TitleId, so be sure to call before using it
|
|
||||||
private MetaLoader ReadNpdm(IFileSystem fs)
|
|
||||||
{
|
|
||||||
using var npdmFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
Result result = fs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read);
|
|
||||||
|
|
||||||
MetaLoader metaData;
|
|
||||||
|
|
||||||
if (ResultFs.PathNotFound.Includes(result))
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Loader, "NPDM file not found, using default values!");
|
|
||||||
|
|
||||||
metaData = GetDefaultNpdm();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure();
|
|
||||||
|
|
||||||
var npdmBuffer = new byte[fileSize];
|
|
||||||
npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure();
|
|
||||||
|
|
||||||
metaData = new MetaLoader();
|
|
||||||
metaData.Load(npdmBuffer).ThrowIfFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
metaData.GetNpdm(out var npdm).ThrowIfFailure();
|
|
||||||
|
|
||||||
TitleId = npdm.Aci.Value.ProgramId.Value;
|
|
||||||
TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
|
|
||||||
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
|
|
||||||
|
|
||||||
return metaData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReadControlData(Switch device, Nca controlNca, ref BlitStruct<ApplicationControlProperty> controlData, ref string titleName, ref string displayVersion)
|
|
||||||
{
|
|
||||||
using var controlFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
IFileSystem controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel);
|
|
||||||
Result result = controlFs.OpenFile(ref controlFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read);
|
|
||||||
|
|
||||||
if (result.IsSuccess())
|
|
||||||
{
|
|
||||||
result = controlFile.Get.Read(out long bytesRead, 0, controlData.ByteSpan, ReadOption.None);
|
|
||||||
|
|
||||||
if (result.IsSuccess() && bytesRead == controlData.ByteSpan.Length)
|
|
||||||
{
|
|
||||||
titleName = controlData.Value.Title[(int)device.System.State.DesiredTitleLanguage].NameString.ToString();
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(titleName))
|
|
||||||
{
|
|
||||||
titleName = controlData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
displayVersion = controlData.Value.DisplayVersionString.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
controlData.ByteSpan.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false)
|
|
||||||
{
|
|
||||||
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
|
|
||||||
{
|
|
||||||
metaData = null; // TODO: Check if we should retain old npdm.
|
|
||||||
}
|
|
||||||
|
|
||||||
metaData ??= ReadNpdm(codeFs);
|
|
||||||
|
|
||||||
NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < nsos.Length; i++)
|
|
||||||
{
|
|
||||||
string name = ExeFsPrefixes[i];
|
|
||||||
|
|
||||||
if (!codeFs.FileExists($"/{name}"))
|
|
||||||
{
|
|
||||||
continue; // File doesn't exist, skip.
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
|
|
||||||
|
|
||||||
using var nsoFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
codeFs.OpenFile(ref nsoFile.Ref(), $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
|
|
||||||
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExeFs file replacements.
|
|
||||||
ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
|
|
||||||
|
|
||||||
// Collect the nsos, ignoring ones that aren't used.
|
|
||||||
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
|
|
||||||
|
|
||||||
// Take the npdm from mods if present.
|
|
||||||
if (modLoadResult.Npdm != null)
|
|
||||||
{
|
|
||||||
metaData = modLoadResult.Npdm;
|
|
||||||
}
|
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(TitleId, programs);
|
|
||||||
|
|
||||||
_device.Configuration.ContentManager.LoadEntries(_device);
|
|
||||||
|
|
||||||
bool usePtc = _device.System.EnablePtc;
|
|
||||||
|
|
||||||
// Don't use PPTC if ExeFs files have been replaced.
|
|
||||||
usePtc &= !modLoadResult.Modified;
|
|
||||||
|
|
||||||
if (_device.System.EnablePtc && !usePtc)
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PPTC disabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;
|
|
||||||
_device.Gpu.HostInitalized.Set();
|
|
||||||
|
|
||||||
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
|
|
||||||
|
|
||||||
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
|
||||||
{
|
|
||||||
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We allow it for nx-hbloader because it can be used to launch homebrew.
|
|
||||||
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew;
|
|
||||||
|
|
||||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
|
||||||
ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit);
|
|
||||||
ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
|
|
||||||
|
|
||||||
DiskCacheLoadState = result.DiskCacheLoadState;
|
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
|
|
||||||
|
|
||||||
return result.ProcessId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadProgram(string filePath)
|
|
||||||
{
|
|
||||||
MetaLoader metaData = GetDefaultNpdm();
|
|
||||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
|
||||||
ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true);
|
|
||||||
|
|
||||||
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
|
|
||||||
|
|
||||||
IExecutable executable;
|
|
||||||
Stream romfsStream = null;
|
|
||||||
|
|
||||||
if (isNro)
|
|
||||||
{
|
|
||||||
FileStream input = new FileStream(filePath, FileMode.Open);
|
|
||||||
NroExecutable obj = new NroExecutable(input.AsStorage());
|
|
||||||
|
|
||||||
executable = obj;
|
|
||||||
|
|
||||||
// Homebrew NRO can actually have some data after the actual NRO.
|
|
||||||
if (input.Length > obj.FileSize)
|
|
||||||
{
|
|
||||||
input.Position = obj.FileSize;
|
|
||||||
|
|
||||||
BinaryReader reader = new BinaryReader(input);
|
|
||||||
|
|
||||||
uint asetMagic = reader.ReadUInt32();
|
|
||||||
if (asetMagic == 0x54455341)
|
|
||||||
{
|
|
||||||
uint asetVersion = reader.ReadUInt32();
|
|
||||||
if (asetVersion == 0)
|
|
||||||
{
|
|
||||||
ulong iconOffset = reader.ReadUInt64();
|
|
||||||
ulong iconSize = reader.ReadUInt64();
|
|
||||||
|
|
||||||
ulong nacpOffset = reader.ReadUInt64();
|
|
||||||
ulong nacpSize = reader.ReadUInt64();
|
|
||||||
|
|
||||||
ulong romfsOffset = reader.ReadUInt64();
|
|
||||||
ulong romfsSize = reader.ReadUInt64();
|
|
||||||
|
|
||||||
if (romfsSize != 0)
|
|
||||||
{
|
|
||||||
romfsStream = new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nacpSize != 0)
|
|
||||||
{
|
|
||||||
input.Seek(obj.FileSize + (long)nacpOffset, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
reader.Read(ControlData.ByteSpan);
|
|
||||||
|
|
||||||
ref ApplicationControlProperty nacp = ref ControlData.Value;
|
|
||||||
|
|
||||||
programInfo.Name = nacp.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString();
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(programInfo.Name))
|
|
||||||
{
|
|
||||||
programInfo.Name = nacp.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nacp.PresenceGroupId != 0)
|
|
||||||
{
|
|
||||||
programInfo.ProgramId = nacp.PresenceGroupId;
|
|
||||||
}
|
|
||||||
else if (nacp.SaveDataOwnerId != 0)
|
|
||||||
{
|
|
||||||
programInfo.ProgramId = nacp.SaveDataOwnerId;
|
|
||||||
}
|
|
||||||
else if (nacp.AddOnContentBaseId != 0)
|
|
||||||
{
|
|
||||||
programInfo.ProgramId = nacp.AddOnContentBaseId - 0x1000;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
programInfo.ProgramId = 0000000000000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Loader, $"Unsupported ASET header version found \"{asetVersion}\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
executable = new NsoExecutable(new LocalStorage(filePath, FileAccess.Read), Path.GetFileNameWithoutExtension(filePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
_device.Configuration.ContentManager.LoadEntries(_device);
|
|
||||||
|
|
||||||
_titleName = programInfo.Name;
|
|
||||||
TitleId = programInfo.ProgramId;
|
|
||||||
TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
|
|
||||||
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
|
|
||||||
|
|
||||||
// Explicitly null titleid to disable the shader cache.
|
|
||||||
Graphics.Gpu.GraphicsConfig.TitleId = null;
|
|
||||||
_device.Gpu.HostInitalized.Set();
|
|
||||||
|
|
||||||
ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable);
|
|
||||||
|
|
||||||
if (romfsStream != null)
|
|
||||||
{
|
|
||||||
_device.Configuration.VirtualFileSystem.SetRomFs(result.ProcessId, romfsStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
DiskCacheLoadState = result.DiskCacheLoadState;
|
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MetaLoader GetDefaultNpdm()
|
|
||||||
{
|
|
||||||
Assembly asm = Assembly.GetCallingAssembly();
|
|
||||||
|
|
||||||
using (Stream npdmStream = asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"))
|
|
||||||
{
|
|
||||||
var npdmBuffer = new byte[npdmStream.Length];
|
|
||||||
npdmStream.Read(npdmBuffer);
|
|
||||||
|
|
||||||
var metaLoader = new MetaLoader();
|
|
||||||
metaLoader.Load(npdmBuffer).ThrowIfFailure();
|
|
||||||
|
|
||||||
return metaLoader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (ulong applicationId, int programCount) GetMultiProgramInfo(VirtualFileSystem fileSystem, PartitionFileSystem pfs)
|
|
||||||
{
|
|
||||||
ulong mainProgramId = 0;
|
|
||||||
Span<bool> hasIndex = stackalloc bool[0x10];
|
|
||||||
|
|
||||||
fileSystem.ImportTickets(pfs);
|
|
||||||
|
|
||||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
|
||||||
{
|
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
||||||
|
|
||||||
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
|
||||||
|
|
||||||
if (nca.Header.ContentType != NcaContentType.Program)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
|
|
||||||
|
|
||||||
if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong currentProgramId = nca.Header.TitleId;
|
|
||||||
ulong currentMainProgramId = currentProgramId & ~0xFFFul;
|
|
||||||
|
|
||||||
if (mainProgramId == 0 && currentMainProgramId != 0)
|
|
||||||
{
|
|
||||||
mainProgramId = currentMainProgramId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainProgramId != currentMainProgramId)
|
|
||||||
{
|
|
||||||
// As far as I know there aren't any multi-application game cards containing multi-program applications,
|
|
||||||
// so because multi-application game cards are the only way we should run into multiple applications
|
|
||||||
// we'll just return that there's a single program.
|
|
||||||
return (mainProgramId, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasIndex[(int)(currentProgramId & 0xF)] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int programCount = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < hasIndex.Length && hasIndex[i]; i++)
|
|
||||||
{
|
|
||||||
programCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (mainProgramId, programCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result RegisterProgramMapInfo(PartitionFileSystem pfs)
|
|
||||||
{
|
|
||||||
(ulong applicationId, int programCount) = GetMultiProgramInfo(_device.Configuration.VirtualFileSystem, pfs);
|
|
||||||
|
|
||||||
if (programCount <= 0)
|
|
||||||
return Result.Success;
|
|
||||||
|
|
||||||
Span<ProgramIndexMapInfo> mapInfo = stackalloc ProgramIndexMapInfo[0x10];
|
|
||||||
|
|
||||||
for (int i = 0; i < programCount; i++)
|
|
||||||
{
|
|
||||||
mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i);
|
|
||||||
mapInfo[i].MainProgramId = new ApplicationId(applicationId);
|
|
||||||
mapInfo[i].ProgramIndex = (byte)i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _device.System.LibHacHorizonManager.NsClient.Fs.RegisterProgramIndexMapInfo(mapInfo.Slice(0, programCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result EnsureSaveData(ApplicationId applicationId)
|
|
||||||
{
|
|
||||||
Logger.Info?.Print(LogClass.Application, "Ensuring required savedata exists.");
|
|
||||||
|
|
||||||
Uid user = _device.System.AccountManager.LastOpenedUser.UserId.ToLibHacUid();
|
|
||||||
|
|
||||||
ref ApplicationControlProperty control = ref ControlData.Value;
|
|
||||||
|
|
||||||
if (LibHac.Common.Utilities.IsZeros(ControlData.ByteSpan))
|
|
||||||
{
|
|
||||||
// If the current application doesn't have a loaded control property, create a dummy one
|
|
||||||
// and set the savedata sizes so a user savedata will be created.
|
|
||||||
control = ref new BlitStruct<ApplicationControlProperty>(1).Value;
|
|
||||||
|
|
||||||
// The set sizes don't actually matter as long as they're non-zero because we use directory savedata.
|
|
||||||
control.UserAccountSaveDataSize = 0x4000;
|
|
||||||
control.UserAccountSaveDataJournalSize = 0x4000;
|
|
||||||
control.SaveDataOwnerId = applicationId.Value;
|
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Application,
|
|
||||||
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
|
|
||||||
}
|
|
||||||
|
|
||||||
HorizonClient hos = _device.System.LibHacHorizonManager.RyujinxClient;
|
|
||||||
Result resultCode = hos.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, in control);
|
|
||||||
|
|
||||||
if (resultCode.IsFailure())
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationCacheStorage. Result code {resultCode.ToStringWithName()}");
|
|
||||||
|
|
||||||
return resultCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
resultCode = hos.Fs.EnsureApplicationSaveData(out _, applicationId, in control, in user);
|
|
||||||
|
|
||||||
if (resultCode.IsFailure())
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationSaveData. Result code {resultCode.ToStringWithName()}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using Ryujinx.Cpu;
|
|
||||||
using Ryujinx.Cpu.Jit;
|
|
||||||
using Ryujinx.Graphics.Gpu;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
|
||||||
using Ryujinx.Memory;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
|
||||||
{
|
|
||||||
class ArmProcessContextFactory : IProcessContextFactory
|
|
||||||
{
|
|
||||||
private readonly ICpuEngine _cpuEngine;
|
|
||||||
private readonly GpuContext _gpu;
|
|
||||||
private readonly string _titleIdText;
|
|
||||||
private readonly string _displayVersion;
|
|
||||||
private readonly bool _diskCacheEnabled;
|
|
||||||
private readonly ulong _codeAddress;
|
|
||||||
private readonly ulong _codeSize;
|
|
||||||
|
|
||||||
public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
|
|
||||||
|
|
||||||
public ArmProcessContextFactory(
|
|
||||||
ICpuEngine cpuEngine,
|
|
||||||
GpuContext gpu,
|
|
||||||
string titleIdText,
|
|
||||||
string displayVersion,
|
|
||||||
bool diskCacheEnabled,
|
|
||||||
ulong codeAddress,
|
|
||||||
ulong codeSize)
|
|
||||||
{
|
|
||||||
_cpuEngine = cpuEngine;
|
|
||||||
_gpu = gpu;
|
|
||||||
_titleIdText = titleIdText;
|
|
||||||
_displayVersion = displayVersion;
|
|
||||||
_diskCacheEnabled = diskCacheEnabled;
|
|
||||||
_codeAddress = codeAddress;
|
|
||||||
_codeSize = codeSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
|
||||||
{
|
|
||||||
MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode;
|
|
||||||
|
|
||||||
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
|
||||||
{
|
|
||||||
mode = MemoryManagerMode.SoftwarePageTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
IArmProcessContext processContext;
|
|
||||||
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case MemoryManagerMode.SoftwarePageTable:
|
|
||||||
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
|
||||||
processContext = new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MemoryManagerMode.HostMapped:
|
|
||||||
case MemoryManagerMode.HostMappedUnsafe:
|
|
||||||
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
|
||||||
var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler);
|
|
||||||
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
|
|
||||||
|
|
||||||
return processContext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Ipc
|
|
||||||
{
|
|
||||||
class IpcHandleDesc
|
|
||||||
{
|
|
||||||
public bool HasPId { get; private set; }
|
|
||||||
|
|
||||||
public ulong PId { get; private set; }
|
|
||||||
|
|
||||||
public int[] ToCopy { get; private set; }
|
|
||||||
public int[] ToMove { get; private set; }
|
|
||||||
|
|
||||||
public IpcHandleDesc(BinaryReader reader)
|
|
||||||
{
|
|
||||||
int word = reader.ReadInt32();
|
|
||||||
|
|
||||||
HasPId = (word & 1) != 0;
|
|
||||||
|
|
||||||
ToCopy = new int[(word >> 1) & 0xf];
|
|
||||||
ToMove = new int[(word >> 5) & 0xf];
|
|
||||||
|
|
||||||
PId = HasPId ? reader.ReadUInt64() : 0;
|
|
||||||
|
|
||||||
for (int index = 0; index < ToCopy.Length; index++)
|
|
||||||
{
|
|
||||||
ToCopy[index] = reader.ReadInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < ToMove.Length; index++)
|
|
||||||
{
|
|
||||||
ToMove[index] = reader.ReadInt32();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IpcHandleDesc(int[] copy, int[] move)
|
|
||||||
{
|
|
||||||
ToCopy = copy ?? throw new ArgumentNullException(nameof(copy));
|
|
||||||
ToMove = move ?? throw new ArgumentNullException(nameof(move));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IpcHandleDesc(int[] copy, int[] move, ulong pId) : this(copy, move)
|
|
||||||
{
|
|
||||||
PId = pId;
|
|
||||||
|
|
||||||
HasPId = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IpcHandleDesc MakeCopy(params int[] handles)
|
|
||||||
{
|
|
||||||
return new IpcHandleDesc(handles, new int[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IpcHandleDesc MakeMove(params int[] handles)
|
|
||||||
{
|
|
||||||
return new IpcHandleDesc(new int[0], handles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] GetBytes()
|
|
||||||
{
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
|
||||||
|
|
||||||
int word = HasPId ? 1 : 0;
|
|
||||||
|
|
||||||
word |= (ToCopy.Length & 0xf) << 1;
|
|
||||||
word |= (ToMove.Length & 0xf) << 5;
|
|
||||||
|
|
||||||
writer.Write(word);
|
|
||||||
|
|
||||||
if (HasPId)
|
|
||||||
{
|
|
||||||
writer.Write(PId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int handle in ToCopy)
|
|
||||||
{
|
|
||||||
writer.Write(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int handle in ToMove)
|
|
||||||
{
|
|
||||||
writer.Write(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,278 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Ipc
|
|
||||||
{
|
|
||||||
class IpcMessage
|
|
||||||
{
|
|
||||||
public IpcMessageType Type { get; set; }
|
|
||||||
|
|
||||||
public IpcHandleDesc HandleDesc { get; set; }
|
|
||||||
|
|
||||||
public List<IpcPtrBuffDesc> PtrBuff { get; private set; }
|
|
||||||
public List<IpcBuffDesc> SendBuff { get; private set; }
|
|
||||||
public List<IpcBuffDesc> ReceiveBuff { get; private set; }
|
|
||||||
public List<IpcBuffDesc> ExchangeBuff { get; private set; }
|
|
||||||
public List<IpcRecvListBuffDesc> RecvListBuff { get; private set; }
|
|
||||||
|
|
||||||
public List<int> ObjectIds { get; private set; }
|
|
||||||
|
|
||||||
public byte[] RawData { get; set; }
|
|
||||||
|
|
||||||
public IpcMessage()
|
|
||||||
{
|
|
||||||
PtrBuff = new List<IpcPtrBuffDesc>();
|
|
||||||
SendBuff = new List<IpcBuffDesc>();
|
|
||||||
ReceiveBuff = new List<IpcBuffDesc>();
|
|
||||||
ExchangeBuff = new List<IpcBuffDesc>();
|
|
||||||
RecvListBuff = new List<IpcRecvListBuffDesc>();
|
|
||||||
|
|
||||||
ObjectIds = new List<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IpcMessage(byte[] data, long cmdPtr) : this()
|
|
||||||
{
|
|
||||||
using (MemoryStream ms = new MemoryStream(data))
|
|
||||||
{
|
|
||||||
BinaryReader reader = new BinaryReader(ms);
|
|
||||||
|
|
||||||
Initialize(reader, cmdPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize(BinaryReader reader, long cmdPtr)
|
|
||||||
{
|
|
||||||
int word0 = reader.ReadInt32();
|
|
||||||
int word1 = reader.ReadInt32();
|
|
||||||
|
|
||||||
Type = (IpcMessageType)(word0 & 0xffff);
|
|
||||||
|
|
||||||
int ptrBuffCount = (word0 >> 16) & 0xf;
|
|
||||||
int sendBuffCount = (word0 >> 20) & 0xf;
|
|
||||||
int recvBuffCount = (word0 >> 24) & 0xf;
|
|
||||||
int xchgBuffCount = (word0 >> 28) & 0xf;
|
|
||||||
|
|
||||||
int rawDataSize = (word1 >> 0) & 0x3ff;
|
|
||||||
int recvListFlags = (word1 >> 10) & 0xf;
|
|
||||||
bool hndDescEnable = ((word1 >> 31) & 0x1) != 0;
|
|
||||||
|
|
||||||
if (hndDescEnable)
|
|
||||||
{
|
|
||||||
HandleDesc = new IpcHandleDesc(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < ptrBuffCount; index++)
|
|
||||||
{
|
|
||||||
PtrBuff.Add(new IpcPtrBuffDesc(reader));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadBuff(List<IpcBuffDesc> buff, int count)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < count; index++)
|
|
||||||
{
|
|
||||||
buff.Add(new IpcBuffDesc(reader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadBuff(SendBuff, sendBuffCount);
|
|
||||||
ReadBuff(ReceiveBuff, recvBuffCount);
|
|
||||||
ReadBuff(ExchangeBuff, xchgBuffCount);
|
|
||||||
|
|
||||||
rawDataSize *= 4;
|
|
||||||
|
|
||||||
long recvListPos = reader.BaseStream.Position + rawDataSize;
|
|
||||||
|
|
||||||
// only HIPC have the padding requirements.
|
|
||||||
if (Type < IpcMessageType.TipcCloseSession)
|
|
||||||
{
|
|
||||||
long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr);
|
|
||||||
|
|
||||||
if (rawDataSize != 0)
|
|
||||||
{
|
|
||||||
rawDataSize -= (int)pad0;
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.BaseStream.Seek(pad0, SeekOrigin.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
int recvListCount = recvListFlags - 2;
|
|
||||||
|
|
||||||
if (recvListCount == 0)
|
|
||||||
{
|
|
||||||
recvListCount = 1;
|
|
||||||
}
|
|
||||||
else if (recvListCount < 0)
|
|
||||||
{
|
|
||||||
recvListCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
RawData = reader.ReadBytes(rawDataSize);
|
|
||||||
|
|
||||||
reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
for (int index = 0; index < recvListCount; index++)
|
|
||||||
{
|
|
||||||
RecvListBuff.Add(new IpcRecvListBuffDesc(reader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] GetBytes(long cmdPtr, ulong recvListAddr)
|
|
||||||
{
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
|
||||||
|
|
||||||
int word0;
|
|
||||||
int word1;
|
|
||||||
|
|
||||||
word0 = (int)Type;
|
|
||||||
word0 |= (PtrBuff.Count & 0xf) << 16;
|
|
||||||
word0 |= (SendBuff.Count & 0xf) << 20;
|
|
||||||
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
|
||||||
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
|
||||||
|
|
||||||
byte[] handleData = new byte[0];
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
handleData = HandleDesc.GetBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
int dataLength = RawData?.Length ?? 0;
|
|
||||||
|
|
||||||
dataLength = (dataLength + 3) & ~3;
|
|
||||||
|
|
||||||
int rawLength = dataLength;
|
|
||||||
|
|
||||||
int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8);
|
|
||||||
|
|
||||||
// Apparently, padding after Raw Data is 16 bytes, however when there is
|
|
||||||
// padding before Raw Data too, we need to subtract the size of this padding.
|
|
||||||
// This is the weirdest padding I've seen so far...
|
|
||||||
int pad1 = 0x10 - pad0;
|
|
||||||
|
|
||||||
dataLength = (dataLength + pad0 + pad1) / 4;
|
|
||||||
|
|
||||||
word1 = (dataLength & 0x3ff) | (2 << 10);
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
word1 |= 1 << 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(word0);
|
|
||||||
writer.Write(word1);
|
|
||||||
writer.Write(handleData);
|
|
||||||
|
|
||||||
for (int index = 0; index < PtrBuff.Count; index++)
|
|
||||||
{
|
|
||||||
writer.Write(PtrBuff[index].GetWord0());
|
|
||||||
writer.Write(PtrBuff[index].GetWord1());
|
|
||||||
}
|
|
||||||
|
|
||||||
ms.Seek(pad0, SeekOrigin.Current);
|
|
||||||
|
|
||||||
if (RawData != null)
|
|
||||||
{
|
|
||||||
writer.Write(RawData);
|
|
||||||
ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(new byte[pad1]);
|
|
||||||
writer.Write(recvListAddr);
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] GetBytesTipc()
|
|
||||||
{
|
|
||||||
Debug.Assert(PtrBuff.Count == 0);
|
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
|
||||||
|
|
||||||
int word0;
|
|
||||||
int word1;
|
|
||||||
|
|
||||||
word0 = (int)Type;
|
|
||||||
word0 |= (SendBuff.Count & 0xf) << 20;
|
|
||||||
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
|
||||||
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
|
||||||
|
|
||||||
byte[] handleData = new byte[0];
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
handleData = HandleDesc.GetBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
int dataLength = RawData?.Length ?? 0;
|
|
||||||
|
|
||||||
dataLength = ((dataLength + 3) & ~3) / 4;
|
|
||||||
|
|
||||||
word1 = (dataLength & 0x3ff);
|
|
||||||
|
|
||||||
if (HandleDesc != null)
|
|
||||||
{
|
|
||||||
word1 |= 1 << 31;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(word0);
|
|
||||||
writer.Write(word1);
|
|
||||||
writer.Write(handleData);
|
|
||||||
|
|
||||||
if (RawData != null)
|
|
||||||
{
|
|
||||||
writer.Write(RawData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ms.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long GetPadSize16(long position)
|
|
||||||
{
|
|
||||||
if ((position & 0xf) != 0)
|
|
||||||
{
|
|
||||||
return 0x10 - (position & 0xf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
public (ulong Position, ulong Size) GetBufferType0x21(int index = 0)
|
|
||||||
{
|
|
||||||
if (PtrBuff.Count > index && PtrBuff[index].Position != 0)
|
|
||||||
{
|
|
||||||
return (PtrBuff[index].Position, PtrBuff[index].Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SendBuff.Count > index)
|
|
||||||
{
|
|
||||||
return (SendBuff[index].Position, SendBuff[index].Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
public (ulong Position, ulong Size) GetBufferType0x22(int index = 0)
|
|
||||||
{
|
|
||||||
if (RecvListBuff.Count > index && RecvListBuff[index].Position != 0)
|
|
||||||
{
|
|
||||||
return (RecvListBuff[index].Position, RecvListBuff[index].Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReceiveBuff.Count > index)
|
|
||||||
{
|
|
||||||
return (ReceiveBuff[index].Position, ReceiveBuff[index].Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Ipc
|
|
||||||
{
|
|
||||||
enum IpcMessageType
|
|
||||||
{
|
|
||||||
HipcResponse = 0,
|
|
||||||
HipcCloseSession = 2,
|
|
||||||
HipcRequest = 4,
|
|
||||||
HipcControl = 5,
|
|
||||||
HipcRequestWithContext = 6,
|
|
||||||
HipcControlWithContext = 7,
|
|
||||||
TipcCloseSession = 0xF
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|
||||||
{
|
|
||||||
public enum AccountState
|
|
||||||
{
|
|
||||||
Closed,
|
|
||||||
Open
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
using LibHac;
|
|
||||||
using LibHac.Common;
|
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Arp;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Bcat
|
|
||||||
{
|
|
||||||
[Service("bcat:a", "bcat:a")]
|
|
||||||
[Service("bcat:m", "bcat:m")]
|
|
||||||
[Service("bcat:u", "bcat:u")]
|
|
||||||
[Service("bcat:s", "bcat:s")]
|
|
||||||
class IServiceCreator : DisposableIpcService
|
|
||||||
{
|
|
||||||
private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _base;
|
|
||||||
|
|
||||||
public IServiceCreator(ServiceCtx context, string serviceName)
|
|
||||||
{
|
|
||||||
var applicationClient = context.Device.System.LibHacHorizonManager.ApplicationClient;
|
|
||||||
applicationClient.Sm.GetService(ref _base, serviceName).ThrowIfFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
if (isDisposing)
|
|
||||||
{
|
|
||||||
_base.Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(0)]
|
|
||||||
// CreateBcatService(pid) -> object<nn::bcat::detail::ipc::IBcatService>
|
|
||||||
public ResultCode CreateBcatService(ServiceCtx context)
|
|
||||||
{
|
|
||||||
// TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId.
|
|
||||||
// Add an instance of nn::bcat::detail::service::core::PassphraseManager.
|
|
||||||
// Add an instance of nn::bcat::detail::service::ServiceMemoryManager.
|
|
||||||
// Add an instance of nn::bcat::detail::service::core::TaskManager who load "bcat-sys:/" system save data and open "dc/task.bin".
|
|
||||||
// If the file don't exist, create a new one (size of 0x800) and write 2 empty struct with a size of 0x400.
|
|
||||||
|
|
||||||
MakeObject(context, new IBcatService(ApplicationLaunchProperty.GetByPid(context)));
|
|
||||||
|
|
||||||
// NOTE: If the IBcatService is null this error is returned, Doesn't occur in our case.
|
|
||||||
// return ResultCode.NullObject;
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(1)]
|
|
||||||
// CreateDeliveryCacheStorageService(pid) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService>
|
|
||||||
public ResultCode CreateDeliveryCacheStorageService(ServiceCtx context)
|
|
||||||
{
|
|
||||||
ulong pid = context.RequestData.ReadUInt64();
|
|
||||||
|
|
||||||
using var serv = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>();
|
|
||||||
|
|
||||||
Result rc = _base.Get.CreateDeliveryCacheStorageService(ref serv.Ref(), pid);
|
|
||||||
|
|
||||||
if (rc.IsSuccess())
|
|
||||||
{
|
|
||||||
MakeObject(context, new IDeliveryCacheStorageService(context, ref serv.Ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ResultCode)rc.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(2)]
|
|
||||||
// CreateDeliveryCacheStorageServiceWithApplicationId(nn::ApplicationId) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService>
|
|
||||||
public ResultCode CreateDeliveryCacheStorageServiceWithApplicationId(ServiceCtx context)
|
|
||||||
{
|
|
||||||
ApplicationId applicationId = context.RequestData.ReadStruct<ApplicationId>();
|
|
||||||
|
|
||||||
using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>();
|
|
||||||
|
|
||||||
Result rc = _base.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref service.Ref(), applicationId);
|
|
||||||
|
|
||||||
if (rc.IsSuccess())
|
|
||||||
{
|
|
||||||
MakeObject(context, new IDeliveryCacheStorageService(context, ref service.Ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ResultCode)rc.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Bcat
|
|
||||||
{
|
|
||||||
enum ResultCode
|
|
||||||
{
|
|
||||||
ModuleId = 122,
|
|
||||||
ErrorCodeShift = 9,
|
|
||||||
|
|
||||||
Success = 0,
|
|
||||||
|
|
||||||
InvalidArgument = (1 << ErrorCodeShift) | ModuleId,
|
|
||||||
NotFound = (2 << ErrorCodeShift) | ModuleId,
|
|
||||||
TargetLocked = (3 << ErrorCodeShift) | ModuleId,
|
|
||||||
TargetAlreadyMounted = (4 << ErrorCodeShift) | ModuleId,
|
|
||||||
TargetNotMounted = (5 << ErrorCodeShift) | ModuleId,
|
|
||||||
AlreadyOpen = (6 << ErrorCodeShift) | ModuleId,
|
|
||||||
NotOpen = (7 << ErrorCodeShift) | ModuleId,
|
|
||||||
InternetRequestDenied = (8 << ErrorCodeShift) | ModuleId,
|
|
||||||
ServiceOpenLimitReached = (9 << ErrorCodeShift) | ModuleId,
|
|
||||||
SaveDataNotFound = (10 << ErrorCodeShift) | ModuleId,
|
|
||||||
NetworkServiceAccountNotAvailable = (31 << ErrorCodeShift) | ModuleId,
|
|
||||||
PassphrasePathNotFound = (80 << ErrorCodeShift) | ModuleId,
|
|
||||||
DataVerificationFailed = (81 << ErrorCodeShift) | ModuleId,
|
|
||||||
PermissionDenied = (90 << ErrorCodeShift) | ModuleId,
|
|
||||||
AllocationFailed = (91 << ErrorCodeShift) | ModuleId,
|
|
||||||
InvalidOperation = (98 << ErrorCodeShift) | ModuleId,
|
|
||||||
InvalidDeliveryCacheStorageFile = (204 << ErrorCodeShift) | ModuleId,
|
|
||||||
StorageOpenLimitReached = (205 << ErrorCodeShift) | ModuleId
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Arp;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
|
|
||||||
{
|
|
||||||
class IBcatService : IpcService
|
|
||||||
{
|
|
||||||
public IBcatService(ApplicationLaunchProperty applicationLaunchProperty) { }
|
|
||||||
|
|
||||||
[CommandHipc(10100)]
|
|
||||||
// RequestSyncDeliveryCache() -> object<nn::bcat::detail::ipc::IDeliveryCacheProgressService>
|
|
||||||
public ResultCode RequestSyncDeliveryCache(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MakeObject(context, new IDeliveryCacheProgressService(context));
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
using LibHac;
|
|
||||||
using LibHac.Bcat;
|
|
||||||
using LibHac.Common;
|
|
||||||
using Ryujinx.Common;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
|
|
||||||
{
|
|
||||||
class IDeliveryCacheDirectoryService : DisposableIpcService
|
|
||||||
{
|
|
||||||
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _base;
|
|
||||||
|
|
||||||
public IDeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> baseService)
|
|
||||||
{
|
|
||||||
_base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref baseService);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
if (isDisposing)
|
|
||||||
{
|
|
||||||
_base.Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(0)]
|
|
||||||
// Open(nn::bcat::DirectoryName)
|
|
||||||
public ResultCode Open(ServiceCtx context)
|
|
||||||
{
|
|
||||||
DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>();
|
|
||||||
|
|
||||||
Result result = _base.Get.Open(ref directoryName);
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(1)]
|
|
||||||
// Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>)
|
|
||||||
public ResultCode Read(ServiceCtx context)
|
|
||||||
{
|
|
||||||
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
|
|
||||||
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
|
|
||||||
|
|
||||||
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
|
|
||||||
{
|
|
||||||
Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(region.Memory.Span));
|
|
||||||
|
|
||||||
context.ResponseData.Write(entriesRead);
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(2)]
|
|
||||||
// GetCount() -> u32
|
|
||||||
public ResultCode GetCount(ServiceCtx context)
|
|
||||||
{
|
|
||||||
Result result = _base.Get.GetCount(out int count);
|
|
||||||
|
|
||||||
context.ResponseData.Write(count);
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
using LibHac;
|
|
||||||
using LibHac.Bcat;
|
|
||||||
using LibHac.Common;
|
|
||||||
using Ryujinx.Common;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
|
|
||||||
{
|
|
||||||
class IDeliveryCacheFileService : DisposableIpcService
|
|
||||||
{
|
|
||||||
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _base;
|
|
||||||
|
|
||||||
public IDeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> baseService)
|
|
||||||
{
|
|
||||||
_base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref baseService);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
if (isDisposing)
|
|
||||||
{
|
|
||||||
_base.Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(0)]
|
|
||||||
// Open(nn::bcat::DirectoryName, nn::bcat::FileName)
|
|
||||||
public ResultCode Open(ServiceCtx context)
|
|
||||||
{
|
|
||||||
DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>();
|
|
||||||
FileName fileName = context.RequestData.ReadStruct<FileName>();
|
|
||||||
|
|
||||||
Result result = _base.Get.Open(ref directoryName, ref fileName);
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(1)]
|
|
||||||
// Read(u64) -> (u64, buffer<bytes, 6>)
|
|
||||||
public ResultCode Read(ServiceCtx context)
|
|
||||||
{
|
|
||||||
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
|
|
||||||
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
|
|
||||||
|
|
||||||
long offset = context.RequestData.ReadInt64();
|
|
||||||
|
|
||||||
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
|
|
||||||
{
|
|
||||||
Result result = _base.Get.Read(out long bytesRead, offset, region.Memory.Span);
|
|
||||||
|
|
||||||
context.ResponseData.Write(bytesRead);
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(2)]
|
|
||||||
// GetSize() -> u64
|
|
||||||
public ResultCode GetSize(ServiceCtx context)
|
|
||||||
{
|
|
||||||
Result result = _base.Get.GetSize(out long size);
|
|
||||||
|
|
||||||
context.ResponseData.Write(size);
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(3)]
|
|
||||||
// GetDigest() -> nn::bcat::Digest
|
|
||||||
public ResultCode GetDigest(ServiceCtx context)
|
|
||||||
{
|
|
||||||
Result result = _base.Get.GetDigest(out Digest digest);
|
|
||||||
|
|
||||||
context.ResponseData.WriteStruct(digest);
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Cpu;
|
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types;
|
|
||||||
using Ryujinx.Horizon.Common;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
|
|
||||||
{
|
|
||||||
class IDeliveryCacheProgressService : IpcService
|
|
||||||
{
|
|
||||||
private KEvent _event;
|
|
||||||
private int _eventHandle;
|
|
||||||
|
|
||||||
public IDeliveryCacheProgressService(ServiceCtx context)
|
|
||||||
{
|
|
||||||
_event = new KEvent(context.Device.System.KernelContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(0)]
|
|
||||||
// GetEvent() -> handle<copy>
|
|
||||||
public ResultCode GetEvent(ServiceCtx context)
|
|
||||||
{
|
|
||||||
if (_eventHandle == 0)
|
|
||||||
{
|
|
||||||
if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Out of handles!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceBcat);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(1)]
|
|
||||||
// GetImpl() -> buffer<nn::bcat::detail::DeliveryCacheProgressImpl, 0x1a>
|
|
||||||
public ResultCode GetImpl(ServiceCtx context)
|
|
||||||
{
|
|
||||||
DeliveryCacheProgressImpl deliveryCacheProgress = new DeliveryCacheProgressImpl
|
|
||||||
{
|
|
||||||
State = DeliveryCacheProgressImpl.Status.Done,
|
|
||||||
Result = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
ulong dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress);
|
|
||||||
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(dcpSize);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceBcat);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress)
|
|
||||||
{
|
|
||||||
return MemoryHelper.Write(context.Memory, ipcDesc.Position, deliveryCacheProgress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
using LibHac;
|
|
||||||
using LibHac.Bcat;
|
|
||||||
using LibHac.Common;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
|
|
||||||
{
|
|
||||||
class IDeliveryCacheStorageService : DisposableIpcService
|
|
||||||
{
|
|
||||||
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _base;
|
|
||||||
|
|
||||||
public IDeliveryCacheStorageService(ServiceCtx context, ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> baseService)
|
|
||||||
{
|
|
||||||
_base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref baseService);
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(0)]
|
|
||||||
// CreateFileService() -> object<nn::bcat::detail::ipc::IDeliveryCacheFileService>
|
|
||||||
public ResultCode CreateFileService(ServiceCtx context)
|
|
||||||
{
|
|
||||||
using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>();
|
|
||||||
|
|
||||||
Result result = _base.Get.CreateFileService(ref service.Ref());
|
|
||||||
|
|
||||||
if (result.IsSuccess())
|
|
||||||
{
|
|
||||||
MakeObject(context, new IDeliveryCacheFileService(ref service.Ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(1)]
|
|
||||||
// CreateDirectoryService() -> object<nn::bcat::detail::ipc::IDeliveryCacheDirectoryService>
|
|
||||||
public ResultCode CreateDirectoryService(ServiceCtx context)
|
|
||||||
{
|
|
||||||
using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>();
|
|
||||||
|
|
||||||
Result result = _base.Get.CreateDirectoryService(ref service.Ref());
|
|
||||||
|
|
||||||
if (result.IsSuccess())
|
|
||||||
{
|
|
||||||
MakeObject(context, new IDeliveryCacheDirectoryService(ref service.Ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHipc(10)]
|
|
||||||
// EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>)
|
|
||||||
public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context)
|
|
||||||
{
|
|
||||||
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
|
|
||||||
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
|
|
||||||
|
|
||||||
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
|
|
||||||
{
|
|
||||||
Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(region.Memory.Span));
|
|
||||||
|
|
||||||
context.ResponseData.Write(count);
|
|
||||||
|
|
||||||
return (ResultCode)result.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
if (isDisposing)
|
|
||||||
{
|
|
||||||
_base.Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
|
||||||
{
|
|
||||||
interface ISampledData
|
|
||||||
{
|
|
||||||
ulong SamplingNumber { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct ZbcColorArray
|
|
||||||
{
|
|
||||||
private uint element0;
|
|
||||||
private uint element1;
|
|
||||||
private uint element2;
|
|
||||||
private uint element3;
|
|
||||||
|
|
||||||
public uint this[int index]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (index == 0)
|
|
||||||
{
|
|
||||||
return element0;
|
|
||||||
}
|
|
||||||
else if (index == 1)
|
|
||||||
{
|
|
||||||
return element1;
|
|
||||||
}
|
|
||||||
else if (index == 2)
|
|
||||||
{
|
|
||||||
return element2;
|
|
||||||
}
|
|
||||||
else if (index == 2)
|
|
||||||
{
|
|
||||||
return element3;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IndexOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct ZbcSetTableArguments
|
|
||||||
{
|
|
||||||
public ZbcColorArray ColorDs;
|
|
||||||
public ZbcColorArray ColorL2;
|
|
||||||
public uint Depth;
|
|
||||||
public uint Format;
|
|
||||||
public uint Type;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,384 +0,0 @@
|
|||||||
using Ryujinx.HLE.HOS.Ipc;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
||||||
using Ryujinx.Horizon.Common;
|
|
||||||
using System;
|
|
||||||
using System.Buffers.Binary;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services
|
|
||||||
{
|
|
||||||
class ServerBase : IDisposable
|
|
||||||
{
|
|
||||||
// Must be the maximum value used by services (highest one know is the one used by nvservices = 0x8000).
|
|
||||||
// Having a size that is too low will cause failures as data copy will fail if the receiving buffer is
|
|
||||||
// not large enough.
|
|
||||||
private const int PointerBufferSize = 0x8000;
|
|
||||||
|
|
||||||
private readonly static int[] DefaultCapabilities = new int[]
|
|
||||||
{
|
|
||||||
0x030363F7,
|
|
||||||
0x1FFFFFCF,
|
|
||||||
0x207FFFEF,
|
|
||||||
0x47E0060F,
|
|
||||||
0x0048BFFF,
|
|
||||||
0x01007FFF
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
|
||||||
private KProcess _selfProcess;
|
|
||||||
|
|
||||||
private readonly List<int> _sessionHandles = new List<int>();
|
|
||||||
private readonly List<int> _portHandles = new List<int>();
|
|
||||||
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
|
|
||||||
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
|
|
||||||
|
|
||||||
public ManualResetEvent InitDone { get; }
|
|
||||||
public string Name { get; }
|
|
||||||
public Func<IpcService> SmObjectFactory { get; }
|
|
||||||
|
|
||||||
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
|
||||||
{
|
|
||||||
InitDone = new ManualResetEvent(false);
|
|
||||||
_context = context;
|
|
||||||
Name = name;
|
|
||||||
SmObjectFactory = smObjectFactory;
|
|
||||||
|
|
||||||
const ProcessCreationFlags flags =
|
|
||||||
ProcessCreationFlags.EnableAslr |
|
|
||||||
ProcessCreationFlags.AddressSpace64Bit |
|
|
||||||
ProcessCreationFlags.Is64Bit |
|
|
||||||
ProcessCreationFlags.PoolPartitionSystem;
|
|
||||||
|
|
||||||
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
|
|
||||||
|
|
||||||
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
|
||||||
{
|
|
||||||
_portHandles.Add(serverPortHandle);
|
|
||||||
_ports.Add(serverPortHandle, objectFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddSessionObj(KServerSession serverSession, IpcService obj)
|
|
||||||
{
|
|
||||||
// Ensure that the sever loop is running.
|
|
||||||
InitDone.WaitOne();
|
|
||||||
|
|
||||||
_selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle);
|
|
||||||
AddSessionObj(serverSessionHandle, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddSessionObj(int serverSessionHandle, IpcService obj)
|
|
||||||
{
|
|
||||||
_sessionHandles.Add(serverSessionHandle);
|
|
||||||
_sessions.Add(serverSessionHandle, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Main()
|
|
||||||
{
|
|
||||||
ServerLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ServerLoop()
|
|
||||||
{
|
|
||||||
_selfProcess = KernelStatic.GetCurrentProcess();
|
|
||||||
|
|
||||||
if (SmObjectFactory != null)
|
|
||||||
{
|
|
||||||
_context.Syscall.ManageNamedPort(out int serverPortHandle, "sm:", 50);
|
|
||||||
|
|
||||||
AddPort(serverPortHandle, SmObjectFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
InitDone.Set();
|
|
||||||
|
|
||||||
KThread thread = KernelStatic.GetCurrentThread();
|
|
||||||
ulong messagePtr = thread.TlsAddress;
|
|
||||||
_context.Syscall.SetHeapSize(out ulong heapAddr, 0x200000);
|
|
||||||
|
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
|
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
|
||||||
|
|
||||||
int replyTargetHandle = 0;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int[] portHandles = _portHandles.ToArray();
|
|
||||||
int[] sessionHandles = _sessionHandles.ToArray();
|
|
||||||
int[] handles = new int[portHandles.Length + sessionHandles.Length];
|
|
||||||
|
|
||||||
portHandles.CopyTo(handles, 0);
|
|
||||||
sessionHandles.CopyTo(handles, portHandles.Length);
|
|
||||||
|
|
||||||
// We still need a timeout here to allow the service to pick up and listen new sessions...
|
|
||||||
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles, replyTargetHandle, 1000000L);
|
|
||||||
|
|
||||||
thread.HandlePostSyscall();
|
|
||||||
|
|
||||||
if (!thread.Context.Running)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
replyTargetHandle = 0;
|
|
||||||
|
|
||||||
if (rc == Result.Success && signaledIndex >= portHandles.Length)
|
|
||||||
{
|
|
||||||
// We got a IPC request, process it, pass to the appropriate service if needed.
|
|
||||||
int signaledHandle = handles[signaledIndex];
|
|
||||||
|
|
||||||
if (Process(signaledHandle, heapAddr))
|
|
||||||
{
|
|
||||||
replyTargetHandle = signaledHandle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (rc == Result.Success)
|
|
||||||
{
|
|
||||||
// We got a new connection, accept the session to allow servicing future requests.
|
|
||||||
if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == Result.Success)
|
|
||||||
{
|
|
||||||
IpcService obj = _ports[handles[signaledIndex]].Invoke();
|
|
||||||
|
|
||||||
AddSessionObj(serverSessionHandle, obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
|
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool Process(int serverSessionHandle, ulong recvListAddr)
|
|
||||||
{
|
|
||||||
KProcess process = KernelStatic.GetCurrentProcess();
|
|
||||||
KThread thread = KernelStatic.GetCurrentThread();
|
|
||||||
ulong messagePtr = thread.TlsAddress;
|
|
||||||
ulong messageSize = 0x100;
|
|
||||||
|
|
||||||
byte[] reqData = new byte[messageSize];
|
|
||||||
|
|
||||||
process.CpuMemory.Read(messagePtr, reqData);
|
|
||||||
|
|
||||||
IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
|
|
||||||
IpcMessage response = new IpcMessage();
|
|
||||||
|
|
||||||
ulong tempAddr = recvListAddr;
|
|
||||||
int sizesOffset = request.RawData.Length - ((request.RecvListBuff.Count * 2 + 3) & ~3);
|
|
||||||
|
|
||||||
bool noReceive = true;
|
|
||||||
|
|
||||||
for (int i = 0; i < request.ReceiveBuff.Count; i++)
|
|
||||||
{
|
|
||||||
noReceive &= (request.ReceiveBuff[i].Position == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (noReceive)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < request.RecvListBuff.Count; i++)
|
|
||||||
{
|
|
||||||
ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan(sizesOffset + i * 2, 2));
|
|
||||||
|
|
||||||
response.PtrBuff.Add(new IpcPtrBuffDesc(tempAddr, (uint)i, size));
|
|
||||||
|
|
||||||
request.RecvListBuff[i] = new IpcRecvListBuffDesc(tempAddr, size);
|
|
||||||
|
|
||||||
tempAddr += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shouldReply = true;
|
|
||||||
bool isTipcCommunication = false;
|
|
||||||
|
|
||||||
using (MemoryStream raw = new MemoryStream(request.RawData))
|
|
||||||
{
|
|
||||||
BinaryReader reqReader = new BinaryReader(raw);
|
|
||||||
|
|
||||||
if (request.Type == IpcMessageType.HipcRequest ||
|
|
||||||
request.Type == IpcMessageType.HipcRequestWithContext)
|
|
||||||
{
|
|
||||||
response.Type = IpcMessageType.HipcResponse;
|
|
||||||
|
|
||||||
using (MemoryStream resMs = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
|
||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
|
||||||
_context.Device,
|
|
||||||
process,
|
|
||||||
process.CpuMemory,
|
|
||||||
thread,
|
|
||||||
request,
|
|
||||||
response,
|
|
||||||
reqReader,
|
|
||||||
resWriter);
|
|
||||||
|
|
||||||
_sessions[serverSessionHandle].CallHipcMethod(context);
|
|
||||||
|
|
||||||
response.RawData = resMs.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (request.Type == IpcMessageType.HipcControl ||
|
|
||||||
request.Type == IpcMessageType.HipcControlWithContext)
|
|
||||||
{
|
|
||||||
uint magic = (uint)reqReader.ReadUInt64();
|
|
||||||
uint cmdId = (uint)reqReader.ReadUInt64();
|
|
||||||
|
|
||||||
switch (cmdId)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
request = FillResponse(response, 0, PointerBufferSize);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
int unknown = reqReader.ReadInt32();
|
|
||||||
|
|
||||||
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
|
|
||||||
|
|
||||||
AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
|
|
||||||
|
|
||||||
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
|
|
||||||
|
|
||||||
request = FillResponse(response, 0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: throw new NotImplementedException(cmdId.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
|
|
||||||
{
|
|
||||||
_context.Syscall.CloseHandle(serverSessionHandle);
|
|
||||||
_sessionHandles.Remove(serverSessionHandle);
|
|
||||||
IpcService service = _sessions[serverSessionHandle];
|
|
||||||
if (service is IDisposable disposableObj)
|
|
||||||
{
|
|
||||||
disposableObj.Dispose();
|
|
||||||
}
|
|
||||||
_sessions.Remove(serverSessionHandle);
|
|
||||||
shouldReply = false;
|
|
||||||
}
|
|
||||||
// If the type is past 0xF, we are using TIPC
|
|
||||||
else if (request.Type > IpcMessageType.TipcCloseSession)
|
|
||||||
{
|
|
||||||
isTipcCommunication = true;
|
|
||||||
|
|
||||||
// Response type is always the same as request on TIPC.
|
|
||||||
response.Type = request.Type;
|
|
||||||
|
|
||||||
using (MemoryStream resMs = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
|
||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
|
||||||
_context.Device,
|
|
||||||
process,
|
|
||||||
process.CpuMemory,
|
|
||||||
thread,
|
|
||||||
request,
|
|
||||||
response,
|
|
||||||
reqReader,
|
|
||||||
resWriter);
|
|
||||||
|
|
||||||
_sessions[serverSessionHandle].CallTipcMethod(context);
|
|
||||||
|
|
||||||
response.RawData = resMs.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new NotImplementedException(request.Type.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTipcCommunication)
|
|
||||||
{
|
|
||||||
process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldReply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
|
|
||||||
{
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
|
||||||
|
|
||||||
foreach (int value in values)
|
|
||||||
{
|
|
||||||
writer.Write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FillResponse(response, result, ms.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
|
|
||||||
{
|
|
||||||
response.Type = IpcMessageType.HipcResponse;
|
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
|
||||||
|
|
||||||
writer.Write(IpcMagic.Sfco);
|
|
||||||
writer.Write(result);
|
|
||||||
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
writer.Write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
response.RawData = ms.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
foreach (IpcService service in _sessions.Values)
|
|
||||||
{
|
|
||||||
if (service is IDisposable disposableObj)
|
|
||||||
{
|
|
||||||
disposableObj.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
service.DestroyAtExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
_sessions.Clear();
|
|
||||||
|
|
||||||
InitDone.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace Ryujinx.Headless.SDL2
|
|
||||||
{
|
|
||||||
public enum HideCursor
|
|
||||||
{
|
|
||||||
Never,
|
|
||||||
OnIdle,
|
|
||||||
Always
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Ryujinx.Horizon.Generators.Kernel
|
|
||||||
{
|
|
||||||
class SyscallSyntaxReceiver : ISyntaxReceiver
|
|
||||||
{
|
|
||||||
public List<MethodDeclarationSyntax> SvcImplementations { get; }
|
|
||||||
|
|
||||||
public SyscallSyntaxReceiver()
|
|
||||||
{
|
|
||||||
SvcImplementations = new List<MethodDeclarationSyntax>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
|
||||||
{
|
|
||||||
if (syntaxNode is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Count != 0)
|
|
||||||
{
|
|
||||||
foreach (var attributeList in classDeclaration.AttributeLists)
|
|
||||||
{
|
|
||||||
if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "SvcImpl"))
|
|
||||||
{
|
|
||||||
foreach (var memberDeclaration in classDeclaration.Members)
|
|
||||||
{
|
|
||||||
if (memberDeclaration is MethodDeclarationSyntax methodDeclaration)
|
|
||||||
{
|
|
||||||
VisitMethod(methodDeclaration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void VisitMethod(MethodDeclarationSyntax methodDeclaration)
|
|
||||||
{
|
|
||||||
if (methodDeclaration.AttributeLists.Count != 0)
|
|
||||||
{
|
|
||||||
foreach (var attributeList in methodDeclaration.AttributeLists)
|
|
||||||
{
|
|
||||||
if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "Svc"))
|
|
||||||
{
|
|
||||||
SvcImplementations.Add(methodDeclaration);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
namespace Ryujinx.Tests.Unicorn
|
|
||||||
{
|
|
||||||
public enum MemoryPermission
|
|
||||||
{
|
|
||||||
NONE = 0,
|
|
||||||
READ = 1,
|
|
||||||
WRITE = 2,
|
|
||||||
EXEC = 4,
|
|
||||||
ALL = 7,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Arch
|
|
||||||
{
|
|
||||||
ARM = 1,
|
|
||||||
ARM64 = 2,
|
|
||||||
MIPS = 3,
|
|
||||||
X86 = 4,
|
|
||||||
PPC = 5,
|
|
||||||
SPARC = 6,
|
|
||||||
M68K = 7,
|
|
||||||
RISCV = 8,
|
|
||||||
S390X = 9,
|
|
||||||
TRICORE = 10,
|
|
||||||
MAX = 11,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Arm
|
|
||||||
{
|
|
||||||
|
|
||||||
// ARM CPU
|
|
||||||
|
|
||||||
CPU_ARM_926 = 0,
|
|
||||||
CPU_ARM_946 = 1,
|
|
||||||
CPU_ARM_1026 = 2,
|
|
||||||
CPU_ARM_1136_R2 = 3,
|
|
||||||
CPU_ARM_1136 = 4,
|
|
||||||
CPU_ARM_1176 = 5,
|
|
||||||
CPU_ARM_11MPCORE = 6,
|
|
||||||
CPU_ARM_CORTEX_M0 = 7,
|
|
||||||
CPU_ARM_CORTEX_M3 = 8,
|
|
||||||
CPU_ARM_CORTEX_M4 = 9,
|
|
||||||
CPU_ARM_CORTEX_M7 = 10,
|
|
||||||
CPU_ARM_CORTEX_M33 = 11,
|
|
||||||
CPU_ARM_CORTEX_R5 = 12,
|
|
||||||
CPU_ARM_CORTEX_R5F = 13,
|
|
||||||
CPU_ARM_CORTEX_A7 = 14,
|
|
||||||
CPU_ARM_CORTEX_A8 = 15,
|
|
||||||
CPU_ARM_CORTEX_A9 = 16,
|
|
||||||
CPU_ARM_CORTEX_A15 = 17,
|
|
||||||
CPU_ARM_TI925T = 18,
|
|
||||||
CPU_ARM_SA1100 = 19,
|
|
||||||
CPU_ARM_SA1110 = 20,
|
|
||||||
CPU_ARM_PXA250 = 21,
|
|
||||||
CPU_ARM_PXA255 = 22,
|
|
||||||
CPU_ARM_PXA260 = 23,
|
|
||||||
CPU_ARM_PXA261 = 24,
|
|
||||||
CPU_ARM_PXA262 = 25,
|
|
||||||
CPU_ARM_PXA270 = 26,
|
|
||||||
CPU_ARM_PXA270A0 = 27,
|
|
||||||
CPU_ARM_PXA270A1 = 28,
|
|
||||||
CPU_ARM_PXA270B0 = 29,
|
|
||||||
CPU_ARM_PXA270B1 = 30,
|
|
||||||
CPU_ARM_PXA270C0 = 31,
|
|
||||||
CPU_ARM_PXA270C5 = 32,
|
|
||||||
CPU_ARM_MAX = 33,
|
|
||||||
CPU_ARM_ENDING = 34,
|
|
||||||
|
|
||||||
// ARM registers
|
|
||||||
|
|
||||||
REG_INVALID = 0,
|
|
||||||
REG_APSR = 1,
|
|
||||||
REG_APSR_NZCV = 2,
|
|
||||||
REG_CPSR = 3,
|
|
||||||
REG_FPEXC = 4,
|
|
||||||
REG_FPINST = 5,
|
|
||||||
REG_FPSCR = 6,
|
|
||||||
REG_FPSCR_NZCV = 7,
|
|
||||||
REG_FPSID = 8,
|
|
||||||
REG_ITSTATE = 9,
|
|
||||||
REG_LR = 10,
|
|
||||||
REG_PC = 11,
|
|
||||||
REG_SP = 12,
|
|
||||||
REG_SPSR = 13,
|
|
||||||
REG_D0 = 14,
|
|
||||||
REG_D1 = 15,
|
|
||||||
REG_D2 = 16,
|
|
||||||
REG_D3 = 17,
|
|
||||||
REG_D4 = 18,
|
|
||||||
REG_D5 = 19,
|
|
||||||
REG_D6 = 20,
|
|
||||||
REG_D7 = 21,
|
|
||||||
REG_D8 = 22,
|
|
||||||
REG_D9 = 23,
|
|
||||||
REG_D10 = 24,
|
|
||||||
REG_D11 = 25,
|
|
||||||
REG_D12 = 26,
|
|
||||||
REG_D13 = 27,
|
|
||||||
REG_D14 = 28,
|
|
||||||
REG_D15 = 29,
|
|
||||||
REG_D16 = 30,
|
|
||||||
REG_D17 = 31,
|
|
||||||
REG_D18 = 32,
|
|
||||||
REG_D19 = 33,
|
|
||||||
REG_D20 = 34,
|
|
||||||
REG_D21 = 35,
|
|
||||||
REG_D22 = 36,
|
|
||||||
REG_D23 = 37,
|
|
||||||
REG_D24 = 38,
|
|
||||||
REG_D25 = 39,
|
|
||||||
REG_D26 = 40,
|
|
||||||
REG_D27 = 41,
|
|
||||||
REG_D28 = 42,
|
|
||||||
REG_D29 = 43,
|
|
||||||
REG_D30 = 44,
|
|
||||||
REG_D31 = 45,
|
|
||||||
REG_FPINST2 = 46,
|
|
||||||
REG_MVFR0 = 47,
|
|
||||||
REG_MVFR1 = 48,
|
|
||||||
REG_MVFR2 = 49,
|
|
||||||
REG_Q0 = 50,
|
|
||||||
REG_Q1 = 51,
|
|
||||||
REG_Q2 = 52,
|
|
||||||
REG_Q3 = 53,
|
|
||||||
REG_Q4 = 54,
|
|
||||||
REG_Q5 = 55,
|
|
||||||
REG_Q6 = 56,
|
|
||||||
REG_Q7 = 57,
|
|
||||||
REG_Q8 = 58,
|
|
||||||
REG_Q9 = 59,
|
|
||||||
REG_Q10 = 60,
|
|
||||||
REG_Q11 = 61,
|
|
||||||
REG_Q12 = 62,
|
|
||||||
REG_Q13 = 63,
|
|
||||||
REG_Q14 = 64,
|
|
||||||
REG_Q15 = 65,
|
|
||||||
REG_R0 = 66,
|
|
||||||
REG_R1 = 67,
|
|
||||||
REG_R2 = 68,
|
|
||||||
REG_R3 = 69,
|
|
||||||
REG_R4 = 70,
|
|
||||||
REG_R5 = 71,
|
|
||||||
REG_R6 = 72,
|
|
||||||
REG_R7 = 73,
|
|
||||||
REG_R8 = 74,
|
|
||||||
REG_R9 = 75,
|
|
||||||
REG_R10 = 76,
|
|
||||||
REG_R11 = 77,
|
|
||||||
REG_R12 = 78,
|
|
||||||
REG_S0 = 79,
|
|
||||||
REG_S1 = 80,
|
|
||||||
REG_S2 = 81,
|
|
||||||
REG_S3 = 82,
|
|
||||||
REG_S4 = 83,
|
|
||||||
REG_S5 = 84,
|
|
||||||
REG_S6 = 85,
|
|
||||||
REG_S7 = 86,
|
|
||||||
REG_S8 = 87,
|
|
||||||
REG_S9 = 88,
|
|
||||||
REG_S10 = 89,
|
|
||||||
REG_S11 = 90,
|
|
||||||
REG_S12 = 91,
|
|
||||||
REG_S13 = 92,
|
|
||||||
REG_S14 = 93,
|
|
||||||
REG_S15 = 94,
|
|
||||||
REG_S16 = 95,
|
|
||||||
REG_S17 = 96,
|
|
||||||
REG_S18 = 97,
|
|
||||||
REG_S19 = 98,
|
|
||||||
REG_S20 = 99,
|
|
||||||
REG_S21 = 100,
|
|
||||||
REG_S22 = 101,
|
|
||||||
REG_S23 = 102,
|
|
||||||
REG_S24 = 103,
|
|
||||||
REG_S25 = 104,
|
|
||||||
REG_S26 = 105,
|
|
||||||
REG_S27 = 106,
|
|
||||||
REG_S28 = 107,
|
|
||||||
REG_S29 = 108,
|
|
||||||
REG_S30 = 109,
|
|
||||||
REG_S31 = 110,
|
|
||||||
REG_C1_C0_2 = 111,
|
|
||||||
REG_C13_C0_2 = 112,
|
|
||||||
REG_C13_C0_3 = 113,
|
|
||||||
REG_IPSR = 114,
|
|
||||||
REG_MSP = 115,
|
|
||||||
REG_PSP = 116,
|
|
||||||
REG_CONTROL = 117,
|
|
||||||
REG_IAPSR = 118,
|
|
||||||
REG_EAPSR = 119,
|
|
||||||
REG_XPSR = 120,
|
|
||||||
REG_EPSR = 121,
|
|
||||||
REG_IEPSR = 122,
|
|
||||||
REG_PRIMASK = 123,
|
|
||||||
REG_BASEPRI = 124,
|
|
||||||
REG_BASEPRI_MAX = 125,
|
|
||||||
REG_FAULTMASK = 126,
|
|
||||||
REG_APSR_NZCVQ = 127,
|
|
||||||
REG_APSR_G = 128,
|
|
||||||
REG_APSR_NZCVQG = 129,
|
|
||||||
REG_IAPSR_NZCVQ = 130,
|
|
||||||
REG_IAPSR_G = 131,
|
|
||||||
REG_IAPSR_NZCVQG = 132,
|
|
||||||
REG_EAPSR_NZCVQ = 133,
|
|
||||||
REG_EAPSR_G = 134,
|
|
||||||
REG_EAPSR_NZCVQG = 135,
|
|
||||||
REG_XPSR_NZCVQ = 136,
|
|
||||||
REG_XPSR_G = 137,
|
|
||||||
REG_XPSR_NZCVQG = 138,
|
|
||||||
REG_CP_REG = 139,
|
|
||||||
REG_ENDING = 140,
|
|
||||||
|
|
||||||
// alias registers
|
|
||||||
REG_R13 = 12,
|
|
||||||
REG_R14 = 10,
|
|
||||||
REG_R15 = 11,
|
|
||||||
REG_SB = 75,
|
|
||||||
REG_SL = 76,
|
|
||||||
REG_FP = 77,
|
|
||||||
REG_IP = 78,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,341 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Arm64
|
|
||||||
{
|
|
||||||
|
|
||||||
// ARM64 CPU
|
|
||||||
|
|
||||||
CPU_ARM64_A57 = 0,
|
|
||||||
CPU_ARM64_A53 = 1,
|
|
||||||
CPU_ARM64_A72 = 2,
|
|
||||||
CPU_ARM64_MAX = 3,
|
|
||||||
CPU_ARM64_ENDING = 4,
|
|
||||||
|
|
||||||
// ARM64 registers
|
|
||||||
|
|
||||||
REG_INVALID = 0,
|
|
||||||
REG_X29 = 1,
|
|
||||||
REG_X30 = 2,
|
|
||||||
REG_NZCV = 3,
|
|
||||||
REG_SP = 4,
|
|
||||||
REG_WSP = 5,
|
|
||||||
REG_WZR = 6,
|
|
||||||
REG_XZR = 7,
|
|
||||||
REG_B0 = 8,
|
|
||||||
REG_B1 = 9,
|
|
||||||
REG_B2 = 10,
|
|
||||||
REG_B3 = 11,
|
|
||||||
REG_B4 = 12,
|
|
||||||
REG_B5 = 13,
|
|
||||||
REG_B6 = 14,
|
|
||||||
REG_B7 = 15,
|
|
||||||
REG_B8 = 16,
|
|
||||||
REG_B9 = 17,
|
|
||||||
REG_B10 = 18,
|
|
||||||
REG_B11 = 19,
|
|
||||||
REG_B12 = 20,
|
|
||||||
REG_B13 = 21,
|
|
||||||
REG_B14 = 22,
|
|
||||||
REG_B15 = 23,
|
|
||||||
REG_B16 = 24,
|
|
||||||
REG_B17 = 25,
|
|
||||||
REG_B18 = 26,
|
|
||||||
REG_B19 = 27,
|
|
||||||
REG_B20 = 28,
|
|
||||||
REG_B21 = 29,
|
|
||||||
REG_B22 = 30,
|
|
||||||
REG_B23 = 31,
|
|
||||||
REG_B24 = 32,
|
|
||||||
REG_B25 = 33,
|
|
||||||
REG_B26 = 34,
|
|
||||||
REG_B27 = 35,
|
|
||||||
REG_B28 = 36,
|
|
||||||
REG_B29 = 37,
|
|
||||||
REG_B30 = 38,
|
|
||||||
REG_B31 = 39,
|
|
||||||
REG_D0 = 40,
|
|
||||||
REG_D1 = 41,
|
|
||||||
REG_D2 = 42,
|
|
||||||
REG_D3 = 43,
|
|
||||||
REG_D4 = 44,
|
|
||||||
REG_D5 = 45,
|
|
||||||
REG_D6 = 46,
|
|
||||||
REG_D7 = 47,
|
|
||||||
REG_D8 = 48,
|
|
||||||
REG_D9 = 49,
|
|
||||||
REG_D10 = 50,
|
|
||||||
REG_D11 = 51,
|
|
||||||
REG_D12 = 52,
|
|
||||||
REG_D13 = 53,
|
|
||||||
REG_D14 = 54,
|
|
||||||
REG_D15 = 55,
|
|
||||||
REG_D16 = 56,
|
|
||||||
REG_D17 = 57,
|
|
||||||
REG_D18 = 58,
|
|
||||||
REG_D19 = 59,
|
|
||||||
REG_D20 = 60,
|
|
||||||
REG_D21 = 61,
|
|
||||||
REG_D22 = 62,
|
|
||||||
REG_D23 = 63,
|
|
||||||
REG_D24 = 64,
|
|
||||||
REG_D25 = 65,
|
|
||||||
REG_D26 = 66,
|
|
||||||
REG_D27 = 67,
|
|
||||||
REG_D28 = 68,
|
|
||||||
REG_D29 = 69,
|
|
||||||
REG_D30 = 70,
|
|
||||||
REG_D31 = 71,
|
|
||||||
REG_H0 = 72,
|
|
||||||
REG_H1 = 73,
|
|
||||||
REG_H2 = 74,
|
|
||||||
REG_H3 = 75,
|
|
||||||
REG_H4 = 76,
|
|
||||||
REG_H5 = 77,
|
|
||||||
REG_H6 = 78,
|
|
||||||
REG_H7 = 79,
|
|
||||||
REG_H8 = 80,
|
|
||||||
REG_H9 = 81,
|
|
||||||
REG_H10 = 82,
|
|
||||||
REG_H11 = 83,
|
|
||||||
REG_H12 = 84,
|
|
||||||
REG_H13 = 85,
|
|
||||||
REG_H14 = 86,
|
|
||||||
REG_H15 = 87,
|
|
||||||
REG_H16 = 88,
|
|
||||||
REG_H17 = 89,
|
|
||||||
REG_H18 = 90,
|
|
||||||
REG_H19 = 91,
|
|
||||||
REG_H20 = 92,
|
|
||||||
REG_H21 = 93,
|
|
||||||
REG_H22 = 94,
|
|
||||||
REG_H23 = 95,
|
|
||||||
REG_H24 = 96,
|
|
||||||
REG_H25 = 97,
|
|
||||||
REG_H26 = 98,
|
|
||||||
REG_H27 = 99,
|
|
||||||
REG_H28 = 100,
|
|
||||||
REG_H29 = 101,
|
|
||||||
REG_H30 = 102,
|
|
||||||
REG_H31 = 103,
|
|
||||||
REG_Q0 = 104,
|
|
||||||
REG_Q1 = 105,
|
|
||||||
REG_Q2 = 106,
|
|
||||||
REG_Q3 = 107,
|
|
||||||
REG_Q4 = 108,
|
|
||||||
REG_Q5 = 109,
|
|
||||||
REG_Q6 = 110,
|
|
||||||
REG_Q7 = 111,
|
|
||||||
REG_Q8 = 112,
|
|
||||||
REG_Q9 = 113,
|
|
||||||
REG_Q10 = 114,
|
|
||||||
REG_Q11 = 115,
|
|
||||||
REG_Q12 = 116,
|
|
||||||
REG_Q13 = 117,
|
|
||||||
REG_Q14 = 118,
|
|
||||||
REG_Q15 = 119,
|
|
||||||
REG_Q16 = 120,
|
|
||||||
REG_Q17 = 121,
|
|
||||||
REG_Q18 = 122,
|
|
||||||
REG_Q19 = 123,
|
|
||||||
REG_Q20 = 124,
|
|
||||||
REG_Q21 = 125,
|
|
||||||
REG_Q22 = 126,
|
|
||||||
REG_Q23 = 127,
|
|
||||||
REG_Q24 = 128,
|
|
||||||
REG_Q25 = 129,
|
|
||||||
REG_Q26 = 130,
|
|
||||||
REG_Q27 = 131,
|
|
||||||
REG_Q28 = 132,
|
|
||||||
REG_Q29 = 133,
|
|
||||||
REG_Q30 = 134,
|
|
||||||
REG_Q31 = 135,
|
|
||||||
REG_S0 = 136,
|
|
||||||
REG_S1 = 137,
|
|
||||||
REG_S2 = 138,
|
|
||||||
REG_S3 = 139,
|
|
||||||
REG_S4 = 140,
|
|
||||||
REG_S5 = 141,
|
|
||||||
REG_S6 = 142,
|
|
||||||
REG_S7 = 143,
|
|
||||||
REG_S8 = 144,
|
|
||||||
REG_S9 = 145,
|
|
||||||
REG_S10 = 146,
|
|
||||||
REG_S11 = 147,
|
|
||||||
REG_S12 = 148,
|
|
||||||
REG_S13 = 149,
|
|
||||||
REG_S14 = 150,
|
|
||||||
REG_S15 = 151,
|
|
||||||
REG_S16 = 152,
|
|
||||||
REG_S17 = 153,
|
|
||||||
REG_S18 = 154,
|
|
||||||
REG_S19 = 155,
|
|
||||||
REG_S20 = 156,
|
|
||||||
REG_S21 = 157,
|
|
||||||
REG_S22 = 158,
|
|
||||||
REG_S23 = 159,
|
|
||||||
REG_S24 = 160,
|
|
||||||
REG_S25 = 161,
|
|
||||||
REG_S26 = 162,
|
|
||||||
REG_S27 = 163,
|
|
||||||
REG_S28 = 164,
|
|
||||||
REG_S29 = 165,
|
|
||||||
REG_S30 = 166,
|
|
||||||
REG_S31 = 167,
|
|
||||||
REG_W0 = 168,
|
|
||||||
REG_W1 = 169,
|
|
||||||
REG_W2 = 170,
|
|
||||||
REG_W3 = 171,
|
|
||||||
REG_W4 = 172,
|
|
||||||
REG_W5 = 173,
|
|
||||||
REG_W6 = 174,
|
|
||||||
REG_W7 = 175,
|
|
||||||
REG_W8 = 176,
|
|
||||||
REG_W9 = 177,
|
|
||||||
REG_W10 = 178,
|
|
||||||
REG_W11 = 179,
|
|
||||||
REG_W12 = 180,
|
|
||||||
REG_W13 = 181,
|
|
||||||
REG_W14 = 182,
|
|
||||||
REG_W15 = 183,
|
|
||||||
REG_W16 = 184,
|
|
||||||
REG_W17 = 185,
|
|
||||||
REG_W18 = 186,
|
|
||||||
REG_W19 = 187,
|
|
||||||
REG_W20 = 188,
|
|
||||||
REG_W21 = 189,
|
|
||||||
REG_W22 = 190,
|
|
||||||
REG_W23 = 191,
|
|
||||||
REG_W24 = 192,
|
|
||||||
REG_W25 = 193,
|
|
||||||
REG_W26 = 194,
|
|
||||||
REG_W27 = 195,
|
|
||||||
REG_W28 = 196,
|
|
||||||
REG_W29 = 197,
|
|
||||||
REG_W30 = 198,
|
|
||||||
REG_X0 = 199,
|
|
||||||
REG_X1 = 200,
|
|
||||||
REG_X2 = 201,
|
|
||||||
REG_X3 = 202,
|
|
||||||
REG_X4 = 203,
|
|
||||||
REG_X5 = 204,
|
|
||||||
REG_X6 = 205,
|
|
||||||
REG_X7 = 206,
|
|
||||||
REG_X8 = 207,
|
|
||||||
REG_X9 = 208,
|
|
||||||
REG_X10 = 209,
|
|
||||||
REG_X11 = 210,
|
|
||||||
REG_X12 = 211,
|
|
||||||
REG_X13 = 212,
|
|
||||||
REG_X14 = 213,
|
|
||||||
REG_X15 = 214,
|
|
||||||
REG_X16 = 215,
|
|
||||||
REG_X17 = 216,
|
|
||||||
REG_X18 = 217,
|
|
||||||
REG_X19 = 218,
|
|
||||||
REG_X20 = 219,
|
|
||||||
REG_X21 = 220,
|
|
||||||
REG_X22 = 221,
|
|
||||||
REG_X23 = 222,
|
|
||||||
REG_X24 = 223,
|
|
||||||
REG_X25 = 224,
|
|
||||||
REG_X26 = 225,
|
|
||||||
REG_X27 = 226,
|
|
||||||
REG_X28 = 227,
|
|
||||||
REG_V0 = 228,
|
|
||||||
REG_V1 = 229,
|
|
||||||
REG_V2 = 230,
|
|
||||||
REG_V3 = 231,
|
|
||||||
REG_V4 = 232,
|
|
||||||
REG_V5 = 233,
|
|
||||||
REG_V6 = 234,
|
|
||||||
REG_V7 = 235,
|
|
||||||
REG_V8 = 236,
|
|
||||||
REG_V9 = 237,
|
|
||||||
REG_V10 = 238,
|
|
||||||
REG_V11 = 239,
|
|
||||||
REG_V12 = 240,
|
|
||||||
REG_V13 = 241,
|
|
||||||
REG_V14 = 242,
|
|
||||||
REG_V15 = 243,
|
|
||||||
REG_V16 = 244,
|
|
||||||
REG_V17 = 245,
|
|
||||||
REG_V18 = 246,
|
|
||||||
REG_V19 = 247,
|
|
||||||
REG_V20 = 248,
|
|
||||||
REG_V21 = 249,
|
|
||||||
REG_V22 = 250,
|
|
||||||
REG_V23 = 251,
|
|
||||||
REG_V24 = 252,
|
|
||||||
REG_V25 = 253,
|
|
||||||
REG_V26 = 254,
|
|
||||||
REG_V27 = 255,
|
|
||||||
REG_V28 = 256,
|
|
||||||
REG_V29 = 257,
|
|
||||||
REG_V30 = 258,
|
|
||||||
REG_V31 = 259,
|
|
||||||
|
|
||||||
// pseudo registers
|
|
||||||
REG_PC = 260,
|
|
||||||
REG_CPACR_EL1 = 261,
|
|
||||||
|
|
||||||
// thread registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
|
||||||
REG_TPIDR_EL0 = 262,
|
|
||||||
REG_TPIDRRO_EL0 = 263,
|
|
||||||
REG_TPIDR_EL1 = 264,
|
|
||||||
REG_PSTATE = 265,
|
|
||||||
|
|
||||||
// exception link registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
|
||||||
REG_ELR_EL0 = 266,
|
|
||||||
REG_ELR_EL1 = 267,
|
|
||||||
REG_ELR_EL2 = 268,
|
|
||||||
REG_ELR_EL3 = 269,
|
|
||||||
|
|
||||||
// stack pointers registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
|
||||||
REG_SP_EL0 = 270,
|
|
||||||
REG_SP_EL1 = 271,
|
|
||||||
REG_SP_EL2 = 272,
|
|
||||||
REG_SP_EL3 = 273,
|
|
||||||
|
|
||||||
// other CP15 registers, depreciated, use UC_ARM64_REG_CP_REG instead
|
|
||||||
REG_TTBR0_EL1 = 274,
|
|
||||||
REG_TTBR1_EL1 = 275,
|
|
||||||
REG_ESR_EL0 = 276,
|
|
||||||
REG_ESR_EL1 = 277,
|
|
||||||
REG_ESR_EL2 = 278,
|
|
||||||
REG_ESR_EL3 = 279,
|
|
||||||
REG_FAR_EL0 = 280,
|
|
||||||
REG_FAR_EL1 = 281,
|
|
||||||
REG_FAR_EL2 = 282,
|
|
||||||
REG_FAR_EL3 = 283,
|
|
||||||
REG_PAR_EL1 = 284,
|
|
||||||
REG_MAIR_EL1 = 285,
|
|
||||||
REG_VBAR_EL0 = 286,
|
|
||||||
REG_VBAR_EL1 = 287,
|
|
||||||
REG_VBAR_EL2 = 288,
|
|
||||||
REG_VBAR_EL3 = 289,
|
|
||||||
REG_CP_REG = 290,
|
|
||||||
|
|
||||||
// floating point control and status registers
|
|
||||||
REG_FPCR = 291,
|
|
||||||
REG_FPSR = 292,
|
|
||||||
REG_ENDING = 293,
|
|
||||||
|
|
||||||
// alias registers
|
|
||||||
REG_IP0 = 215,
|
|
||||||
REG_IP1 = 216,
|
|
||||||
REG_FP = 1,
|
|
||||||
REG_LR = 2,
|
|
||||||
|
|
||||||
// ARM64 instructions
|
|
||||||
|
|
||||||
INS_INVALID = 0,
|
|
||||||
INS_MRS = 1,
|
|
||||||
INS_MSR = 2,
|
|
||||||
INS_SYS = 3,
|
|
||||||
INS_SYSL = 4,
|
|
||||||
INS_ENDING = 5,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Common
|
|
||||||
{
|
|
||||||
API_MAJOR = 2,
|
|
||||||
|
|
||||||
API_MINOR = 0,
|
|
||||||
|
|
||||||
API_PATCH = 0,
|
|
||||||
API_EXTRA = 255,
|
|
||||||
VERSION_MAJOR = 2,
|
|
||||||
|
|
||||||
VERSION_MINOR = 0,
|
|
||||||
|
|
||||||
VERSION_PATCH = 0,
|
|
||||||
VERSION_EXTRA = 255,
|
|
||||||
SECOND_SCALE = 1000000,
|
|
||||||
MILISECOND_SCALE = 1000,
|
|
||||||
QUERY_MODE = 1,
|
|
||||||
QUERY_PAGE_SIZE = 2,
|
|
||||||
QUERY_ARCH = 3,
|
|
||||||
QUERY_TIMEOUT = 4,
|
|
||||||
|
|
||||||
CTL_IO_NONE = 0,
|
|
||||||
CTL_IO_WRITE = 1,
|
|
||||||
CTL_IO_READ = 2,
|
|
||||||
CTL_IO_READ_WRITE = 3,
|
|
||||||
|
|
||||||
CTL_UC_MODE = 0,
|
|
||||||
CTL_UC_PAGE_SIZE = 1,
|
|
||||||
CTL_UC_ARCH = 2,
|
|
||||||
CTL_UC_TIMEOUT = 3,
|
|
||||||
CTL_UC_USE_EXITS = 4,
|
|
||||||
CTL_UC_EXITS_CNT = 5,
|
|
||||||
CTL_UC_EXITS = 6,
|
|
||||||
CTL_CPU_MODEL = 7,
|
|
||||||
CTL_TB_REQUEST_CACHE = 8,
|
|
||||||
CTL_TB_REMOVE_CACHE = 9,
|
|
||||||
CTL_TB_FLUSH = 10,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Error
|
|
||||||
{
|
|
||||||
OK = 0,
|
|
||||||
NOMEM = 1,
|
|
||||||
ARCH = 2,
|
|
||||||
HANDLE = 3,
|
|
||||||
MODE = 4,
|
|
||||||
VERSION = 5,
|
|
||||||
READ_UNMAPPED = 6,
|
|
||||||
WRITE_UNMAPPED = 7,
|
|
||||||
FETCH_UNMAPPED = 8,
|
|
||||||
HOOK = 9,
|
|
||||||
INSN_INVALID = 10,
|
|
||||||
MAP = 11,
|
|
||||||
WRITE_PROT = 12,
|
|
||||||
READ_PROT = 13,
|
|
||||||
FETCH_PROT = 14,
|
|
||||||
ARG = 15,
|
|
||||||
READ_UNALIGNED = 16,
|
|
||||||
WRITE_UNALIGNED = 17,
|
|
||||||
FETCH_UNALIGNED = 18,
|
|
||||||
HOOK_EXIST = 19,
|
|
||||||
RESOURCE = 20,
|
|
||||||
EXCEPTION = 21,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Hook
|
|
||||||
{
|
|
||||||
INTR = 1,
|
|
||||||
INSN = 2,
|
|
||||||
CODE = 4,
|
|
||||||
BLOCK = 8,
|
|
||||||
MEM_READ_UNMAPPED = 16,
|
|
||||||
MEM_WRITE_UNMAPPED = 32,
|
|
||||||
MEM_FETCH_UNMAPPED = 64,
|
|
||||||
MEM_READ_PROT = 128,
|
|
||||||
MEM_WRITE_PROT = 256,
|
|
||||||
MEM_FETCH_PROT = 512,
|
|
||||||
MEM_READ = 1024,
|
|
||||||
MEM_WRITE = 2048,
|
|
||||||
MEM_FETCH = 4096,
|
|
||||||
MEM_READ_AFTER = 8192,
|
|
||||||
INSN_INVALID = 16384,
|
|
||||||
EDGE_GENERATED = 32768,
|
|
||||||
TCG_OPCODE = 65536,
|
|
||||||
MEM_UNMAPPED = 112,
|
|
||||||
MEM_PROT = 896,
|
|
||||||
MEM_READ_INVALID = 144,
|
|
||||||
MEM_WRITE_INVALID = 288,
|
|
||||||
MEM_FETCH_INVALID = 576,
|
|
||||||
MEM_INVALID = 1008,
|
|
||||||
MEM_VALID = 7168,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Memory
|
|
||||||
{
|
|
||||||
READ = 16,
|
|
||||||
WRITE = 17,
|
|
||||||
FETCH = 18,
|
|
||||||
READ_UNMAPPED = 19,
|
|
||||||
WRITE_UNMAPPED = 20,
|
|
||||||
FETCH_UNMAPPED = 21,
|
|
||||||
WRITE_PROT = 22,
|
|
||||||
READ_PROT = 23,
|
|
||||||
FETCH_PROT = 24,
|
|
||||||
READ_AFTER = 25,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Mode
|
|
||||||
{
|
|
||||||
LITTLE_ENDIAN = 0,
|
|
||||||
BIG_ENDIAN = 1073741824,
|
|
||||||
ARM = 0,
|
|
||||||
THUMB = 16,
|
|
||||||
MCLASS = 32,
|
|
||||||
V8 = 64,
|
|
||||||
ARMBE8 = 1024,
|
|
||||||
ARM926 = 128,
|
|
||||||
ARM946 = 256,
|
|
||||||
ARM1176 = 512,
|
|
||||||
MICRO = 16,
|
|
||||||
MIPS3 = 32,
|
|
||||||
MIPS32R6 = 64,
|
|
||||||
MIPS32 = 4,
|
|
||||||
MIPS64 = 8,
|
|
||||||
MODE_16 = 2,
|
|
||||||
MODE_32 = 4,
|
|
||||||
MODE_64 = 8,
|
|
||||||
PPC32 = 4,
|
|
||||||
PPC64 = 8,
|
|
||||||
QPX = 16,
|
|
||||||
SPARC32 = 4,
|
|
||||||
SPARC64 = 8,
|
|
||||||
V9 = 16,
|
|
||||||
RISCV32 = 4,
|
|
||||||
RISCV64 = 8,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum Permission
|
|
||||||
{
|
|
||||||
NONE = 0,
|
|
||||||
READ = 1,
|
|
||||||
WRITE = 2,
|
|
||||||
EXEC = 4,
|
|
||||||
ALL = 7,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
// Constants for Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native.Const
|
|
||||||
{
|
|
||||||
public enum TCG
|
|
||||||
{
|
|
||||||
OP_SUB = 0,
|
|
||||||
OP_FLAG_CMP = 1,
|
|
||||||
OP_FLAG_DIRECT = 2,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
using Ryujinx.Tests.Unicorn.Native.Const;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native
|
|
||||||
{
|
|
||||||
public static partial class Interface
|
|
||||||
{
|
|
||||||
public static bool IsUnicornAvailable { get; private set; } = true;
|
|
||||||
|
|
||||||
private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
|
||||||
{
|
|
||||||
if (libraryName == "unicorn")
|
|
||||||
{
|
|
||||||
string loadPath = $"{Path.GetDirectoryName(assembly.Location)}/";
|
|
||||||
loadPath += OperatingSystem.IsWindows() ? $"{libraryName}.dll" : $"lib{libraryName}.so";
|
|
||||||
|
|
||||||
if (!NativeLibrary.TryLoad(loadPath, out IntPtr libraryPtr))
|
|
||||||
{
|
|
||||||
IsUnicornAvailable = false;
|
|
||||||
Console.Error.WriteLine($"ERROR: Could not find unicorn at: {loadPath}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return libraryPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, fallback to default import resolver.
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Interface()
|
|
||||||
{
|
|
||||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), ImportResolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Checked(Error error)
|
|
||||||
{
|
|
||||||
if (error != Error.OK)
|
|
||||||
{
|
|
||||||
throw new UnicornException(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MarshalArrayOf<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(IntPtr input, int length, out T[] output)
|
|
||||||
{
|
|
||||||
int size = Marshal.SizeOf<T>();
|
|
||||||
|
|
||||||
output = new T[length];
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
IntPtr item = new IntPtr(input.ToInt64() + i * size);
|
|
||||||
|
|
||||||
output[i] = Marshal.PtrToStructure<T>(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial uint uc_version(out uint major, out uint minor);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_open(Arch arch, Mode mode, out IntPtr uc);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_close(IntPtr uc);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial IntPtr uc_strerror(Error err);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_reg_write(IntPtr uc, int regid, byte[] value);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_reg_read(IntPtr uc, int regid, byte[] value);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_unmap(IntPtr uc, ulong address, ulong size);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms);
|
|
||||||
|
|
||||||
[LibraryImport("unicorn")]
|
|
||||||
public static partial Error uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Unicorn.Native
|
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct UnicornMemoryRegion
|
|
||||||
{
|
|
||||||
public UInt64 begin; // begin address of the region (inclusive)
|
|
||||||
public UInt64 end; // end address of the region (inclusive)
|
|
||||||
public UInt32 perms; // memory permissions of the region
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user