mirror of
https://git.zaroz.cloud/nintendo-back-up/Ryujinx.git
synced 2025-12-18 08:22:41 +00:00
Compare commits
298 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
c14844d12c | ||
|
|
7fea26e97e | ||
|
|
7b7f62c776 | ||
|
|
423dbc8888 | ||
|
|
6adf15e479 | ||
|
|
2747f12591 | ||
|
|
a47824f961 | ||
|
|
8474d52778 | ||
|
|
dd7a924596 | ||
|
|
a76eaf9a9a | ||
|
|
009e6bcd1b | ||
|
|
eb2cc159fa | ||
|
|
bb89e36fd8 | ||
|
|
de3134adbe | ||
|
|
36d53819a4 | ||
|
|
ae4324032a | ||
|
|
f449895e6d | ||
|
|
410be95ab6 | ||
|
|
cff9046fc7 | ||
|
|
86fd0643c2 | ||
|
|
43a83a401e | ||
|
|
f0e27a23a5 | ||
|
|
e68650237d | ||
|
|
1faff14e73 | ||
|
|
784cf9d594 | ||
|
|
64263c5218 | ||
|
|
065c4e520d | ||
|
|
139a930407 | ||
|
|
719dc97bbd | ||
|
|
41bba5310a | ||
|
|
8071c8c8c0 | ||
|
|
b402b4e7f6 | ||
|
|
93df366b2c | ||
|
|
cd3a15aea5 | ||
|
|
070136b3f7 | ||
|
|
08ab47c6c0 | ||
|
|
85faa9d8fa | ||
|
|
dca5b14493 | ||
|
|
4d2c8e2a44 | ||
|
|
8fa248ceb4 | ||
|
|
30862b5ffd | ||
|
|
9f57747c57 | ||
|
|
fe29a2ff6e | ||
|
|
e9a173e00c | ||
|
|
a11784fcbf | ||
|
|
fd36c8deca | ||
|
|
70638340b3 | ||
|
|
4b495f3333 | ||
|
|
934b5a64e5 | ||
|
|
cee667b491 | ||
|
|
94a64f2aea | ||
|
|
2355c2af62 | ||
|
|
5e0f8e8738 | ||
|
|
d16288a2a8 | ||
|
|
600f86dc7b | ||
|
|
7210c17c5e | ||
|
|
a16854e55a | ||
|
|
3e455a90a1 | ||
|
|
e4413542b2 | ||
|
|
8c720783f5 | ||
|
|
8734ea9dd4 | ||
|
|
c586e6d2b7 | ||
|
|
3a4eeb77fe | ||
|
|
51b3953cfc | ||
|
|
610eecc1c1 |
@@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# Namespace preferences
|
||||
csharp_style_namespace_declarations = block_scoped:warning
|
||||
resharper_csharp_namespace_body = block_scoped
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = false:silent
|
||||
csharp_style_var_for_built_in_types = false:silent
|
||||
|
||||
61
.gitattributes
vendored
61
.gitattributes
vendored
@@ -1,63 +1,4 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
||||
* text=auto eol=lf
|
||||
|
||||
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
|
||||
104
.github/workflows/build.yml
vendored
104
.github/workflows/build.yml
vendored
@@ -18,10 +18,16 @@ on:
|
||||
- '*.yml'
|
||||
- 'README.md'
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1.0"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.os }} (${{ matrix.configuration }})
|
||||
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 35
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
@@ -33,7 +39,7 @@ jobs:
|
||||
RELEASE_ZIP_OS_NAME: linux_x64
|
||||
|
||||
- os: macOS-latest
|
||||
OS_NAME: MacOS x64
|
||||
OS_NAME: macOS x64
|
||||
DOTNET_RUNTIME_IDENTIFIER: osx-x64
|
||||
RELEASE_ZIP_OS_NAME: osx_x64
|
||||
|
||||
@@ -43,53 +49,107 @@ jobs:
|
||||
RELEASE_ZIP_OS_NAME: win_x64
|
||||
|
||||
fail-fast: false
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1.0"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Get git short hash
|
||||
id: git_short_hash
|
||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- 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
|
||||
|
||||
- name: Test
|
||||
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||
|
||||
- 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
|
||||
if: github.event_name == 'pull_request'
|
||||
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' && matrix.os != 'macOS-latest'
|
||||
|
||||
- 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
|
||||
if: github.event_name == 'pull_request'
|
||||
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' && matrix.os != 'macOS-latest'
|
||||
|
||||
- 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
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Rename Avalonia (Windows)
|
||||
run: mv ./publish_ava/Ryujinx.Ava.exe ./publish_ava/Ryujinx.exe
|
||||
if: runner.os == 'Windows' && github.event_name == 'pull_request'
|
||||
- name: Rename Avalonia (Unix)
|
||||
run: mv ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx
|
||||
if: runner.os != 'Windows' && github.event_name == 'pull_request'
|
||||
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' && 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
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish
|
||||
if: github.event_name == 'pull_request'
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
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
|
||||
if: github.event_name == 'pull_request'
|
||||
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
|
||||
|
||||
- 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 }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
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: 35
|
||||
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: 35
|
||||
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:
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 35
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
@@ -65,4 +66,4 @@ jobs:
|
||||
} else {
|
||||
core.info(`Creating a comment`);
|
||||
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
||||
}
|
||||
}
|
||||
189
.github/workflows/release.yml
vendored
189
.github/workflows/release.yml
vendored
@@ -13,88 +13,117 @@ on:
|
||||
|
||||
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:
|
||||
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@v5
|
||||
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:
|
||||
runs-on: windows-latest
|
||||
|
||||
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"
|
||||
name: Release ${{ matrix.OS_NAME }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 35
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
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:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
global-json-file: global.json
|
||||
|
||||
- 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
|
||||
shell: bash
|
||||
|
||||
- name: Configure for release
|
||||
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_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' 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_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' 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_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: Create output dir
|
||||
run: "mkdir release_output"
|
||||
|
||||
- name: Publish Windows
|
||||
- name: Publish
|
||||
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 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 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
|
||||
mv ./publish_windows_ava/publish/Ryujinx.Ava.exe ./publish_windows_ava/publish/Ryujinx.exe
|
||||
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 "${{ 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 "${{ 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
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
pushd publish_windows
|
||||
pushd publish_gtk
|
||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
|
||||
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
|
||||
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
|
||||
popd
|
||||
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
|
||||
mv ./publish_linux_ava/publish/Ryujinx.Ava ./publish_linux_ava/publish/Ryujinx
|
||||
- name: Packing Linux builds
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
pushd publish_linux
|
||||
tar --exclude "publish/Ryujinx" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
|
||||
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
|
||||
pushd publish_gtk
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
|
||||
pushd publish_linux_sdl2_headless
|
||||
tar --exclude "publish/Ryujinx.Headless.SDL2" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
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"
|
||||
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
|
||||
pushd publish_sdl2_headless
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
|
||||
pushd publish_linux_ava
|
||||
tar --exclude "publish/Ryujinx" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
|
||||
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
|
||||
pushd publish_ava
|
||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava
|
||||
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
@@ -104,10 +133,78 @@ jobs:
|
||||
name: ${{ steps.version_info.outputs.build_version }}
|
||||
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
||||
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
|
||||
removeArtifacts: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
macos_release:
|
||||
name: Release MacOS universal
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 35
|
||||
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
|
||||
PublishProfiles/
|
||||
|
||||
# Glade backup files
|
||||
*.glade~
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace ARMeilleure.Decoders;
|
||||
|
||||
interface IOpCode32Exception
|
||||
{
|
||||
int Id { get; }
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
enum Intrinsic : ushort
|
||||
{
|
||||
X86Addpd,
|
||||
X86Addps,
|
||||
X86Addsd,
|
||||
X86Addss,
|
||||
X86Aesdec,
|
||||
X86Aesdeclast,
|
||||
X86Aesenc,
|
||||
X86Aesenclast,
|
||||
X86Aesimc,
|
||||
X86Andnpd,
|
||||
X86Andnps,
|
||||
X86Andpd,
|
||||
X86Andps,
|
||||
X86Blendvpd,
|
||||
X86Blendvps,
|
||||
X86Cmppd,
|
||||
X86Cmpps,
|
||||
X86Cmpsd,
|
||||
X86Cmpss,
|
||||
X86Comisdeq,
|
||||
X86Comisdge,
|
||||
X86Comisdlt,
|
||||
X86Comisseq,
|
||||
X86Comissge,
|
||||
X86Comisslt,
|
||||
X86Crc32,
|
||||
X86Crc32_16,
|
||||
X86Crc32_8,
|
||||
X86Cvtdq2pd,
|
||||
X86Cvtdq2ps,
|
||||
X86Cvtpd2dq,
|
||||
X86Cvtpd2ps,
|
||||
X86Cvtps2dq,
|
||||
X86Cvtps2pd,
|
||||
X86Cvtsd2si,
|
||||
X86Cvtsd2ss,
|
||||
X86Cvtsi2sd,
|
||||
X86Cvtsi2si,
|
||||
X86Cvtsi2ss,
|
||||
X86Cvtss2sd,
|
||||
X86Cvtss2si,
|
||||
X86Divpd,
|
||||
X86Divps,
|
||||
X86Divsd,
|
||||
X86Divss,
|
||||
X86Gf2p8affineqb,
|
||||
X86Haddpd,
|
||||
X86Haddps,
|
||||
X86Insertps,
|
||||
X86Maxpd,
|
||||
X86Maxps,
|
||||
X86Maxsd,
|
||||
X86Maxss,
|
||||
X86Minpd,
|
||||
X86Minps,
|
||||
X86Minsd,
|
||||
X86Minss,
|
||||
X86Movhlps,
|
||||
X86Movlhps,
|
||||
X86Movss,
|
||||
X86Mulpd,
|
||||
X86Mulps,
|
||||
X86Mulsd,
|
||||
X86Mulss,
|
||||
X86Mxcsrmb,
|
||||
X86Mxcsrub,
|
||||
X86Paddb,
|
||||
X86Paddd,
|
||||
X86Paddq,
|
||||
X86Paddw,
|
||||
X86Palignr,
|
||||
X86Pand,
|
||||
X86Pandn,
|
||||
X86Pavgb,
|
||||
X86Pavgw,
|
||||
X86Pblendvb,
|
||||
X86Pclmulqdq,
|
||||
X86Pcmpeqb,
|
||||
X86Pcmpeqd,
|
||||
X86Pcmpeqq,
|
||||
X86Pcmpeqw,
|
||||
X86Pcmpgtb,
|
||||
X86Pcmpgtd,
|
||||
X86Pcmpgtq,
|
||||
X86Pcmpgtw,
|
||||
X86Pmaxsb,
|
||||
X86Pmaxsd,
|
||||
X86Pmaxsw,
|
||||
X86Pmaxub,
|
||||
X86Pmaxud,
|
||||
X86Pmaxuw,
|
||||
X86Pminsb,
|
||||
X86Pminsd,
|
||||
X86Pminsw,
|
||||
X86Pminub,
|
||||
X86Pminud,
|
||||
X86Pminuw,
|
||||
X86Pmovsxbw,
|
||||
X86Pmovsxdq,
|
||||
X86Pmovsxwd,
|
||||
X86Pmovzxbw,
|
||||
X86Pmovzxdq,
|
||||
X86Pmovzxwd,
|
||||
X86Pmulld,
|
||||
X86Pmullw,
|
||||
X86Popcnt,
|
||||
X86Por,
|
||||
X86Pshufb,
|
||||
X86Pshufd,
|
||||
X86Pslld,
|
||||
X86Pslldq,
|
||||
X86Psllq,
|
||||
X86Psllw,
|
||||
X86Psrad,
|
||||
X86Psraw,
|
||||
X86Psrld,
|
||||
X86Psrlq,
|
||||
X86Psrldq,
|
||||
X86Psrlw,
|
||||
X86Psubb,
|
||||
X86Psubd,
|
||||
X86Psubq,
|
||||
X86Psubw,
|
||||
X86Punpckhbw,
|
||||
X86Punpckhdq,
|
||||
X86Punpckhqdq,
|
||||
X86Punpckhwd,
|
||||
X86Punpcklbw,
|
||||
X86Punpckldq,
|
||||
X86Punpcklqdq,
|
||||
X86Punpcklwd,
|
||||
X86Pxor,
|
||||
X86Rcpps,
|
||||
X86Rcpss,
|
||||
X86Roundpd,
|
||||
X86Roundps,
|
||||
X86Roundsd,
|
||||
X86Roundss,
|
||||
X86Rsqrtps,
|
||||
X86Rsqrtss,
|
||||
X86Sha256Msg1,
|
||||
X86Sha256Msg2,
|
||||
X86Sha256Rnds2,
|
||||
X86Shufpd,
|
||||
X86Shufps,
|
||||
X86Sqrtpd,
|
||||
X86Sqrtps,
|
||||
X86Sqrtsd,
|
||||
X86Sqrtss,
|
||||
X86Subpd,
|
||||
X86Subps,
|
||||
X86Subsd,
|
||||
X86Subss,
|
||||
X86Unpckhpd,
|
||||
X86Unpckhps,
|
||||
X86Unpcklpd,
|
||||
X86Unpcklps,
|
||||
X86Vcvtph2ps,
|
||||
X86Vcvtps2ph,
|
||||
X86Vfmadd231ps,
|
||||
X86Vfmadd231sd,
|
||||
X86Vfmadd231ss,
|
||||
X86Vfmsub231sd,
|
||||
X86Vfmsub231ss,
|
||||
X86Vfnmadd231ps,
|
||||
X86Vfnmadd231sd,
|
||||
X86Vfnmadd231ss,
|
||||
X86Vfnmsub231sd,
|
||||
X86Vfnmsub231ss,
|
||||
X86Xorpd,
|
||||
X86Xorps
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using ARMeilleure.CodeGen.X86;
|
||||
|
||||
namespace ARMeilleure
|
||||
{
|
||||
public static class Optimizations
|
||||
{
|
||||
public static bool FastFP { get; set; } = true;
|
||||
|
||||
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
||||
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
||||
|
||||
public static bool UseSseIfAvailable { get; set; } = true;
|
||||
public static bool UseSse2IfAvailable { get; set; } = true;
|
||||
public static bool UseSse3IfAvailable { get; set; } = true;
|
||||
public static bool UseSsse3IfAvailable { get; set; } = true;
|
||||
public static bool UseSse41IfAvailable { get; set; } = true;
|
||||
public static bool UseSse42IfAvailable { get; set; } = true;
|
||||
public static bool UsePopCntIfAvailable { get; set; } = true;
|
||||
public static bool UseAvxIfAvailable { get; set; } = true;
|
||||
public static bool UseF16cIfAvailable { get; set; } = true;
|
||||
public static bool UseFmaIfAvailable { get; set; } = true;
|
||||
public static bool UseAesniIfAvailable { get; set; } = true;
|
||||
public static bool UsePclmulqdqIfAvailable { get; set; } = true;
|
||||
public static bool UseShaIfAvailable { get; set; } = true;
|
||||
public static bool UseGfniIfAvailable { get; set; } = true;
|
||||
|
||||
public static bool ForceLegacySse
|
||||
{
|
||||
get => HardwareCapabilities.ForceLegacySse;
|
||||
set => HardwareCapabilities.ForceLegacySse = value;
|
||||
}
|
||||
|
||||
internal static bool UseSse => UseSseIfAvailable && HardwareCapabilities.SupportsSse;
|
||||
internal static bool UseSse2 => UseSse2IfAvailable && HardwareCapabilities.SupportsSse2;
|
||||
internal static bool UseSse3 => UseSse3IfAvailable && HardwareCapabilities.SupportsSse3;
|
||||
internal static bool UseSsse3 => UseSsse3IfAvailable && HardwareCapabilities.SupportsSsse3;
|
||||
internal static bool UseSse41 => UseSse41IfAvailable && HardwareCapabilities.SupportsSse41;
|
||||
internal static bool UseSse42 => UseSse42IfAvailable && HardwareCapabilities.SupportsSse42;
|
||||
internal static bool UsePopCnt => UsePopCntIfAvailable && HardwareCapabilities.SupportsPopcnt;
|
||||
internal static bool UseAvx => UseAvxIfAvailable && HardwareCapabilities.SupportsAvx && !ForceLegacySse;
|
||||
internal static bool UseF16c => UseF16cIfAvailable && HardwareCapabilities.SupportsF16c;
|
||||
internal static bool UseFma => UseFmaIfAvailable && HardwareCapabilities.SupportsFma;
|
||||
internal static bool UseAesni => UseAesniIfAvailable && HardwareCapabilities.SupportsAesni;
|
||||
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && HardwareCapabilities.SupportsPclmulqdq;
|
||||
internal static bool UseSha => UseShaIfAvailable && HardwareCapabilities.SupportsSha;
|
||||
internal static bool UseGfni => UseGfniIfAvailable && HardwareCapabilities.SupportsGfni;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<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="DynamicData" Version="7.12.11" />
|
||||
<PackageVersion Include="DynamicData" Version="7.13.8" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
||||
<PackageVersion Include="LibHac" Version="0.17.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||
<PackageVersion Include="LibHac" Version="0.18.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
<PackageVersion Include="OpenTK.Core" Version="4.7.5" />
|
||||
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
|
||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" />
|
||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
||||
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
|
||||
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
|
||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
|
||||
<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.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<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="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.Extensions.EXT" 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="SPB" Version="0.0.4-build28" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" />
|
||||
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Management" Version="7.0.1" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
## 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!
|
||||
|
||||
## Usage
|
||||
@@ -96,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
||||
|
||||
- **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**
|
||||
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Ryujinx.Ava.Common.Locale
|
||||
{
|
||||
class LocaleManager : BaseModel
|
||||
{
|
||||
private const string DefaultLanguageCode = "en_US";
|
||||
|
||||
private Dictionary<LocaleKeys, string> _localeStrings;
|
||||
private ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
|
||||
|
||||
public static LocaleManager Instance { get; } = new LocaleManager();
|
||||
public Dictionary<LocaleKeys, string> LocaleStrings { get => _localeStrings; set => _localeStrings = value; }
|
||||
|
||||
|
||||
public LocaleManager()
|
||||
{
|
||||
_localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
string localeLanguageCode = CultureInfo.CurrentCulture.Name.Replace('-', '_');
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ConfigurationState.Instance.Ui.LanguageCode.Value))
|
||||
{
|
||||
localeLanguageCode = ConfigurationState.Instance.Ui.LanguageCode.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Load english first, if the target language translation is incomplete, we default to english.
|
||||
LoadDefaultLanguage();
|
||||
|
||||
if (localeLanguageCode != DefaultLanguageCode)
|
||||
{
|
||||
LoadLanguage(localeLanguageCode);
|
||||
}
|
||||
}
|
||||
|
||||
public string this[LocaleKeys key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_localeStrings.TryGetValue(key, out string value))
|
||||
{
|
||||
if (_dynamicValues.TryGetValue(key, out var dynamicValue))
|
||||
{
|
||||
return string.Format(value, dynamicValue);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return key.ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
_localeStrings[key] = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateDynamicValue(LocaleKeys key, params object[] values)
|
||||
{
|
||||
_dynamicValues[key] = values;
|
||||
|
||||
OnPropertyChanged("Item");
|
||||
}
|
||||
|
||||
public void LoadDefaultLanguage()
|
||||
{
|
||||
LoadLanguage(DefaultLanguageCode);
|
||||
}
|
||||
|
||||
public void LoadLanguage(string languageCode)
|
||||
{
|
||||
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
||||
|
||||
if (languageJson == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
||||
|
||||
foreach (var item in strings)
|
||||
{
|
||||
if (Enum.TryParse<LocaleKeys>(item.Key, out var key))
|
||||
{
|
||||
this[key] = item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
ConfigurationState.Instance.Ui.LanguageCode.Value = languageCode;
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Runtime.InteropServices;
|
||||
using Avalonia;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helper
|
||||
{
|
||||
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,714 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
using ICSharpCode.SharpZipLib.Tar;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Modules
|
||||
{
|
||||
internal static class Updater
|
||||
{
|
||||
private const string GitHubApiURL = "https://api.github.com";
|
||||
internal static bool Running;
|
||||
|
||||
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||
private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish");
|
||||
private static readonly int ConnectionCount = 4;
|
||||
|
||||
private static string _buildVer;
|
||||
private static string _platformExt;
|
||||
private static string _buildUrl;
|
||||
private static long _buildSize;
|
||||
|
||||
private static readonly string[] WindowsDependencyDirs = Array.Empty<string>();
|
||||
|
||||
public static bool UpdateSuccessful { get; private set; }
|
||||
|
||||
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate)
|
||||
{
|
||||
if (Running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Running = true;
|
||||
mainWindow.ViewModel.CanUpdate = false;
|
||||
|
||||
// Detect current platform
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
_platformExt = "osx_x64.zip";
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
_platformExt = "win_x64.zip";
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
_platformExt = "linux_x64.tar.gz";
|
||||
}
|
||||
|
||||
Version newVersion;
|
||||
Version currentVersion;
|
||||
|
||||
try
|
||||
{
|
||||
currentVersion = Version.Parse(Program.Version);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get latest version number from GitHub API
|
||||
try
|
||||
{
|
||||
using (HttpClient jsonClient = ConstructHttpClient())
|
||||
{
|
||||
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||
|
||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
||||
JObject jsonRoot = JObject.Parse(fetchedJson);
|
||||
JToken assets = jsonRoot["assets"];
|
||||
|
||||
_buildVer = (string)jsonRoot["name"];
|
||||
|
||||
foreach (JToken asset in assets)
|
||||
{
|
||||
string assetName = (string)asset["name"];
|
||||
string assetState = (string)asset["state"];
|
||||
string downloadURL = (string)asset["browser_download_url"];
|
||||
|
||||
if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt))
|
||||
{
|
||||
_buildUrl = downloadURL;
|
||||
|
||||
if (assetState != "uploaded")
|
||||
{
|
||||
if (showVersionUpToDate)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If build not done, assume no new update are availaible.
|
||||
if (_buildUrl == null)
|
||||
{
|
||||
if (showVersionUpToDate)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, exception.Message);
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
newVersion = Version.Parse(_buildVer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (newVersion <= currentVersion)
|
||||
{
|
||||
if (showVersionUpToDate)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
||||
});
|
||||
}
|
||||
|
||||
Running = false;
|
||||
mainWindow.ViewModel.CanUpdate = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch build size information to learn chunk sizes.
|
||||
using (HttpClient buildSizeClient = ConstructHttpClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
|
||||
|
||||
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
_buildSize = message.Content.Headers.ContentRange.Length.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
|
||||
|
||||
_buildSize = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
// Show a message asking the user if they want to update
|
||||
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage],
|
||||
$"{Program.Version} -> {newVersion}");
|
||||
|
||||
if (shouldUpdate)
|
||||
{
|
||||
UpdateRyujinx(mainWindow, _buildUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static HttpClient ConstructHttpClient()
|
||||
{
|
||||
HttpClient result = new HttpClient();
|
||||
|
||||
// Required by GitHub to interract with APIs.
|
||||
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async void UpdateRyujinx(Window parent, string downloadUrl)
|
||||
{
|
||||
UpdateSuccessful = false;
|
||||
|
||||
// Empty update dir, although it shouldn't ever have anything inside it
|
||||
if (Directory.Exists(UpdateDir))
|
||||
{
|
||||
Directory.Delete(UpdateDir, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(UpdateDir);
|
||||
|
||||
string updateFile = Path.Combine(UpdateDir, "update.bin");
|
||||
|
||||
var taskDialog = new TaskDialog()
|
||||
{
|
||||
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
|
||||
Buttons = { },
|
||||
ShowProgressBar = true
|
||||
};
|
||||
|
||||
taskDialog.XamlRoot = parent;
|
||||
|
||||
taskDialog.Opened += (s, e) =>
|
||||
{
|
||||
if (_buildSize >= 0)
|
||||
{
|
||||
DoUpdateWithMultipleThreads(taskDialog, downloadUrl, updateFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
|
||||
}
|
||||
};
|
||||
|
||||
await taskDialog.ShowAsync(true);
|
||||
|
||||
if (UpdateSuccessful)
|
||||
{
|
||||
var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]);
|
||||
|
||||
if (shouldRestart)
|
||||
{
|
||||
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
||||
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||
|
||||
if (!Path.Exists(ryuExe))
|
||||
{
|
||||
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
|
||||
}
|
||||
|
||||
SetFileExecutable(ryuExe);
|
||||
|
||||
Process.Start(ryuExe, CommandLineState.Arguments);
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||
{
|
||||
// Multi-Threaded Updater
|
||||
long chunkSize = _buildSize / ConnectionCount;
|
||||
long remainderChunk = _buildSize % ConnectionCount;
|
||||
|
||||
int completedRequests = 0;
|
||||
int totalProgressPercentage = 0;
|
||||
int[] progressPercentage = new int[ConnectionCount];
|
||||
|
||||
List<byte[]> list = new List<byte[]>(ConnectionCount);
|
||||
List<WebClient> webClients = new List<WebClient>(ConnectionCount);
|
||||
|
||||
for (int i = 0; i < ConnectionCount; i++)
|
||||
{
|
||||
list.Add(Array.Empty<byte>());
|
||||
}
|
||||
|
||||
for (int i = 0; i < ConnectionCount; i++)
|
||||
{
|
||||
#pragma warning disable SYSLIB0014
|
||||
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
|
||||
using (WebClient client = new WebClient())
|
||||
#pragma warning restore SYSLIB0014
|
||||
{
|
||||
webClients.Add(client);
|
||||
|
||||
if (i == ConnectionCount - 1)
|
||||
{
|
||||
client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}");
|
||||
}
|
||||
else
|
||||
{
|
||||
client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}");
|
||||
}
|
||||
|
||||
client.DownloadProgressChanged += (_, args) =>
|
||||
{
|
||||
int index = (int)args.UserState;
|
||||
|
||||
Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]);
|
||||
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
|
||||
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
|
||||
|
||||
taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal);
|
||||
};
|
||||
|
||||
client.DownloadDataCompleted += (_, args) =>
|
||||
{
|
||||
int index = (int)args.UserState;
|
||||
|
||||
if (args.Cancelled)
|
||||
{
|
||||
webClients[index].Dispose();
|
||||
|
||||
taskDialog.Hide();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
list[index] = args.Result;
|
||||
Interlocked.Increment(ref completedRequests);
|
||||
|
||||
if (Equals(completedRequests, ConnectionCount))
|
||||
{
|
||||
byte[] mergedFileBytes = new byte[_buildSize];
|
||||
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++)
|
||||
{
|
||||
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
|
||||
destinationOffset += list[connectionIndex].Length;
|
||||
}
|
||||
|
||||
File.WriteAllBytes(updateFile, mergedFileBytes);
|
||||
|
||||
try
|
||||
{
|
||||
InstallUpdate(taskDialog, updateFile);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, e.Message);
|
||||
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
|
||||
|
||||
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
client.DownloadDataAsync(new Uri(downloadUrl), i);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
|
||||
|
||||
for (int j = 0; j < webClients.Count; j++)
|
||||
{
|
||||
webClients[j].CancelAsync();
|
||||
}
|
||||
|
||||
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoUpdateWithSingleThreadWorker(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||
{
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
// We do not want to timeout while downloading
|
||||
client.Timeout = TimeSpan.FromDays(1);
|
||||
|
||||
using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result)
|
||||
using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result)
|
||||
{
|
||||
using (Stream updateFileStream = File.Open(updateFile, FileMode.Create))
|
||||
{
|
||||
long totalBytes = response.Content.Headers.ContentLength.Value;
|
||||
long byteWritten = 0;
|
||||
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
|
||||
while (true)
|
||||
{
|
||||
int readSize = remoteFileStream.Read(buffer);
|
||||
|
||||
if (readSize == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
byteWritten += readSize;
|
||||
|
||||
taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal);
|
||||
|
||||
updateFileStream.Write(buffer, 0, readSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstallUpdate(taskDialog, updateFile);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static double GetPercentage(double value, double max)
|
||||
{
|
||||
return max == 0 ? 0 : value / max * 100;
|
||||
}
|
||||
|
||||
private static void DoUpdateWithSingleThread(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||
{
|
||||
Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile));
|
||||
worker.Name = "Updater.SingleThreadWorker";
|
||||
worker.Start();
|
||||
}
|
||||
|
||||
private static void SetFileExecutable(string path)
|
||||
{
|
||||
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
|
||||
UnixFileMode.UserWrite |
|
||||
UnixFileMode.UserRead |
|
||||
UnixFileMode.GroupRead |
|
||||
UnixFileMode.GroupWrite |
|
||||
UnixFileMode.OtherRead |
|
||||
UnixFileMode.OtherWrite;
|
||||
|
||||
if (!OperatingSystem.IsWindows() && File.Exists(path))
|
||||
{
|
||||
File.SetUnixFileMode(path, ExecutableFileMode);
|
||||
}
|
||||
}
|
||||
|
||||
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
||||
{
|
||||
// Extract Update
|
||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
using (Stream inStream = File.OpenRead(updateFile))
|
||||
using (Stream gzipStream = new GZipInputStream(inStream))
|
||||
using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII))
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
TarEntry tarEntry;
|
||||
while ((tarEntry = tarStream.GetNextEntry()) != null)
|
||||
{
|
||||
if (tarEntry.IsDirectory) continue;
|
||||
|
||||
string outPath = Path.Combine(UpdateDir, tarEntry.Name);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||
|
||||
using (FileStream outStream = File.OpenWrite(outPath))
|
||||
{
|
||||
tarStream.CopyEntryContents(outStream);
|
||||
}
|
||||
|
||||
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
|
||||
|
||||
TarEntry entry = tarEntry;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Stream inStream = File.OpenRead(updateFile))
|
||||
using (ZipFile zipFile = new ZipFile(inStream))
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
double count = 0;
|
||||
foreach (ZipEntry zipEntry in zipFile)
|
||||
{
|
||||
count++;
|
||||
if (zipEntry.IsDirectory) continue;
|
||||
|
||||
string outPath = Path.Combine(UpdateDir, zipEntry.Name);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||
|
||||
using (Stream zipStream = zipFile.GetInputStream(zipEntry))
|
||||
using (FileStream outStream = File.OpenWrite(outPath))
|
||||
{
|
||||
zipStream.CopyTo(outStream);
|
||||
}
|
||||
|
||||
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Delete downloaded zip
|
||||
File.Delete(updateFile);
|
||||
|
||||
List<string> allFiles = EnumerateFilesToDelete().ToList();
|
||||
|
||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming];
|
||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||
|
||||
// Replace old files
|
||||
await Task.Run(() =>
|
||||
{
|
||||
double count = 0;
|
||||
foreach (string file in allFiles)
|
||||
{
|
||||
count++;
|
||||
try
|
||||
{
|
||||
File.Move(file, file + ".ryuold");
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance[LocaleKeys.UpdaterRenameFailed], file));
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||
});
|
||||
|
||||
MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog);
|
||||
});
|
||||
|
||||
Directory.Delete(UpdateDir, true);
|
||||
|
||||
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
|
||||
|
||||
UpdateSuccessful = true;
|
||||
|
||||
taskDialog.Hide();
|
||||
}
|
||||
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
public static bool CanUpdate(bool showWarnings, StyleableWindow parent)
|
||||
{
|
||||
#if !DISABLE_UPDATER
|
||||
if (RuntimeInformation.OSArchitecture != Architecture.X64)
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid())
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
if (showWarnings)
|
||||
{
|
||||
if (ReleaseInformation.IsFlatHubBuild())
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
// NOTE: This method should always reflect the latest build layout.s
|
||||
private static IEnumerable<string> EnumerateFilesToDelete()
|
||||
{
|
||||
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
foreach (string dir in WindowsDependencyDirs)
|
||||
{
|
||||
string dirPath = Path.Combine(HomeDir, dir);
|
||||
if (Directory.Exists(dirPath))
|
||||
{
|
||||
files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog)
|
||||
{
|
||||
var total = Directory.GetFiles(root, "*", SearchOption.AllDirectories).Length;
|
||||
foreach (string directory in Directory.GetDirectories(root))
|
||||
{
|
||||
string dirName = Path.GetFileName(directory);
|
||||
|
||||
if (!Directory.Exists(Path.Combine(dest, dirName)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(dest, dirName));
|
||||
}
|
||||
|
||||
MoveAllFilesOver(directory, Path.Combine(dest, dirName), taskDialog);
|
||||
}
|
||||
|
||||
double count = 0;
|
||||
foreach (string file in Directory.GetFiles(root))
|
||||
{
|
||||
count++;
|
||||
File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true);
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
taskDialog.SetProgressBarState(GetPercentage(count, total), TaskDialogProgressState.Normal);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void CleanupUpdate()
|
||||
{
|
||||
foreach (string file in Directory.GetFiles(HomeDir, "*.ryuold", SearchOption.AllDirectories))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,198 +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" Opened="MenuBase_OnMenuOpened">
|
||||
<MenuItem
|
||||
Command="{Binding ToggleFavorite}"
|
||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{Binding OpenUserSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenDeviceSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserDeviceDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserDeviceDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenBcatSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserBcatDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserBcatDirectoryToolTip}" />
|
||||
<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="Padding" Value="0" />
|
||||
<Setter Property="Margin" Value="5" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:0.7">
|
||||
<KeyFrame Cue="0%">
|
||||
<Setter Property="MaxWidth" Value="0" />
|
||||
<Setter Property="Opacity" Value="0.0" />
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="50%">
|
||||
<Setter Property="MaxWidth" Value="1000" />
|
||||
<Setter Property="Opacity" Value="0.3" />
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="100%">
|
||||
<Setter Property="MaxWidth" Value="1000" />
|
||||
<Setter Property="Opacity" Value="1.0" />
|
||||
</KeyFrame>
|
||||
</Animation>
|
||||
</Style.Animations>
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
|
||||
</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,83 +0,0 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using LibHac.Common;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class GameGridView : UserControl
|
||||
{
|
||||
private ApplicationData _selectedApplication;
|
||||
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
|
||||
RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
|
||||
|
||||
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
|
||||
{
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
remove { RemoveHandler(ApplicationOpenedEvent, value); }
|
||||
}
|
||||
|
||||
public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
if (listBox.SelectedItem is ApplicationData selected)
|
||||
{
|
||||
RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GameList_SelectionChanged(object sender, SelectionChangedEventArgs args)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
var selected = listBox.SelectedItem as ApplicationData;
|
||||
|
||||
_selectedApplication = selected;
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationData SelectedApplication => _selectedApplication;
|
||||
|
||||
public GameGridView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
(DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text;
|
||||
}
|
||||
|
||||
private void MenuBase_OnMenuOpened(object sender, EventArgs e)
|
||||
{
|
||||
var selection = SelectedApplication;
|
||||
|
||||
if (selection != null)
|
||||
{
|
||||
if (sender is ContextMenu menu)
|
||||
{
|
||||
bool canHaveUserSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.UserAccountSaveDataSize > 0;
|
||||
bool canHaveDeviceSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.DeviceSaveDataSize > 0;
|
||||
bool canHaveBcatSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
|
||||
|
||||
((menu.Items as AvaloniaList<object>)[2] as MenuItem).IsEnabled = canHaveUserSave;
|
||||
((menu.Items as AvaloniaList<object>)[3] as MenuItem).IsEnabled = canHaveDeviceSave;
|
||||
((menu.Items as AvaloniaList<object>)[4] as MenuItem).IsEnabled = canHaveBcatSave;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Controls.GameListView"
|
||||
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: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" Opened="MenuBase_OnMenuOpened">
|
||||
<MenuItem
|
||||
Command="{Binding ToggleFavorite}"
|
||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{Binding OpenUserSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenDeviceSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserDeviceDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserDeviceDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenBcatSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserBcatDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserBcatDirectoryToolTip}" />
|
||||
<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
|
||||
Name="GameListBox"
|
||||
Grid.Row="0"
|
||||
Padding="8"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ContextFlyout="{StaticResource GameContextMenu}"
|
||||
DoubleTapped="GameList_DoubleTapped"
|
||||
Items="{Binding AppsObservableList}"
|
||||
SelectionChanged="GameList_SelectionChanged">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="2" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem">
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="CornerRadius" Value="5" />
|
||||
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
|
||||
<Setter Property="BorderThickness" Value="2"/>
|
||||
<Style.Animations>
|
||||
<Animation Duration="0:0:0.7">
|
||||
<KeyFrame Cue="0%">
|
||||
<Setter Property="MaxHeight" Value="0" />
|
||||
<Setter Property="Opacity" Value="0.0" />
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="50%">
|
||||
<Setter Property="MaxHeight" Value="1000" />
|
||||
<Setter Property="Opacity" Value="0.3" />
|
||||
</KeyFrame>
|
||||
<KeyFrame Cue="100%">
|
||||
<Setter Property="MaxHeight" Value="1000" />
|
||||
<Setter Property="Opacity" Value="1.0" />
|
||||
</KeyFrame>
|
||||
</Animation>
|
||||
</Style.Animations>
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
|
||||
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.ListItemSelectorSize}" />
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Border
|
||||
Margin="0"
|
||||
Padding="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="0"
|
||||
Margin="0"
|
||||
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}"
|
||||
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical"
|
||||
Spacing="5" >
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding TitleName}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Developer}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical"
|
||||
Spacing="5">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding TimePlayed}"
|
||||
TextAlignment="Right"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding LastPlayed}"
|
||||
TextAlignment="Right"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding FileSize}"
|
||||
TextAlignment="Right"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<ui:SymbolIcon
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="-5,-5,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="16"
|
||||
Foreground="{DynamicResource SystemAccentColor}"
|
||||
IsVisible="{Binding Favorite}"
|
||||
Symbol="StarFilled" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,83 +0,0 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using LibHac.Common;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class GameListView : UserControl
|
||||
{
|
||||
private ApplicationData _selectedApplication;
|
||||
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
|
||||
RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
|
||||
|
||||
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
|
||||
{
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
remove { RemoveHandler(ApplicationOpenedEvent, value); }
|
||||
}
|
||||
|
||||
public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
if (listBox.SelectedItem is ApplicationData selected)
|
||||
{
|
||||
RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GameList_SelectionChanged(object sender, SelectionChangedEventArgs args)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
var selected = listBox.SelectedItem as ApplicationData;
|
||||
|
||||
_selectedApplication = selected;
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationData SelectedApplication => _selectedApplication;
|
||||
|
||||
public GameListView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
(DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text;
|
||||
}
|
||||
|
||||
private void MenuBase_OnMenuOpened(object sender, EventArgs e)
|
||||
{
|
||||
var selection = SelectedApplication;
|
||||
|
||||
if (selection != null)
|
||||
{
|
||||
if (sender is ContextMenu menu)
|
||||
{
|
||||
bool canHaveUserSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.UserAccountSaveDataSize > 0;
|
||||
bool canHaveDeviceSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.DeviceSaveDataSize > 0;
|
||||
bool canHaveBcatSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
|
||||
|
||||
((menu.Items as AvaloniaList<object>)[2] as MenuItem).IsEnabled = canHaveUserSave;
|
||||
((menu.Items as AvaloniaList<object>)[3] as MenuItem).IsEnabled = canHaveDeviceSave;
|
||||
((menu.Items as AvaloniaList<object>)[4] as MenuItem).IsEnabled = canHaveBcatSave;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,91 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using LibHac;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class NavigationDialogHost : UserControl
|
||||
{
|
||||
public AccountManager AccountManager { get; }
|
||||
public ContentManager ContentManager { get; }
|
||||
public VirtualFileSystem VirtualFileSystem { get; }
|
||||
public HorizonClient HorizonClient { get; }
|
||||
public UserProfileViewModel ViewModel { get; set; }
|
||||
|
||||
public NavigationDialogHost()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public NavigationDialogHost(AccountManager accountManager, ContentManager contentManager,
|
||||
VirtualFileSystem virtualFileSystem, HorizonClient horizonClient)
|
||||
{
|
||||
AccountManager = accountManager;
|
||||
ContentManager = contentManager;
|
||||
VirtualFileSystem = virtualFileSystem;
|
||||
HorizonClient = horizonClient;
|
||||
ViewModel = new UserProfileViewModel(this);
|
||||
|
||||
|
||||
if (contentManager.GetCurrentFirmwareVersion() != null)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
AvatarProfileViewModel.PreloadAvatars(contentManager, virtualFileSystem);
|
||||
});
|
||||
}
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void GoBack(object parameter = null)
|
||||
{
|
||||
if (ContentFrame.BackStack.Count > 0)
|
||||
{
|
||||
ContentFrame.GoBack();
|
||||
}
|
||||
|
||||
ViewModel.LoadProfiles();
|
||||
}
|
||||
|
||||
public void Navigate(Type sourcePageType, object parameter)
|
||||
{
|
||||
ContentFrame.Navigate(sourcePageType, parameter);
|
||||
}
|
||||
|
||||
public static async Task Show(AccountManager ownerAccountManager, ContentManager ownerContentManager,
|
||||
VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient)
|
||||
{
|
||||
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient);
|
||||
ContentDialog contentDialog = new ContentDialog
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle],
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
Content = content,
|
||||
Padding = new Thickness(0)
|
||||
};
|
||||
|
||||
contentDialog.Closed += (sender, args) =>
|
||||
{
|
||||
content.ViewModel.Dispose();
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
Navigate(typeof(UserSelector), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Ryujinx.Ava.UI.Controls.ProfileImageSelectionDialog"
|
||||
Focusable="True">
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Margin="5,10,5, 5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="70" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
FontSize="18"
|
||||
HorizontalAlignment="Center"
|
||||
Grid.Row="1"
|
||||
Text="{locale:Locale ProfileImageSelectionHeader}" />
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
Grid.Row="2"
|
||||
Margin="10"
|
||||
MaxWidth="400"
|
||||
TextWrapping="Wrap"
|
||||
HorizontalAlignment="Center"
|
||||
TextAlignment="Center"
|
||||
Text="{locale:Locale ProfileImageSelectionNote}" />
|
||||
<StackPanel
|
||||
Margin="5,0"
|
||||
Spacing="10"
|
||||
Grid.Row="4"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Name="Import"
|
||||
Click="Import_OnClick"
|
||||
Width="200">
|
||||
<TextBlock Text="{locale:Locale ProfileImageSelectionImportImage}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="SelectFirmwareImage"
|
||||
IsEnabled="{Binding FirmwareFound}"
|
||||
Click="SelectFirmwareImage_OnClick"
|
||||
Width="200">
|
||||
<TextBlock Text="{locale:Locale ProfileImageSelectionSelectAvatar}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,127 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Silk.NET.Vulkan;
|
||||
using SPB.Graphics.OpenGL;
|
||||
using SPB.Windowing;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class RendererHost : UserControl, IDisposable
|
||||
{
|
||||
private readonly GraphicsDebugLevel _graphicsDebugLevel;
|
||||
private EmbeddedWindow _currentWindow;
|
||||
|
||||
public bool IsVulkan { get; private set; }
|
||||
|
||||
public RendererHost(GraphicsDebugLevel graphicsDebugLevel)
|
||||
{
|
||||
_graphicsDebugLevel = graphicsDebugLevel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public RendererHost()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void CreateOpenGL()
|
||||
{
|
||||
Dispose();
|
||||
|
||||
_currentWindow = new OpenGLEmbeddedWindow(3, 3, _graphicsDebugLevel);
|
||||
Initialize();
|
||||
|
||||
IsVulkan = false;
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_currentWindow.WindowCreated += CurrentWindow_WindowCreated;
|
||||
_currentWindow.SizeChanged += CurrentWindow_SizeChanged;
|
||||
Content = _currentWindow;
|
||||
}
|
||||
|
||||
public void CreateVulkan()
|
||||
{
|
||||
Dispose();
|
||||
|
||||
_currentWindow = new VulkanEmbeddedWindow();
|
||||
Initialize();
|
||||
|
||||
IsVulkan = true;
|
||||
}
|
||||
|
||||
public OpenGLContextBase GetContext()
|
||||
{
|
||||
if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow)
|
||||
{
|
||||
return openGlEmbeddedWindow.Context;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void CurrentWindow_SizeChanged(object sender, Size e)
|
||||
{
|
||||
SizeChanged?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private void CurrentWindow_WindowCreated(object sender, IntPtr e)
|
||||
{
|
||||
RendererInitialized?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void MakeCurrent()
|
||||
{
|
||||
if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow)
|
||||
{
|
||||
openGlEmbeddedWindow.MakeCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
public void MakeCurrent(SwappableNativeWindowBase window)
|
||||
{
|
||||
if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow)
|
||||
{
|
||||
openGlEmbeddedWindow.MakeCurrent(window);
|
||||
}
|
||||
}
|
||||
|
||||
public void SwapBuffers()
|
||||
{
|
||||
if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow)
|
||||
{
|
||||
openGlEmbeddedWindow.SwapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<EventArgs> RendererInitialized;
|
||||
public event Action<object, Size> SizeChanged;
|
||||
public void Dispose()
|
||||
{
|
||||
if (_currentWindow != null)
|
||||
{
|
||||
_currentWindow.WindowCreated -= CurrentWindow_WindowCreated;
|
||||
_currentWindow.SizeChanged -= CurrentWindow_SizeChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public SurfaceKHR CreateVulkanSurface(Instance instance, Vk api)
|
||||
{
|
||||
return (_currentWindow is VulkanEmbeddedWindow vulkanEmbeddedWindow)
|
||||
? vulkanEmbeddedWindow.CreateSurface(instance)
|
||||
: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800"
|
||||
d:DesignHeight="450"
|
||||
Height="400"
|
||||
Width="550"
|
||||
x:Class="Ryujinx.Ava.UI.Controls.SaveManager"
|
||||
Focusable="True">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
Spacing="10"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center">
|
||||
<Label
|
||||
Content="{locale:Locale CommonSort}"
|
||||
VerticalAlignment="Center" />
|
||||
<ComboBox SelectedIndex="{Binding SortIndex}" Width="100">
|
||||
<ComboBoxItem>
|
||||
<Label
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Left"
|
||||
Content="{locale:Locale Name}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<Label
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Left"
|
||||
Content="{locale:Locale Size}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<ComboBox SelectedIndex="{Binding OrderIndex}" Width="150">
|
||||
<ComboBoxItem>
|
||||
<Label
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Left"
|
||||
Content="{locale:Locale OrderAscending}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<Label
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Left"
|
||||
Content="{locale:Locale OrderDescending}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="10,0, 0, 0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Content="{locale:Locale Search}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox
|
||||
Margin="5,0,0,0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Search}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="0,5"
|
||||
BorderThickness="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<ListBox
|
||||
Name="SaveList"
|
||||
Items="{Binding View}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:SaveModel">
|
||||
<Grid HorizontalAlignment="Stretch" Margin="0,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Border
|
||||
Height="42"
|
||||
Margin="2"
|
||||
Width="42"
|
||||
Padding="10"
|
||||
IsVisible="{Binding !InGameList}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Help"
|
||||
FontSize="30"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Image
|
||||
IsVisible="{Binding InGameList}"
|
||||
Margin="2"
|
||||
Width="42"
|
||||
Height="42"
|
||||
Source="{Binding Icon,
|
||||
Converter={StaticResource ByteImage}}" />
|
||||
<TextBlock
|
||||
MaxLines="3"
|
||||
Width="320"
|
||||
Margin="5"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Title}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<Label
|
||||
Content="{Binding SizeString}"
|
||||
IsVisible="{Binding SizeAvailable}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right" />
|
||||
<Button
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right"
|
||||
Padding="10"
|
||||
MinWidth="0"
|
||||
MinHeight="0"
|
||||
Name="OpenLocation"
|
||||
Command="{Binding OpenLocation}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="OpenFolder"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Button>
|
||||
<Button
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right"
|
||||
Padding="10"
|
||||
MinWidth="0"
|
||||
MinHeight="0"
|
||||
Name="Delete"
|
||||
Command="{Binding Delete}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Delete"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,160 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using LibHac;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class SaveManager : UserControl
|
||||
{
|
||||
private readonly UserProfile _userProfile;
|
||||
private readonly HorizonClient _horizonClient;
|
||||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
private int _sortIndex;
|
||||
private int _orderIndex;
|
||||
private ObservableCollection<SaveModel> _view = new ObservableCollection<SaveModel>();
|
||||
private string _search;
|
||||
|
||||
public ObservableCollection<SaveModel> Saves { get; set; } = new ObservableCollection<SaveModel>();
|
||||
|
||||
public ObservableCollection<SaveModel> View
|
||||
{
|
||||
get => _view;
|
||||
set => _view = value;
|
||||
}
|
||||
|
||||
public int SortIndex
|
||||
{
|
||||
get => _sortIndex;
|
||||
set
|
||||
{
|
||||
_sortIndex = value;
|
||||
Sort();
|
||||
}
|
||||
}
|
||||
|
||||
public int OrderIndex
|
||||
{
|
||||
get => _orderIndex;
|
||||
set
|
||||
{
|
||||
_orderIndex = value;
|
||||
Sort();
|
||||
}
|
||||
}
|
||||
|
||||
public string Search
|
||||
{
|
||||
get => _search;
|
||||
set
|
||||
{
|
||||
_search = value;
|
||||
Sort();
|
||||
}
|
||||
}
|
||||
|
||||
public SaveManager()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public SaveManager(UserProfile userProfile, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem)
|
||||
{
|
||||
_userProfile = userProfile;
|
||||
_horizonClient = horizonClient;
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
InitializeComponent();
|
||||
|
||||
DataContext = this;
|
||||
|
||||
Task.Run(LoadSaves);
|
||||
}
|
||||
|
||||
public void LoadSaves()
|
||||
{
|
||||
Saves.Clear();
|
||||
var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account,
|
||||
new UserId((ulong)_userProfile.UserId.High, (ulong)_userProfile.UserId.Low), saveDataId: default, index: default);
|
||||
|
||||
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||
|
||||
_horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
||||
|
||||
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
||||
|
||||
while (true)
|
||||
{
|
||||
saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure();
|
||||
|
||||
if (readCount == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < readCount; i++)
|
||||
{
|
||||
var save = saveDataInfo[i];
|
||||
if (save.ProgramId.Value != 0)
|
||||
{
|
||||
var saveModel = new SaveModel(save, _horizonClient, _virtualFileSystem);
|
||||
Saves.Add(saveModel);
|
||||
saveModel.DeleteAction = () => { Saves.Remove(saveModel); };
|
||||
}
|
||||
|
||||
Sort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Sort()
|
||||
{
|
||||
Saves.AsObservableChangeSet()
|
||||
.Filter(Filter)
|
||||
.Sort(GetComparer())
|
||||
.Bind(out var view).AsObservableList();
|
||||
|
||||
_view.Clear();
|
||||
_view.AddRange(view);
|
||||
}
|
||||
|
||||
private IComparer<SaveModel> GetComparer()
|
||||
{
|
||||
switch (SortIndex)
|
||||
{
|
||||
case 0:
|
||||
return OrderIndex == 0
|
||||
? SortExpressionComparer<SaveModel>.Ascending(save => save.Title)
|
||||
: SortExpressionComparer<SaveModel>.Descending(save => save.Title);
|
||||
case 1:
|
||||
return OrderIndex == 0
|
||||
? SortExpressionComparer<SaveModel>.Ascending(save => save.Size)
|
||||
: SortExpressionComparer<SaveModel>.Descending(save => save.Size);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool Filter(object arg)
|
||||
{
|
||||
if (arg is SaveModel save)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="800"
|
||||
d:DesignHeight="450"
|
||||
MinWidth="500"
|
||||
MinHeight="400"
|
||||
x:Class="Ryujinx.Ava.UI.Controls.UserRecoverer"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModels:UserProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Button Grid.Row="0"
|
||||
Margin="5"
|
||||
Height="30"
|
||||
Width="50"
|
||||
MinWidth="50"
|
||||
HorizontalAlignment="Left"
|
||||
Command="{Binding GoBack}">
|
||||
<ui:SymbolIcon Symbol="Back"/>
|
||||
</Button>
|
||||
<TextBlock Grid.Row="1"
|
||||
Text="{locale:Locale UserProfilesRecoverHeading}"/>
|
||||
<ListBox
|
||||
Margin="5"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Items="{Binding LostProfiles}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="5">
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding UserId}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap" />
|
||||
<Button Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{Binding Recover}"
|
||||
CommandParameter="{Binding}"
|
||||
Content="{locale:Locale Recover}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,44 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class UserRecoverer : UserControl
|
||||
{
|
||||
private UserProfileViewModel _viewModel;
|
||||
private NavigationDialogHost _parent;
|
||||
|
||||
public UserRecoverer()
|
||||
{
|
||||
InitializeComponent();
|
||||
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
||||
{
|
||||
NavigatedTo(e);
|
||||
}, RoutingStrategies.Direct);
|
||||
}
|
||||
|
||||
private void NavigatedTo(NavigationEventArgs arg)
|
||||
{
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
switch (arg.NavigationMode)
|
||||
{
|
||||
case NavigationMode.New:
|
||||
var args = ((NavigationDialogHost parent, UserProfileViewModel viewModel))arg.Parameter;
|
||||
|
||||
_viewModel = args.viewModel;
|
||||
_parent = args.parent;
|
||||
break;
|
||||
}
|
||||
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Controls.UserSelector"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
d:DesignHeight="450"
|
||||
MinWidth="500"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Design.DataContext>
|
||||
<viewModels:UserProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListBox
|
||||
Margin="5"
|
||||
MaxHeight="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
DoubleTapped="ProfilesList_DoubleTapped"
|
||||
Items="{Binding Profiles}"
|
||||
SelectionChanged="SelectingItemsControl_SelectionChanged">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<flex:FlexPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
AlignContent="FlexStart"
|
||||
JustifyContent="Center" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Border
|
||||
Margin="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="5">
|
||||
<Grid Margin="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
Width="96"
|
||||
Height="96"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Source="{Binding Image, Converter={StaticResource ByteImage}}" />
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Height="30"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border
|
||||
Width="10"
|
||||
Height="10"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Background="LimeGreen"
|
||||
CornerRadius="5"
|
||||
IsVisible="{Binding IsOpened}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
Command="{Binding AddUser}"
|
||||
Content="{locale:Locale UserProfilesAddNewProfile}" />
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
Grid.Row="0"
|
||||
Margin="2"
|
||||
Grid.Column="1"
|
||||
Command="{Binding EditUser}"
|
||||
Content="{locale:Locale UserProfilesEditProfile}"
|
||||
IsEnabled="{Binding IsSelectedProfiledEditable}" />
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
Content="{locale:Locale UserProfilesManageSaves}"
|
||||
Command="{Binding ManageSaves}" />
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="2"
|
||||
Command="{Binding DeleteUser}"
|
||||
Content="{locale:Locale UserProfilesDeleteSelectedProfile}"
|
||||
IsEnabled="{Binding IsSelectedProfileDeletable}" />
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Grid.Column="0"
|
||||
Margin="2"
|
||||
Command="{Binding RecoverLostAccounts}"
|
||||
Content="{locale:Locale UserProfilesRecoverLostAccounts}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,77 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class UserSelector : UserControl
|
||||
{
|
||||
private NavigationDialogHost _parent;
|
||||
public UserProfileViewModel ViewModel { get; set; }
|
||||
|
||||
public UserSelector()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
AddHandler(Frame.NavigatedToEvent, (s, e) =>
|
||||
{
|
||||
NavigatedTo(e);
|
||||
}, RoutingStrategies.Direct);
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigatedTo(NavigationEventArgs arg)
|
||||
{
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
if (arg.NavigationMode == NavigationMode.New)
|
||||
{
|
||||
_parent = (NavigationDialogHost)arg.Parameter;
|
||||
ViewModel = _parent.ViewModel;
|
||||
}
|
||||
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProfilesList_DoubleTapped(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
int selectedIndex = listBox.SelectedIndex;
|
||||
|
||||
if (selectedIndex >= 0 && selectedIndex < ViewModel.Profiles.Count)
|
||||
{
|
||||
ViewModel.SelectedProfile = ViewModel.Profiles[selectedIndex];
|
||||
|
||||
_parent?.AccountManager?.OpenUser(ViewModel.SelectedProfile.UserId);
|
||||
|
||||
ViewModel.LoadProfiles();
|
||||
|
||||
foreach (UserProfile profile in ViewModel.Profiles)
|
||||
{
|
||||
profile.UpdateState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectingItemsControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
int selectedIndex = listBox.SelectedIndex;
|
||||
|
||||
if (selectedIndex >= 0 && selectedIndex < ViewModel.Profiles.Count)
|
||||
{
|
||||
ViewModel.HighlightedProfile = ViewModel.Profiles[selectedIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using SPB.Graphics;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
[SupportedOSPlatform("linux")]
|
||||
internal class AvaloniaGlxContext : SPB.Platform.GLX.GLXOpenGLContext
|
||||
{
|
||||
public AvaloniaGlxContext(IntPtr handle)
|
||||
: base(FramebufferFormat.Default, 0, 0, 0, false, null)
|
||||
{
|
||||
ContextHandle = handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using SPB.Graphics;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal class AvaloniaWglContext : SPB.Platform.WGL.WGLOpenGLContext
|
||||
{
|
||||
public AvaloniaWglContext(IntPtr handle)
|
||||
: base(FramebufferFormat.Default, 0, 0, 0, false, null)
|
||||
{
|
||||
ContextHandle = handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Platform;
|
||||
using Ryujinx.Ava.UI.Helper;
|
||||
using SPB.Graphics;
|
||||
using SPB.Platform;
|
||||
using SPB.Platform.GLX;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading.Tasks;
|
||||
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
public class EmbeddedWindow : NativeControlHost
|
||||
{
|
||||
private WindowProc _wndProcDelegate;
|
||||
private string _className;
|
||||
|
||||
protected GLXWindow X11Window { get; set; }
|
||||
protected IntPtr WindowHandle { get; set; }
|
||||
protected IntPtr X11Display { get; set; }
|
||||
protected IntPtr NsView { get; set; }
|
||||
protected IntPtr MetalLayer { get; set; }
|
||||
|
||||
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
|
||||
|
||||
public event EventHandler<IntPtr> WindowCreated;
|
||||
public event EventHandler<Size> SizeChanged;
|
||||
|
||||
protected virtual void OnWindowDestroyed() { }
|
||||
protected virtual void OnWindowDestroying()
|
||||
{
|
||||
WindowHandle = IntPtr.Zero;
|
||||
X11Display = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public EmbeddedWindow()
|
||||
{
|
||||
var stateObserverable = this.GetObservable(BoundsProperty);
|
||||
|
||||
stateObserverable.Subscribe(StateChanged);
|
||||
|
||||
this.Initialized += NativeEmbeddedWindow_Initialized;
|
||||
}
|
||||
|
||||
public virtual void OnWindowCreated() { }
|
||||
|
||||
private void NativeEmbeddedWindow_Initialized(object sender, EventArgs e)
|
||||
{
|
||||
OnWindowCreated();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
WindowCreated?.Invoke(this, WindowHandle);
|
||||
});
|
||||
}
|
||||
|
||||
private void StateChanged(Rect rect)
|
||||
{
|
||||
SizeChanged?.Invoke(this, rect.Size);
|
||||
_updateBoundsCallback?.Invoke(rect);
|
||||
}
|
||||
|
||||
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
|
||||
{
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
return CreateLinux(parent);
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
return CreateWin32(parent);
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
return CreateMacOs(parent);
|
||||
}
|
||||
|
||||
return base.CreateNativeControlCore(parent);
|
||||
}
|
||||
|
||||
protected override void DestroyNativeControlCore(IPlatformHandle control)
|
||||
{
|
||||
OnWindowDestroying();
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
DestroyLinux();
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
DestroyWin32(control);
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
DestroyMacOS();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DestroyNativeControlCore(control);
|
||||
}
|
||||
|
||||
OnWindowDestroyed();
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
protected virtual IPlatformHandle CreateLinux(IPlatformHandle parent)
|
||||
{
|
||||
X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow;
|
||||
WindowHandle = X11Window.WindowHandle.RawHandle;
|
||||
X11Display = X11Window.DisplayHandle.RawHandle;
|
||||
|
||||
return new PlatformHandle(WindowHandle, "X11");
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
IPlatformHandle CreateWin32(IPlatformHandle parent)
|
||||
{
|
||||
_className = "NativeWindow-" + Guid.NewGuid();
|
||||
_wndProcDelegate = WndProc;
|
||||
var wndClassEx = new WNDCLASSEX
|
||||
{
|
||||
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
|
||||
hInstance = GetModuleHandle(null),
|
||||
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
|
||||
style = ClassStyles.CS_OWNDC,
|
||||
lpszClassName = Marshal.StringToHGlobalUni(_className),
|
||||
hCursor = LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW)
|
||||
};
|
||||
|
||||
var atom = RegisterClassEx(ref wndClassEx);
|
||||
|
||||
var handle = CreateWindowEx(
|
||||
0,
|
||||
_className,
|
||||
"NativeWindow",
|
||||
WindowStyles.WS_CHILD,
|
||||
0,
|
||||
0,
|
||||
640,
|
||||
480,
|
||||
parent.Handle,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero);
|
||||
|
||||
WindowHandle = handle;
|
||||
|
||||
Marshal.FreeHGlobal(wndClassEx.lpszClassName);
|
||||
|
||||
return new PlatformHandle(WindowHandle, "HWND");
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF);
|
||||
var root = VisualRoot as Window;
|
||||
bool isLeft = false;
|
||||
switch (msg)
|
||||
{
|
||||
case WindowsMessages.LBUTTONDOWN:
|
||||
case WindowsMessages.RBUTTONDOWN:
|
||||
isLeft = msg == WindowsMessages.LBUTTONDOWN;
|
||||
this.RaiseEvent(new PointerPressedEventArgs(
|
||||
this,
|
||||
new Pointer(0, PointerType.Mouse, true),
|
||||
root,
|
||||
this.TranslatePoint(point, root).Value,
|
||||
(ulong)Environment.TickCount64,
|
||||
new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed),
|
||||
KeyModifiers.None));
|
||||
break;
|
||||
case WindowsMessages.LBUTTONUP:
|
||||
case WindowsMessages.RBUTTONUP:
|
||||
isLeft = msg == WindowsMessages.LBUTTONUP;
|
||||
this.RaiseEvent(new PointerReleasedEventArgs(
|
||||
this,
|
||||
new Pointer(0, PointerType.Mouse, true),
|
||||
root,
|
||||
this.TranslatePoint(point, root).Value,
|
||||
(ulong)Environment.TickCount64,
|
||||
new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased),
|
||||
KeyModifiers.None,
|
||||
isLeft ? MouseButton.Left : MouseButton.Right));
|
||||
break;
|
||||
case WindowsMessages.MOUSEMOVE:
|
||||
this.RaiseEvent(new PointerEventArgs(
|
||||
PointerMovedEvent,
|
||||
this,
|
||||
new Pointer(0, PointerType.Mouse, true),
|
||||
root,
|
||||
this.TranslatePoint(point, root).Value,
|
||||
(ulong)Environment.TickCount64,
|
||||
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
|
||||
KeyModifiers.None));
|
||||
break;
|
||||
}
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
IPlatformHandle CreateMacOs(IPlatformHandle parent)
|
||||
{
|
||||
MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
|
||||
|
||||
NsView = nsView;
|
||||
|
||||
return new PlatformHandle(nsView, "NSView");
|
||||
}
|
||||
|
||||
void DestroyLinux()
|
||||
{
|
||||
X11Window?.Dispose();
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
void DestroyWin32(IPlatformHandle handle)
|
||||
{
|
||||
DestroyWindow(handle.Handle);
|
||||
UnregisterClass(_className, GetModuleHandle(null));
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
void DestroyMacOS()
|
||||
{
|
||||
MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Avalonia.OpenGL;
|
||||
using SPB.Graphics.OpenGL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
internal static class IGlContextExtension
|
||||
{
|
||||
public static OpenGLContextBase AsOpenGLContextBase(this IGlContext context)
|
||||
{
|
||||
var handle = (IntPtr)context.GetType().GetProperty("Handle").GetValue(context);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
return new AvaloniaWglContext(handle);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
return new AvaloniaGlxContext(handle);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using Avalonia.Platform;
|
||||
using Silk.NET.Vulkan;
|
||||
using SPB.Graphics.Vulkan;
|
||||
using SPB.Platform.GLX;
|
||||
using SPB.Platform.Metal;
|
||||
using SPB.Platform.Win32;
|
||||
using SPB.Platform.X11;
|
||||
using SPB.Windowing;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
public class VulkanEmbeddedWindow : EmbeddedWindow
|
||||
{
|
||||
private NativeWindowBase _window;
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
protected override IPlatformHandle CreateLinux(IPlatformHandle parent)
|
||||
{
|
||||
X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(parent.Handle));
|
||||
WindowHandle = X11Window.WindowHandle.RawHandle;
|
||||
X11Display = X11Window.DisplayHandle.RawHandle;
|
||||
|
||||
X11Window.Hide();
|
||||
|
||||
return new PlatformHandle(WindowHandle, "X11");
|
||||
}
|
||||
|
||||
public SurfaceKHR CreateSurface(Instance instance)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
_window = new SimpleWin32Window(new NativeHandle(WindowHandle));
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
_window = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle));
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
_window = new SimpleMetalWindow(new NativeHandle(NsView), new NativeHandle(MetalLayer));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, _window));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,14 +0,0 @@
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class ProfileImageModel
|
||||
{
|
||||
public ProfileImageModel(string name, byte[] data)
|
||||
{
|
||||
Name = name;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
internal class TitleUpdateModel
|
||||
{
|
||||
public bool IsEnabled { get; set; }
|
||||
public bool IsNoUpdate { get; }
|
||||
public ApplicationControlProperty Control { get; }
|
||||
public string Path { get; }
|
||||
|
||||
public string Label => IsNoUpdate
|
||||
? LocaleManager.Instance[LocaleKeys.NoUpdate]
|
||||
: string.Format(LocaleManager.Instance[LocaleKeys.TitleUpdateVersionLabel], Control.DisplayVersionString.ToString(),
|
||||
Path);
|
||||
|
||||
public TitleUpdateModel(ApplicationControlProperty control, string path, bool isNoUpdate = false)
|
||||
{
|
||||
Control = control;
|
||||
Path = path;
|
||||
IsNoUpdate = isNoUpdate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
|
||||
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public class UserProfileViewModel : BaseModel, IDisposable
|
||||
{
|
||||
private readonly NavigationDialogHost _owner;
|
||||
|
||||
private UserProfile _selectedProfile;
|
||||
private UserProfile _highlightedProfile;
|
||||
|
||||
public UserProfileViewModel()
|
||||
{
|
||||
Profiles = new ObservableCollection<UserProfile>();
|
||||
LostProfiles = new ObservableCollection<UserProfile>();
|
||||
}
|
||||
|
||||
public UserProfileViewModel(NavigationDialogHost owner) : this()
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
LoadProfiles();
|
||||
}
|
||||
|
||||
public ObservableCollection<UserProfile> Profiles { get; set; }
|
||||
|
||||
public ObservableCollection<UserProfile> LostProfiles { get; set; }
|
||||
|
||||
public UserProfile SelectedProfile
|
||||
{
|
||||
get => _selectedProfile;
|
||||
set
|
||||
{
|
||||
_selectedProfile = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(IsHighlightedProfileDeletable));
|
||||
OnPropertyChanged(nameof(IsHighlightedProfileEditable));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHighlightedProfileEditable => _highlightedProfile != null;
|
||||
|
||||
public bool IsHighlightedProfileDeletable => _highlightedProfile != null && _highlightedProfile.UserId != AccountManager.DefaultUserId;
|
||||
|
||||
public UserProfile HighlightedProfile
|
||||
{
|
||||
get => _highlightedProfile;
|
||||
set
|
||||
{
|
||||
_highlightedProfile = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(IsHighlightedProfileDeletable));
|
||||
OnPropertyChanged(nameof(IsHighlightedProfileEditable));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void LoadProfiles()
|
||||
{
|
||||
Profiles.Clear();
|
||||
LostProfiles.Clear();
|
||||
|
||||
var profiles = _owner.AccountManager.GetAllUsers().OrderByDescending(x => x.AccountState == AccountState.Open);
|
||||
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
Profiles.Add(new UserProfile(profile, _owner));
|
||||
}
|
||||
|
||||
SelectedProfile = Profiles.FirstOrDefault(x => x.UserId == _owner.AccountManager.LastOpenedUser.UserId);
|
||||
|
||||
if (SelectedProfile == null)
|
||||
{
|
||||
SelectedProfile = Profiles.First();
|
||||
|
||||
if (SelectedProfile != null)
|
||||
{
|
||||
_owner.AccountManager.OpenUser(_selectedProfile.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account,
|
||||
default, saveDataId: default, index: default);
|
||||
|
||||
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||
|
||||
_owner.HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
||||
|
||||
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
||||
|
||||
HashSet<UserId> lostAccounts = new HashSet<UserId>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure();
|
||||
|
||||
if (readCount == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < readCount; i++)
|
||||
{
|
||||
var save = saveDataInfo[i];
|
||||
var id = new UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
|
||||
if (Profiles.FirstOrDefault( x=> x.UserId == id) == null)
|
||||
{
|
||||
lostAccounts.Add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var account in lostAccounts)
|
||||
{
|
||||
LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), _owner));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUser()
|
||||
{
|
||||
UserProfile userProfile = null;
|
||||
|
||||
_owner.Navigate(typeof(UserEditor), (this._owner, userProfile, true));
|
||||
}
|
||||
|
||||
public async void ManageSaves()
|
||||
{
|
||||
UserProfile userProfile = _highlightedProfile ?? SelectedProfile;
|
||||
|
||||
SaveManager manager = new SaveManager(userProfile, _owner.HorizonClient, _owner.VirtualFileSystem);
|
||||
|
||||
ContentDialog contentDialog = new ContentDialog
|
||||
{
|
||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.SaveManagerHeading], userProfile.Name),
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
Content = manager,
|
||||
Padding = new Thickness(0)
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
|
||||
public void EditUser()
|
||||
{
|
||||
_owner.Navigate(typeof(UserEditor), (this._owner, _highlightedProfile ?? SelectedProfile, false));
|
||||
}
|
||||
|
||||
public async void DeleteUser()
|
||||
{
|
||||
if (_highlightedProfile != null)
|
||||
{
|
||||
var lastUserId = _owner.AccountManager.LastOpenedUser.UserId;
|
||||
|
||||
if (_highlightedProfile.UserId == lastUserId)
|
||||
{
|
||||
// If we are deleting the currently open profile, then we must open something else before deleting.
|
||||
var profile = Profiles.FirstOrDefault(x => x.UserId != lastUserId);
|
||||
|
||||
if (profile == null)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_owner.AccountManager.OpenUser(profile.UserId);
|
||||
}
|
||||
|
||||
var result =
|
||||
await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionConfirmMessage], "",
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], "");
|
||||
|
||||
if (result == UserResult.Yes)
|
||||
{
|
||||
_owner.AccountManager.DeleteUser(_highlightedProfile.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
LoadProfiles();
|
||||
}
|
||||
|
||||
public void GoBack()
|
||||
{
|
||||
_owner.GoBack();
|
||||
}
|
||||
|
||||
public void RecoverLostAccounts()
|
||||
{
|
||||
_owner.Navigate(typeof(UserRecoverer), (this._owner, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using System.Threading.Tasks;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Ncm;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.Modules;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
public partial class MainMenuBarView : UserControl
|
||||
{
|
||||
public MainWindow Window { get; private set; }
|
||||
public MainWindowViewModel ViewModel { get; private set; }
|
||||
|
||||
public MainMenuBarView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
|
||||
if (this.VisualRoot is MainWindow window)
|
||||
{
|
||||
Window = window;
|
||||
}
|
||||
|
||||
ViewModel = Window.ViewModel;
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
|
||||
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Window.ViewModel.AppHost?.ShowExitPrompt();
|
||||
});
|
||||
}
|
||||
|
||||
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Window.ViewModel.AppHost?.Pause();
|
||||
});
|
||||
}
|
||||
|
||||
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Window.ViewModel.AppHost?.Resume();
|
||||
});
|
||||
}
|
||||
|
||||
public async void OpenSettings(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
||||
|
||||
await Window.SettingsWindow.ShowDialog(Window);
|
||||
|
||||
ViewModel.LoadConfigurableHotKeys();
|
||||
}
|
||||
|
||||
public void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
if (!string.IsNullOrEmpty(contentPath))
|
||||
{
|
||||
ViewModel.LoadApplication(contentPath, false, "Mii Applet");
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenAmiiboWindow(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!ViewModel.IsAmiiboRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
|
||||
{
|
||||
string titleId = ViewModel.AppHost.Device.Application.TitleIdText.ToUpper();
|
||||
AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId);
|
||||
|
||||
await window.ShowDialog(Window);
|
||||
|
||||
if (window.IsScanned)
|
||||
{
|
||||
ViewModel.ShowAll = window.ViewModel.ShowAllAmiibo;
|
||||
ViewModel.LastScannedAmiiboId = window.ScannedAmiibo.GetId();
|
||||
|
||||
ViewModel.AppHost.Device.System.ScanAmiibo(deviceId, ViewModel.LastScannedAmiiboId, window.ViewModel.UseRandomUuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!ViewModel.IsGameRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationLoader application = ViewModel.AppHost.Device.Application;
|
||||
if (application != null)
|
||||
{
|
||||
await new CheatWindow(Window.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(Window);
|
||||
|
||||
ViewModel.AppHost.Device.EnableCheats();
|
||||
}
|
||||
}
|
||||
|
||||
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (sender is MenuItem)
|
||||
{
|
||||
ViewModel.IsAmiiboRequested = Window.ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
|
||||
}
|
||||
}
|
||||
|
||||
public async void CheckForUpdates(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Updater.CanUpdate(true, Window))
|
||||
{
|
||||
await Updater.BeginParse(Window, true);
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenAboutWindow(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await AboutWindow.Show();
|
||||
}
|
||||
|
||||
public void CloseWindow(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Window.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="260"
|
||||
d:DesignWidth="550"
|
||||
Height="260"
|
||||
Width="550"
|
||||
x:Class="Ryujinx.Ava.UI.Windows.AboutWindow"
|
||||
x:DataType="viewModel:AboutWindowViewModel"
|
||||
x:CompileBindings="True"
|
||||
Margin="0 -12 0 0"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModel:AboutWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image
|
||||
Grid.Column="0"
|
||||
Height="80"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
<flex:FlexPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Direction="Column"
|
||||
JustifyContent="SpaceAround"
|
||||
RowSpacing="2">
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Left" />
|
||||
<TextBlock
|
||||
Text="(REE-YOU-JINX)"
|
||||
TextAlignment="Left" />
|
||||
</flex:FlexPanel>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Center"
|
||||
FontSize="10"
|
||||
LineHeight="12" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Width="200"
|
||||
Text="{locale:Locale AboutDisclaimerMessage}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
FontSize="10"
|
||||
LineHeight="12" />
|
||||
<TextBlock
|
||||
Name="AmiiboLabel"
|
||||
HorizontalAlignment="Center"
|
||||
Width="200"
|
||||
PointerPressed="AmiiboLabel_OnPointerPressed"
|
||||
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap"
|
||||
FontSize="10"
|
||||
LineHeight="12" />
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10">
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://www.patreon.com/ryujinx"
|
||||
ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
|
||||
<Image Source="{Binding PatreonLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx"
|
||||
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
|
||||
<Image Source="{Binding GithubLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://discordapp.com/invite/N2FmfVc"
|
||||
ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
|
||||
<Image Source="{Binding DiscordLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://twitter.com/RyujinxEmu"
|
||||
ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
|
||||
<Image Source="{Binding TwitterLogo}" />
|
||||
</Button>
|
||||
<Button
|
||||
MaxHeight="30"
|
||||
MaxWidth="30"
|
||||
MinHeight="30"
|
||||
MinWidth="30"
|
||||
Padding="8"
|
||||
CornerRadius="15"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://www.ryujinx.org"
|
||||
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}">
|
||||
<ui:SymbolIcon
|
||||
Symbol="Link"
|
||||
Foreground="{DynamicResource ThemeForegroundColor}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Width="1"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1,0,0,0"
|
||||
Margin="20 0"/>
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Margin="0 10 0 0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
FontSize="15"
|
||||
Text="{locale:Locale AboutRyujinxAboutTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
TextWrapping="Wrap"
|
||||
Text="{locale:Locale AboutRyujinxAboutContent}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0 10 0 0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
FontSize="15"
|
||||
Text="{locale:Locale AboutRyujinxMaintainersTitle}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Developers}" />
|
||||
<Button
|
||||
HorizontalAlignment="Left"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Padding="5"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
|
||||
TextAlignment="Right"
|
||||
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Margin="0 10 0 0"
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
FontWeight="Bold"
|
||||
FontSize="15"
|
||||
Text="{locale:Locale AboutRyujinxSupprtersTitle}" />
|
||||
<ScrollViewer
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
Height="70">
|
||||
<TextBlock
|
||||
Name="SupportersTextBlock"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="10"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Supporters}" />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,54 +0,0 @@
|
||||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="350"
|
||||
x:Class="Ryujinx.Ava.UI.Windows.AvatarWindow"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:AvatarProfileViewModel"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModels:AvatarProfileViewModel />
|
||||
</Design.DataContext>
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListBox Grid.Row="1" BorderThickness="0" SelectedIndex="{Binding SelectedIndex}" Height="400"
|
||||
Items="{Binding Images}" HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal" MaxWidth="700" Margin="0" HorizontalAlignment="Center" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Image Margin="5" Height="96" Width="96"
|
||||
Source="{Binding Data, Converter={StaticResource ByteImage}}" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<ProgressBar Grid.Row="2" IsIndeterminate="{Binding IsIndeterminate}" Value="{Binding ImagesLoaded}" HorizontalAlignment="Stretch" Margin="5"
|
||||
Maximum="{Binding ImageCount}" Minimum="0" />
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Spacing="10" Margin="10" HorizontalAlignment="Center">
|
||||
<Button Content="{locale:Locale AvatarChoose}" Width="200" Name="ChooseButton" Click="ChooseButton_OnClick" />
|
||||
<ui:ColorPickerButton Color="{Binding BackgroundColor, Mode=TwoWay}" Name="ColorButton" />
|
||||
<Button HorizontalAlignment="Right" Content="{locale:Locale Discard}" Click="CloseButton_OnClick"
|
||||
Name="CloseButton"
|
||||
Width="200" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</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,115 +0,0 @@
|
||||
<window:StyleableWindow
|
||||
x:Class="Ryujinx.Ava.UI.Windows.TitleUpdateWindow"
|
||||
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="600"
|
||||
Height="400"
|
||||
MinWidth="600"
|
||||
MinHeight="400"
|
||||
MaxWidth="600"
|
||||
MaxHeight="400"
|
||||
SizeToContent="Height"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<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" />
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1">
|
||||
<ScrollViewer
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Items="{Binding _titleUpdates}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<RadioButton
|
||||
Padding="8,0"
|
||||
VerticalContentAlignment="Center"
|
||||
GroupName="Update"
|
||||
IsChecked="{Binding IsEnabled, Mode=TwoWay}">
|
||||
<Label
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding Label}"
|
||||
FontSize="12" />
|
||||
</RadioButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
<DockPanel
|
||||
Grid.Row="3"
|
||||
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 Save}">
|
||||
<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,242 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Program;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Window;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
{
|
||||
static class CommandHelper
|
||||
{
|
||||
private delegate void CommandDelegate(Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer);
|
||||
|
||||
private static int _totalCommands = (int)Enum.GetValues<CommandType>().Max() + 1;
|
||||
private static CommandDelegate[] _lookup = new CommandDelegate[_totalCommands];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ref T GetCommand<T>(Span<byte> memory)
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(memory));
|
||||
}
|
||||
|
||||
public static int GetMaxCommandSize()
|
||||
{
|
||||
Assembly assembly = typeof(CommandHelper).Assembly;
|
||||
|
||||
IEnumerable<Type> commands = assembly.GetTypes().Where(type => typeof(IGALCommand).IsAssignableFrom(type) && type.IsValueType);
|
||||
|
||||
int maxSize = commands.Max(command =>
|
||||
{
|
||||
MethodInfo method = typeof(Unsafe).GetMethod(nameof(Unsafe.SizeOf));
|
||||
MethodInfo generic = method.MakeGenericMethod(command);
|
||||
int size = (int)generic.Invoke(null, null);
|
||||
|
||||
return size;
|
||||
});
|
||||
|
||||
InitLookup();
|
||||
|
||||
return maxSize + 1; // 1 byte reserved for command size.
|
||||
}
|
||||
|
||||
private static void InitLookup()
|
||||
{
|
||||
_lookup[(int)CommandType.Action] = (memory, threaded, renderer) =>
|
||||
ActionCommand.Run(ref GetCommand<ActionCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.CreateBuffer] = (memory, threaded, renderer) =>
|
||||
CreateBufferCommand.Run(ref GetCommand<CreateBufferCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.CreateProgram] = (memory, threaded, renderer) =>
|
||||
CreateProgramCommand.Run(ref GetCommand<CreateProgramCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.CreateSampler] = (memory, threaded, renderer) =>
|
||||
CreateSamplerCommand.Run(ref GetCommand<CreateSamplerCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.CreateSync] = (memory, threaded, renderer) =>
|
||||
CreateSyncCommand.Run(ref GetCommand<CreateSyncCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.CreateTexture] = (memory, threaded, renderer) =>
|
||||
CreateTextureCommand.Run(ref GetCommand<CreateTextureCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.GetCapabilities] = (memory, threaded, renderer) =>
|
||||
GetCapabilitiesCommand.Run(ref GetCommand<GetCapabilitiesCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.PreFrame] = (memory, threaded, renderer) =>
|
||||
PreFrameCommand.Run(ref GetCommand<PreFrameCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.ReportCounter] = (memory, threaded, renderer) =>
|
||||
ReportCounterCommand.Run(ref GetCommand<ReportCounterCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.ResetCounter] = (memory, threaded, renderer) =>
|
||||
ResetCounterCommand.Run(ref GetCommand<ResetCounterCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.UpdateCounters] = (memory, threaded, renderer) =>
|
||||
UpdateCountersCommand.Run(ref GetCommand<UpdateCountersCommand>(memory), threaded, renderer);
|
||||
|
||||
_lookup[(int)CommandType.BufferDispose] = (memory, threaded, renderer) =>
|
||||
BufferDisposeCommand.Run(ref GetCommand<BufferDisposeCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.BufferGetData] = (memory, threaded, renderer) =>
|
||||
BufferGetDataCommand.Run(ref GetCommand<BufferGetDataCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.BufferSetData] = (memory, threaded, renderer) =>
|
||||
BufferSetDataCommand.Run(ref GetCommand<BufferSetDataCommand>(memory), threaded, renderer);
|
||||
|
||||
_lookup[(int)CommandType.CounterEventDispose] = (memory, threaded, renderer) =>
|
||||
CounterEventDisposeCommand.Run(ref GetCommand<CounterEventDisposeCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.CounterEventFlush] = (memory, threaded, renderer) =>
|
||||
CounterEventFlushCommand.Run(ref GetCommand<CounterEventFlushCommand>(memory), threaded, renderer);
|
||||
|
||||
_lookup[(int)CommandType.ProgramDispose] = (memory, threaded, renderer) =>
|
||||
ProgramDisposeCommand.Run(ref GetCommand<ProgramDisposeCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.ProgramGetBinary] = (memory, threaded, renderer) =>
|
||||
ProgramGetBinaryCommand.Run(ref GetCommand<ProgramGetBinaryCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.ProgramCheckLink] = (memory, threaded, renderer) =>
|
||||
ProgramCheckLinkCommand.Run(ref GetCommand<ProgramCheckLinkCommand>(memory), threaded, renderer);
|
||||
|
||||
_lookup[(int)CommandType.SamplerDispose] = (memory, threaded, renderer) =>
|
||||
SamplerDisposeCommand.Run(ref GetCommand<SamplerDisposeCommand>(memory), threaded, renderer);
|
||||
|
||||
_lookup[(int)CommandType.TextureCopyTo] = (memory, threaded, renderer) =>
|
||||
TextureCopyToCommand.Run(ref GetCommand<TextureCopyToCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureCopyToScaled] = (memory, threaded, renderer) =>
|
||||
TextureCopyToScaledCommand.Run(ref GetCommand<TextureCopyToScaledCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureCopyToSlice] = (memory, threaded, renderer) =>
|
||||
TextureCopyToSliceCommand.Run(ref GetCommand<TextureCopyToSliceCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureCreateView] = (memory, threaded, renderer) =>
|
||||
TextureCreateViewCommand.Run(ref GetCommand<TextureCreateViewCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureGetData] = (memory, threaded, renderer) =>
|
||||
TextureGetDataCommand.Run(ref GetCommand<TextureGetDataCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureGetDataSlice] = (memory, threaded, renderer) =>
|
||||
TextureGetDataSliceCommand.Run(ref GetCommand<TextureGetDataSliceCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureRelease] = (memory, threaded, renderer) =>
|
||||
TextureReleaseCommand.Run(ref GetCommand<TextureReleaseCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureSetData] = (memory, threaded, renderer) =>
|
||||
TextureSetDataCommand.Run(ref GetCommand<TextureSetDataCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureSetDataSlice] = (memory, threaded, renderer) =>
|
||||
TextureSetDataSliceCommand.Run(ref GetCommand<TextureSetDataSliceCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureSetDataSliceRegion] = (memory, threaded, renderer) =>
|
||||
TextureSetDataSliceRegionCommand.Run(ref GetCommand<TextureSetDataSliceRegionCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureSetStorage] = (memory, threaded, renderer) =>
|
||||
TextureSetStorageCommand.Run(ref GetCommand<TextureSetStorageCommand>(memory), threaded, renderer);
|
||||
|
||||
_lookup[(int)CommandType.WindowPresent] = (memory, threaded, renderer) =>
|
||||
WindowPresentCommand.Run(ref GetCommand<WindowPresentCommand>(memory), threaded, renderer);
|
||||
|
||||
_lookup[(int)CommandType.Barrier] = (memory, threaded, renderer) =>
|
||||
BarrierCommand.Run(ref GetCommand<BarrierCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.BeginTransformFeedback] = (memory, threaded, renderer) =>
|
||||
BeginTransformFeedbackCommand.Run(ref GetCommand<BeginTransformFeedbackCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.ClearBuffer] = (memory, threaded, renderer) =>
|
||||
ClearBufferCommand.Run(ref GetCommand<ClearBufferCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.ClearRenderTargetColor] = (memory, threaded, renderer) =>
|
||||
ClearRenderTargetColorCommand.Run(ref GetCommand<ClearRenderTargetColorCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.ClearRenderTargetDepthStencil] = (memory, threaded, renderer) =>
|
||||
ClearRenderTargetDepthStencilCommand.Run(ref GetCommand<ClearRenderTargetDepthStencilCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.CommandBufferBarrier] = (memory, threaded, renderer) =>
|
||||
CommandBufferBarrierCommand.Run(ref GetCommand<CommandBufferBarrierCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.CopyBuffer] = (memory, threaded, renderer) =>
|
||||
CopyBufferCommand.Run(ref GetCommand<CopyBufferCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DispatchCompute] = (memory, threaded, renderer) =>
|
||||
DispatchComputeCommand.Run(ref GetCommand<DispatchComputeCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.Draw] = (memory, threaded, renderer) =>
|
||||
DrawCommand.Run(ref GetCommand<DrawCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndexed] = (memory, threaded, renderer) =>
|
||||
DrawIndexedCommand.Run(ref GetCommand<DrawIndexedCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndexedIndirect] = (memory, threaded, renderer) =>
|
||||
DrawIndexedIndirectCommand.Run(ref GetCommand<DrawIndexedIndirectCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndexedIndirectCount] = (memory, threaded, renderer) =>
|
||||
DrawIndexedIndirectCountCommand.Run(ref GetCommand<DrawIndexedIndirectCountCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndirect] = (memory, threaded, renderer) =>
|
||||
DrawIndirectCommand.Run(ref GetCommand<DrawIndirectCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawIndirectCount] = (memory, threaded, renderer) =>
|
||||
DrawIndirectCountCommand.Run(ref GetCommand<DrawIndirectCountCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.DrawTexture] = (memory, threaded, renderer) =>
|
||||
DrawTextureCommand.Run(ref GetCommand<DrawTextureCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.EndHostConditionalRendering] = (memory, threaded, renderer) =>
|
||||
EndHostConditionalRenderingCommand.Run(renderer);
|
||||
_lookup[(int)CommandType.EndTransformFeedback] = (memory, threaded, renderer) =>
|
||||
EndTransformFeedbackCommand.Run(ref GetCommand<EndTransformFeedbackCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetAlphaTest] = (memory, threaded, renderer) =>
|
||||
SetAlphaTestCommand.Run(ref GetCommand<SetAlphaTestCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetBlendState] = (memory, threaded, renderer) =>
|
||||
SetBlendStateCommand.Run(ref GetCommand<SetBlendStateCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetDepthBias] = (memory, threaded, renderer) =>
|
||||
SetDepthBiasCommand.Run(ref GetCommand<SetDepthBiasCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetDepthClamp] = (memory, threaded, renderer) =>
|
||||
SetDepthClampCommand.Run(ref GetCommand<SetDepthClampCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetDepthMode] = (memory, threaded, renderer) =>
|
||||
SetDepthModeCommand.Run(ref GetCommand<SetDepthModeCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetDepthTest] = (memory, threaded, renderer) =>
|
||||
SetDepthTestCommand.Run(ref GetCommand<SetDepthTestCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetFaceCulling] = (memory, threaded, renderer) =>
|
||||
SetFaceCullingCommand.Run(ref GetCommand<SetFaceCullingCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetFrontFace] = (memory, threaded, renderer) =>
|
||||
SetFrontFaceCommand.Run(ref GetCommand<SetFrontFaceCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetStorageBuffers] = (memory, threaded, renderer) =>
|
||||
SetStorageBuffersCommand.Run(ref GetCommand<SetStorageBuffersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetTransformFeedbackBuffers] = (memory, threaded, renderer) =>
|
||||
SetTransformFeedbackBuffersCommand.Run(ref GetCommand<SetTransformFeedbackBuffersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetUniformBuffers] = (memory, threaded, renderer) =>
|
||||
SetUniformBuffersCommand.Run(ref GetCommand<SetUniformBuffersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetImage] = (memory, threaded, renderer) =>
|
||||
SetImageCommand.Run(ref GetCommand<SetImageCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetIndexBuffer] = (memory, threaded, renderer) =>
|
||||
SetIndexBufferCommand.Run(ref GetCommand<SetIndexBufferCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetLineParameters] = (memory, threaded, renderer) =>
|
||||
SetLineParametersCommand.Run(ref GetCommand<SetLineParametersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetLogicOpState] = (memory, threaded, renderer) =>
|
||||
SetLogicOpStateCommand.Run(ref GetCommand<SetLogicOpStateCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetMultisampleState] = (memory, threaded, renderer) =>
|
||||
SetMultisampleStateCommand.Run(ref GetCommand<SetMultisampleStateCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPatchParameters] = (memory, threaded, renderer) =>
|
||||
SetPatchParametersCommand.Run(ref GetCommand<SetPatchParametersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPointParameters] = (memory, threaded, renderer) =>
|
||||
SetPointParametersCommand.Run(ref GetCommand<SetPointParametersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPolygonMode] = (memory, threaded, renderer) =>
|
||||
SetPolygonModeCommand.Run(ref GetCommand<SetPolygonModeCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPrimitiveRestart] = (memory, threaded, renderer) =>
|
||||
SetPrimitiveRestartCommand.Run(ref GetCommand<SetPrimitiveRestartCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPrimitiveTopology] = (memory, threaded, renderer) =>
|
||||
SetPrimitiveTopologyCommand.Run(ref GetCommand<SetPrimitiveTopologyCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetProgram] = (memory, threaded, renderer) =>
|
||||
SetProgramCommand.Run(ref GetCommand<SetProgramCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetRasterizerDiscard] = (memory, threaded, renderer) =>
|
||||
SetRasterizerDiscardCommand.Run(ref GetCommand<SetRasterizerDiscardCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetRenderTargetColorMasks] = (memory, threaded, renderer) =>
|
||||
SetRenderTargetColorMasksCommand.Run(ref GetCommand<SetRenderTargetColorMasksCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetRenderTargetScale] = (memory, threaded, renderer) =>
|
||||
SetRenderTargetScaleCommand.Run(ref GetCommand<SetRenderTargetScaleCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetRenderTargets] = (memory, threaded, renderer) =>
|
||||
SetRenderTargetsCommand.Run(ref GetCommand<SetRenderTargetsCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetScissor] = (memory, threaded, renderer) =>
|
||||
SetScissorsCommand.Run(ref GetCommand<SetScissorsCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetStencilTest] = (memory, threaded, renderer) =>
|
||||
SetStencilTestCommand.Run(ref GetCommand<SetStencilTestCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetTextureAndSampler] = (memory, threaded, renderer) =>
|
||||
SetTextureAndSamplerCommand.Run(ref GetCommand<SetTextureAndSamplerCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetUserClipDistance] = (memory, threaded, renderer) =>
|
||||
SetUserClipDistanceCommand.Run(ref GetCommand<SetUserClipDistanceCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetVertexAttribs] = (memory, threaded, renderer) =>
|
||||
SetVertexAttribsCommand.Run(ref GetCommand<SetVertexAttribsCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetVertexBuffers] = (memory, threaded, renderer) =>
|
||||
SetVertexBuffersCommand.Run(ref GetCommand<SetVertexBuffersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetViewports] = (memory, threaded, renderer) =>
|
||||
SetViewportsCommand.Run(ref GetCommand<SetViewportsCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureBarrier] = (memory, threaded, renderer) =>
|
||||
TextureBarrierCommand.Run(ref GetCommand<TextureBarrierCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TextureBarrierTiled] = (memory, threaded, renderer) =>
|
||||
TextureBarrierTiledCommand.Run(ref GetCommand<TextureBarrierTiledCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TryHostConditionalRendering] = (memory, threaded, renderer) =>
|
||||
TryHostConditionalRenderingCommand.Run(ref GetCommand<TryHostConditionalRenderingCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.TryHostConditionalRenderingFlush] = (memory, threaded, renderer) =>
|
||||
TryHostConditionalRenderingFlushCommand.Run(ref GetCommand<TryHostConditionalRenderingFlushCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.UpdateRenderScale] = (memory, threaded, renderer) =>
|
||||
UpdateRenderScaleCommand.Run(ref GetCommand<UpdateRenderScaleCommand>(memory), threaded, renderer);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void RunCommand(Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
_lookup[memory[memory.Length - 1]](memory, threaded, renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
interface IGALCommand
|
||||
{
|
||||
CommandType CommandType { get; }
|
||||
}
|
||||
}
|
||||
@@ -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,117 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// A texture cache that automatically removes older textures that are not used for some time.
|
||||
/// The cache works with a rotated list with a fixed size. When new textures are added, the
|
||||
/// old ones at the bottom of the list are deleted.
|
||||
/// </summary>
|
||||
class AutoDeleteCache : IEnumerable<Texture>
|
||||
{
|
||||
private const int MaxCapacity = 2048;
|
||||
|
||||
private readonly LinkedList<Texture> _textures;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the automatic deletion cache.
|
||||
/// </summary>
|
||||
public AutoDeleteCache()
|
||||
{
|
||||
_textures = new LinkedList<Texture>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new texture to the cache, even if the texture added is already on the cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using this method is only recommended if you know that the texture is not yet on the cache,
|
||||
/// otherwise it would store the same texture more than once.
|
||||
/// </remarks>
|
||||
/// <param name="texture">The texture to be added to the cache</param>
|
||||
public void Add(Texture texture)
|
||||
{
|
||||
texture.IncrementReferenceCount();
|
||||
|
||||
texture.CacheNode = _textures.AddLast(texture);
|
||||
|
||||
if (_textures.Count > MaxCapacity)
|
||||
{
|
||||
Texture oldestTexture = _textures.First.Value;
|
||||
|
||||
if (!oldestTexture.CheckModified(false))
|
||||
{
|
||||
// The texture must be flushed if it falls out of the auto delete cache.
|
||||
// Flushes out of the auto delete cache do not trigger write tracking,
|
||||
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
||||
|
||||
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
||||
oldestTexture.FlushModified(false);
|
||||
}
|
||||
|
||||
_textures.RemoveFirst();
|
||||
|
||||
oldestTexture.DecrementReferenceCount();
|
||||
|
||||
oldestTexture.CacheNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new texture to the cache, or just moves it to the top of the list if the
|
||||
/// texture is already on the cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Moving the texture to the top of the list prevents it from being deleted,
|
||||
/// as the textures on the bottom of the list are deleted when new ones are added.
|
||||
/// </remarks>
|
||||
/// <param name="texture">The texture to be added, or moved to the top</param>
|
||||
public void Lift(Texture texture)
|
||||
{
|
||||
if (texture.CacheNode != null)
|
||||
{
|
||||
if (texture.CacheNode != _textures.Last)
|
||||
{
|
||||
_textures.Remove(texture.CacheNode);
|
||||
|
||||
texture.CacheNode = _textures.AddLast(texture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(texture);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(Texture texture, bool flush)
|
||||
{
|
||||
if (texture.CacheNode == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove our reference to this texture.
|
||||
if (flush)
|
||||
{
|
||||
texture.FlushModified(false);
|
||||
}
|
||||
|
||||
_textures.Remove(texture.CacheNode);
|
||||
|
||||
texture.CacheNode = null;
|
||||
|
||||
return texture.DecrementReferenceCount();
|
||||
}
|
||||
|
||||
public IEnumerator<Texture> GetEnumerator()
|
||||
{
|
||||
return _textures.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _textures.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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,777 +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);
|
||||
}
|
||||
|
||||
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,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,202 +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)
|
||||
{
|
||||
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | AggregateType.FP32, 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,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,968 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Ryujinx.Graphics.Vulkan.Shaders;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class HelperShader : IDisposable
|
||||
{
|
||||
private const int UniformBufferAlignment = 256;
|
||||
|
||||
private readonly PipelineHelperShader _pipeline;
|
||||
private readonly ISampler _samplerLinear;
|
||||
private readonly ISampler _samplerNearest;
|
||||
private readonly IProgram _programColorBlit;
|
||||
private readonly IProgram _programColorBlitClearAlpha;
|
||||
private readonly IProgram _programColorClear;
|
||||
private readonly IProgram _programStrideChange;
|
||||
private readonly IProgram _programConvertIndexBuffer;
|
||||
private readonly IProgram _programConvertIndirectData;
|
||||
private readonly IProgram _programColorCopyToNonMs;
|
||||
private readonly IProgram _programColorDrawToMs;
|
||||
|
||||
public HelperShader(VulkanRenderer gd, Device device)
|
||||
{
|
||||
_pipeline = new PipelineHelperShader(gd, device);
|
||||
_pipeline.Initialize();
|
||||
|
||||
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
||||
|
||||
var colorBlitVertexBindings = new ShaderBindings(
|
||||
new[] { 1 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
var colorBlitFragmentBindings = new ShaderBindings(
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
new[] { 0 },
|
||||
Array.Empty<int>());
|
||||
|
||||
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, colorBlitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
|
||||
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, colorBlitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
|
||||
var colorClearFragmentBindings = new ShaderBindings(
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
_programColorClear = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
|
||||
var strideChangeBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
new[] { 1, 2 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
|
||||
var colorCopyToNonMsBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
Array.Empty<int>(),
|
||||
new[] { 0 },
|
||||
new[] { 0 });
|
||||
|
||||
_programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyToNonMsBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
|
||||
var colorDrawToMsVertexBindings = new ShaderBindings(
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
var colorDrawToMsFragmentBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
Array.Empty<int>(),
|
||||
new[] { 0 },
|
||||
Array.Empty<int>());
|
||||
|
||||
_programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||
new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||
});
|
||||
|
||||
var convertIndexBufferBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
new[] { 1, 2 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
|
||||
var convertIndirectDataBindings = new ShaderBindings(
|
||||
new[] { 0 },
|
||||
new[] { 1, 2, 3 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
|
||||
{
|
||||
new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv),
|
||||
});
|
||||
}
|
||||
|
||||
public void Blit(
|
||||
VulkanRenderer gd,
|
||||
TextureView src,
|
||||
Auto<DisposableImageView> dst,
|
||||
int dstWidth,
|
||||
int dstHeight,
|
||||
VkFormat dstFormat,
|
||||
Extents2D srcRegion,
|
||||
Extents2D dstRegion,
|
||||
bool linearFilter,
|
||||
bool clearAlpha = false)
|
||||
{
|
||||
gd.FlushAllCommands();
|
||||
|
||||
using var cbs = gd.CommandBufferPool.Rent();
|
||||
|
||||
Blit(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha);
|
||||
}
|
||||
|
||||
public void Blit(
|
||||
VulkanRenderer gd,
|
||||
CommandBufferScoped cbs,
|
||||
TextureView src,
|
||||
Auto<DisposableImageView> dst,
|
||||
int dstWidth,
|
||||
int dstHeight,
|
||||
VkFormat dstFormat,
|
||||
Extents2D srcRegion,
|
||||
Extents2D dstRegion,
|
||||
bool linearFilter,
|
||||
bool clearAlpha = false)
|
||||
{
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
|
||||
const int RegionBufferSize = 16;
|
||||
|
||||
var sampler = linearFilter ? _samplerLinear : _samplerNearest;
|
||||
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler);
|
||||
|
||||
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
|
||||
|
||||
region[0] = (float)srcRegion.X1 / src.Width;
|
||||
region[1] = (float)srcRegion.X2 / src.Width;
|
||||
region[2] = (float)srcRegion.Y1 / src.Height;
|
||||
region[3] = (float)srcRegion.Y2 / src.Height;
|
||||
|
||||
if (dstRegion.X1 > dstRegion.X2)
|
||||
{
|
||||
(region[0], region[1]) = (region[1], region[0]);
|
||||
}
|
||||
|
||||
if (dstRegion.Y1 > dstRegion.Y2)
|
||||
{
|
||||
(region[2], region[3]) = (region[3], region[2]);
|
||||
}
|
||||
|
||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
||||
|
||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) });
|
||||
|
||||
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||
|
||||
var rect = new Rectangle<float>(
|
||||
MathF.Min(dstRegion.X1, dstRegion.X2),
|
||||
MathF.Min(dstRegion.Y1, dstRegion.Y2),
|
||||
MathF.Abs(dstRegion.X2 - dstRegion.X1),
|
||||
MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
|
||||
|
||||
viewports[0] = new GAL.Viewport(
|
||||
rect,
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||
|
||||
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
||||
|
||||
_pipeline.SetProgram(clearAlpha ? _programColorBlitClearAlpha : _programColorBlit);
|
||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
|
||||
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
||||
_pipeline.SetScissors(scissors);
|
||||
|
||||
if (clearAlpha)
|
||||
{
|
||||
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
|
||||
}
|
||||
|
||||
_pipeline.SetViewports(viewports, false);
|
||||
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
||||
_pipeline.Draw(4, 1, 0, 0);
|
||||
_pipeline.Finish(gd, cbs);
|
||||
|
||||
gd.BufferManager.Delete(bufferHandle);
|
||||
}
|
||||
|
||||
public void Clear(
|
||||
VulkanRenderer gd,
|
||||
Auto<DisposableImageView> dst,
|
||||
ReadOnlySpan<float> clearColor,
|
||||
uint componentMask,
|
||||
int dstWidth,
|
||||
int dstHeight,
|
||||
VkFormat dstFormat,
|
||||
Rectangle<int> scissor)
|
||||
{
|
||||
const int ClearColorBufferSize = 16;
|
||||
|
||||
gd.FlushAllCommands();
|
||||
|
||||
using var cbs = gd.CommandBufferPool.Rent();
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
|
||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize, false);
|
||||
|
||||
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
|
||||
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) });
|
||||
|
||||
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||
|
||||
viewports[0] = new GAL.Viewport(
|
||||
new Rectangle<float>(0, 0, dstWidth, dstHeight),
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||
|
||||
scissors[0] = scissor;
|
||||
|
||||
_pipeline.SetProgram(_programColorClear);
|
||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
|
||||
_pipeline.SetRenderTargetColorMasks(new uint[] { componentMask });
|
||||
_pipeline.SetViewports(viewports, false);
|
||||
_pipeline.SetScissors(scissors);
|
||||
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
||||
_pipeline.Draw(4, 1, 0, 0);
|
||||
_pipeline.Finish();
|
||||
|
||||
gd.BufferManager.Delete(bufferHandle);
|
||||
}
|
||||
|
||||
public void DrawTexture(
|
||||
VulkanRenderer gd,
|
||||
PipelineBase pipeline,
|
||||
TextureView src,
|
||||
ISampler srcSampler,
|
||||
Extents2DF srcRegion,
|
||||
Extents2DF dstRegion)
|
||||
{
|
||||
const int RegionBufferSize = 16;
|
||||
|
||||
pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler);
|
||||
|
||||
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
|
||||
|
||||
region[0] = srcRegion.X1 / src.Width;
|
||||
region[1] = srcRegion.X2 / src.Width;
|
||||
region[2] = srcRegion.Y1 / src.Height;
|
||||
region[3] = srcRegion.Y2 / src.Height;
|
||||
|
||||
if (dstRegion.X1 > dstRegion.X2)
|
||||
{
|
||||
(region[0], region[1]) = (region[1], region[0]);
|
||||
}
|
||||
|
||||
if (dstRegion.Y1 > dstRegion.Y2)
|
||||
{
|
||||
(region[2], region[3]) = (region[3], region[2]);
|
||||
}
|
||||
|
||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
|
||||
|
||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||
|
||||
pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) });
|
||||
|
||||
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||
|
||||
var rect = new Rectangle<float>(
|
||||
MathF.Min(dstRegion.X1, dstRegion.X2),
|
||||
MathF.Min(dstRegion.Y1, dstRegion.Y2),
|
||||
MathF.Abs(dstRegion.X2 - dstRegion.X1),
|
||||
MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
|
||||
|
||||
viewports[0] = new GAL.Viewport(
|
||||
rect,
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||
|
||||
pipeline.SetProgram(_programColorBlit);
|
||||
pipeline.SetViewports(viewports, false);
|
||||
pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
||||
pipeline.Draw(4, 1, 0, 0);
|
||||
|
||||
gd.BufferManager.Delete(bufferHandle);
|
||||
}
|
||||
|
||||
public unsafe void ConvertI8ToI16(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
|
||||
{
|
||||
ChangeStride(gd, cbs, src, dst, srcOffset, size, 1, 2);
|
||||
}
|
||||
|
||||
public unsafe void ChangeStride(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size, int stride, int newStride)
|
||||
{
|
||||
bool supportsUint8 = gd.Capabilities.SupportsShaderInt8;
|
||||
|
||||
int elems = size / stride;
|
||||
int newSize = elems * newStride;
|
||||
|
||||
var srcBufferAuto = src.GetBuffer();
|
||||
var dstBufferAuto = dst.GetBuffer();
|
||||
|
||||
var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value;
|
||||
var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value;
|
||||
|
||||
var access = supportsUint8 ? AccessFlags.ShaderWriteBit : AccessFlags.TransferWriteBit;
|
||||
var stage = supportsUint8 ? PipelineStageFlags.ComputeShaderBit : PipelineStageFlags.TransferBit;
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
access,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
stage,
|
||||
0,
|
||||
newSize);
|
||||
|
||||
if (supportsUint8)
|
||||
{
|
||||
const int ParamsBufferSize = 16;
|
||||
|
||||
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
|
||||
|
||||
shaderParams[0] = stride;
|
||||
shaderParams[1] = newStride;
|
||||
shaderParams[2] = size;
|
||||
shaderParams[3] = srcOffset;
|
||||
|
||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
||||
|
||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
|
||||
|
||||
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
|
||||
|
||||
sbRanges[0] = srcBufferAuto;
|
||||
sbRanges[1] = dstBufferAuto;
|
||||
|
||||
_pipeline.SetStorageBuffers(1, sbRanges);
|
||||
|
||||
_pipeline.SetProgram(_programStrideChange);
|
||||
_pipeline.DispatchCompute(1, 1, 1);
|
||||
|
||||
gd.BufferManager.Delete(bufferHandle);
|
||||
|
||||
_pipeline.Finish(gd, cbs);
|
||||
}
|
||||
else
|
||||
{
|
||||
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
|
||||
|
||||
var bufferCopy = new BufferCopy[elems];
|
||||
|
||||
for (ulong i = 0; i < (ulong)elems; i++)
|
||||
{
|
||||
bufferCopy[i] = new BufferCopy((ulong)srcOffset + i * (ulong)stride, i * (ulong)newStride, (ulong)stride);
|
||||
}
|
||||
|
||||
fixed (BufferCopy* pBufferCopy = bufferCopy)
|
||||
{
|
||||
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)elems, pBufferCopy);
|
||||
}
|
||||
}
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
access,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
stage,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
0,
|
||||
newSize);
|
||||
}
|
||||
|
||||
public unsafe void ConvertIndexBuffer(VulkanRenderer gd,
|
||||
CommandBufferScoped cbs,
|
||||
BufferHolder src,
|
||||
BufferHolder dst,
|
||||
IndexBufferPattern pattern,
|
||||
int indexSize,
|
||||
int srcOffset,
|
||||
int indexCount)
|
||||
{
|
||||
// TODO: Support conversion with primitive restart enabled.
|
||||
// TODO: Convert with a compute shader?
|
||||
|
||||
int convertedCount = pattern.GetConvertedCount(indexCount);
|
||||
int outputIndexSize = 4;
|
||||
|
||||
var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
|
||||
var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
|
||||
|
||||
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
|
||||
|
||||
var bufferCopy = new List<BufferCopy>();
|
||||
int outputOffset = 0;
|
||||
|
||||
// Try to merge copies of adjacent indices to reduce copy count.
|
||||
int sequenceStart = 0;
|
||||
int sequenceLength = 0;
|
||||
|
||||
foreach (var index in pattern.GetIndexMapping(indexCount))
|
||||
{
|
||||
if (sequenceLength > 0)
|
||||
{
|
||||
if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize)
|
||||
{
|
||||
sequenceLength++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Commit the copy so far.
|
||||
bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
|
||||
outputOffset += outputIndexSize * sequenceLength;
|
||||
}
|
||||
|
||||
sequenceStart = index;
|
||||
sequenceLength = 1;
|
||||
}
|
||||
|
||||
if (sequenceLength > 0)
|
||||
{
|
||||
// Commit final pending copy.
|
||||
bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
|
||||
}
|
||||
|
||||
var bufferCopyArray = bufferCopy.ToArray();
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
AccessFlags.TransferWriteBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
PipelineStageFlags.TransferBit,
|
||||
0,
|
||||
convertedCount * outputIndexSize);
|
||||
|
||||
fixed (BufferCopy* pBufferCopy = bufferCopyArray)
|
||||
{
|
||||
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy);
|
||||
}
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
AccessFlags.TransferWriteBit,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
PipelineStageFlags.TransferBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
0,
|
||||
convertedCount * outputIndexSize);
|
||||
}
|
||||
|
||||
public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
|
||||
{
|
||||
const int ParamsBufferSize = 16;
|
||||
|
||||
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
|
||||
|
||||
int samples = src.Info.Samples;
|
||||
|
||||
// X and Y are the expected texture samples.
|
||||
// Z and W are the actual texture samples used.
|
||||
// They may differ if the GPU does not support the samples count requested and we had to use a lower amount.
|
||||
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
||||
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
||||
|
||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
||||
|
||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||
|
||||
TextureView.InsertImageBarrier(
|
||||
gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
src.GetImage().Get(cbs).Value,
|
||||
TextureStorage.DefaultAccessMask,
|
||||
AccessFlags.ShaderReadBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
PipelineStageFlags.ComputeShaderBit,
|
||||
ImageAspectFlags.ColorBit,
|
||||
src.FirstLayer + srcLayer,
|
||||
src.FirstLevel,
|
||||
depth,
|
||||
1);
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
|
||||
_pipeline.SetProgram(_programColorCopyToNonMs);
|
||||
|
||||
var format = GetFormat(src.Info.BytesPerPixel);
|
||||
|
||||
int dispatchX = (dst.Info.Width + 31) / 32;
|
||||
int dispatchY = (dst.Info.Height + 31) / 32;
|
||||
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
|
||||
|
||||
if (src.Info.Target == Target.Texture2DMultisampleArray ||
|
||||
dst.Info.Target == Target.Texture2DMultisampleArray)
|
||||
{
|
||||
for (int z = 0; z < depth; z++)
|
||||
{
|
||||
var srcView = Create2DLayerView(src, srcLayer + z, format);
|
||||
var dstView = Create2DLayerView(dst, dstLayer + z);
|
||||
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
|
||||
_pipeline.SetImage(0, dstView, format);
|
||||
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
|
||||
srcView.Release();
|
||||
dstView.Release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var srcView = Create2DLayerView(src, srcLayer, format);
|
||||
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
|
||||
_pipeline.SetImage(0, dst, format);
|
||||
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
|
||||
srcView.Release();
|
||||
}
|
||||
|
||||
gd.BufferManager.Delete(bufferHandle);
|
||||
|
||||
_pipeline.Finish(gd, cbs);
|
||||
|
||||
TextureView.InsertImageBarrier(
|
||||
gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
dst.GetImage().Get(cbs).Value,
|
||||
AccessFlags.ShaderWriteBit,
|
||||
TextureStorage.DefaultAccessMask,
|
||||
PipelineStageFlags.ComputeShaderBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
ImageAspectFlags.ColorBit,
|
||||
dst.FirstLayer + dstLayer,
|
||||
dst.FirstLevel,
|
||||
depth,
|
||||
1);
|
||||
}
|
||||
|
||||
public void CopyNonMSToMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
|
||||
{
|
||||
const int ParamsBufferSize = 16;
|
||||
|
||||
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
|
||||
|
||||
int samples = dst.Info.Samples;
|
||||
|
||||
// X and Y are the expected texture samples.
|
||||
// Z and W are the actual texture samples used.
|
||||
// They may differ if the GPU does not support the samples count requested and we had to use a lower amount.
|
||||
(shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
|
||||
(shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
|
||||
|
||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
|
||||
|
||||
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
|
||||
|
||||
TextureView.InsertImageBarrier(
|
||||
gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
src.GetImage().Get(cbs).Value,
|
||||
TextureStorage.DefaultAccessMask,
|
||||
AccessFlags.ShaderReadBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
PipelineStageFlags.FragmentShaderBit,
|
||||
ImageAspectFlags.ColorBit,
|
||||
src.FirstLayer + srcLayer,
|
||||
src.FirstLevel,
|
||||
depth,
|
||||
1);
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
|
||||
_pipeline.SetProgram(_programColorDrawToMs);
|
||||
|
||||
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||
|
||||
var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height);
|
||||
|
||||
viewports[0] = new GAL.Viewport(
|
||||
rect,
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||
|
||||
scissors[0] = new Rectangle<int>(0, 0, dst.Width, dst.Height);
|
||||
|
||||
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
||||
_pipeline.SetScissors(scissors);
|
||||
_pipeline.SetViewports(viewports, false);
|
||||
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
||||
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
|
||||
|
||||
var format = GetFormat(src.Info.BytesPerPixel);
|
||||
var vkFormat = FormatTable.GetFormat(format);
|
||||
|
||||
if (src.Info.Target == Target.Texture2DMultisampleArray ||
|
||||
dst.Info.Target == Target.Texture2DMultisampleArray)
|
||||
{
|
||||
for (int z = 0; z < depth; z++)
|
||||
{
|
||||
var srcView = Create2DLayerView(src, srcLayer + z, format);
|
||||
var dstView = Create2DLayerView(dst, dstLayer + z);
|
||||
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
|
||||
_pipeline.SetRenderTarget(
|
||||
((TextureView)dstView).GetView(format).GetImageViewForAttachment(),
|
||||
(uint)dst.Width,
|
||||
(uint)dst.Height,
|
||||
(uint)samples,
|
||||
false,
|
||||
vkFormat);
|
||||
|
||||
_pipeline.Draw(4, 1, 0, 0);
|
||||
|
||||
srcView.Release();
|
||||
dstView.Release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var srcView = Create2DLayerView(src, srcLayer, format);
|
||||
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
|
||||
_pipeline.SetRenderTarget(
|
||||
dst.GetView(format).GetImageViewForAttachment(),
|
||||
(uint)dst.Width,
|
||||
(uint)dst.Height,
|
||||
(uint)samples,
|
||||
false,
|
||||
vkFormat);
|
||||
|
||||
_pipeline.Draw(4, 1, 0, 0);
|
||||
|
||||
srcView.Release();
|
||||
}
|
||||
|
||||
gd.BufferManager.Delete(bufferHandle);
|
||||
|
||||
_pipeline.Finish(gd, cbs);
|
||||
|
||||
TextureView.InsertImageBarrier(
|
||||
gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
dst.GetImage().Get(cbs).Value,
|
||||
AccessFlags.ColorAttachmentWriteBit,
|
||||
TextureStorage.DefaultAccessMask,
|
||||
PipelineStageFlags.FragmentShaderBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
ImageAspectFlags.ColorBit,
|
||||
dst.FirstLayer + dstLayer,
|
||||
dst.FirstLevel,
|
||||
depth,
|
||||
1);
|
||||
}
|
||||
|
||||
private static (int, int) GetSampleCountXYLog2(int samples)
|
||||
{
|
||||
int samplesInXLog2 = 0;
|
||||
int samplesInYLog2 = 0;
|
||||
|
||||
switch (samples)
|
||||
{
|
||||
case 2: // 2x1
|
||||
samplesInXLog2 = 1;
|
||||
break;
|
||||
case 4: // 2x2
|
||||
samplesInXLog2 = 1;
|
||||
samplesInYLog2 = 1;
|
||||
break;
|
||||
case 8: // 4x2
|
||||
samplesInXLog2 = 2;
|
||||
samplesInYLog2 = 1;
|
||||
break;
|
||||
case 16: // 4x4
|
||||
samplesInXLog2 = 2;
|
||||
samplesInYLog2 = 2;
|
||||
break;
|
||||
case 32: // 8x4
|
||||
samplesInXLog2 = 3;
|
||||
samplesInYLog2 = 2;
|
||||
break;
|
||||
case 64: // 8x8
|
||||
samplesInXLog2 = 3;
|
||||
samplesInYLog2 = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
return (samplesInXLog2, samplesInYLog2);
|
||||
}
|
||||
|
||||
private static ITexture Create2DLayerView(TextureView from, int layer, GAL.Format? format = null)
|
||||
{
|
||||
var target = from.Info.Target switch
|
||||
{
|
||||
Target.Texture1DArray => Target.Texture1D,
|
||||
Target.Texture2DArray => Target.Texture2D,
|
||||
Target.Texture2DMultisampleArray => Target.Texture2DMultisample,
|
||||
_ => from.Info.Target
|
||||
};
|
||||
|
||||
var info = new TextureCreateInfo(
|
||||
from.Info.Width,
|
||||
from.Info.Height,
|
||||
from.Info.Depth,
|
||||
1,
|
||||
from.Info.Samples,
|
||||
from.Info.BlockWidth,
|
||||
from.Info.BlockHeight,
|
||||
from.Info.BytesPerPixel,
|
||||
format ?? from.Info.Format,
|
||||
from.Info.DepthStencilMode,
|
||||
target,
|
||||
from.Info.SwizzleR,
|
||||
from.Info.SwizzleG,
|
||||
from.Info.SwizzleB,
|
||||
from.Info.SwizzleA);
|
||||
|
||||
return from.CreateView(info, layer, 0);
|
||||
}
|
||||
|
||||
private static GAL.Format GetFormat(int bytesPerPixel)
|
||||
{
|
||||
return bytesPerPixel switch
|
||||
{
|
||||
1 => GAL.Format.R8Uint,
|
||||
2 => GAL.Format.R16Uint,
|
||||
4 => GAL.Format.R32Uint,
|
||||
8 => GAL.Format.R32G32Uint,
|
||||
16 => GAL.Format.R32G32B32A32Uint,
|
||||
_ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.")
|
||||
};
|
||||
}
|
||||
|
||||
public void ConvertIndexBufferIndirect(
|
||||
VulkanRenderer gd,
|
||||
CommandBufferScoped cbs,
|
||||
BufferHolder srcIndirectBuffer,
|
||||
BufferHolder dstIndirectBuffer,
|
||||
BufferRange drawCountBuffer,
|
||||
BufferHolder srcIndexBuffer,
|
||||
BufferHolder dstIndexBuffer,
|
||||
IndexBufferPattern pattern,
|
||||
int indexSize,
|
||||
int srcIndexBufferOffset,
|
||||
int srcIndexBufferSize,
|
||||
int srcIndirectBufferOffset,
|
||||
bool hasDrawCount,
|
||||
int maxDrawCount,
|
||||
int indirectDataStride)
|
||||
{
|
||||
// TODO: Support conversion with primitive restart enabled.
|
||||
|
||||
BufferRange drawCountBufferAligned = new BufferRange(
|
||||
drawCountBuffer.Handle,
|
||||
drawCountBuffer.Offset & ~(UniformBufferAlignment - 1),
|
||||
UniformBufferAlignment);
|
||||
|
||||
int indirectDataSize = maxDrawCount * indirectDataStride;
|
||||
|
||||
int indexCount = srcIndexBufferSize / indexSize;
|
||||
int primitivesCount = pattern.GetPrimitiveCount(indexCount);
|
||||
int convertedCount = pattern.GetConvertedCount(indexCount);
|
||||
int outputIndexSize = 4;
|
||||
|
||||
var srcBuffer = srcIndexBuffer.GetBuffer().Get(cbs, srcIndexBufferOffset, indexCount * indexSize).Value;
|
||||
var dstBuffer = dstIndexBuffer.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
|
||||
|
||||
const int ParamsBufferSize = 24 * sizeof(int);
|
||||
const int ParamsIndirectDispatchOffset = 16 * sizeof(int);
|
||||
const int ParamsIndirectDispatchSize = 3 * sizeof(int);
|
||||
|
||||
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
|
||||
|
||||
shaderParams[8] = pattern.PrimitiveVertices;
|
||||
shaderParams[9] = pattern.PrimitiveVerticesOut;
|
||||
shaderParams[10] = indexSize;
|
||||
shaderParams[11] = outputIndexSize;
|
||||
shaderParams[12] = pattern.BaseIndex;
|
||||
shaderParams[13] = pattern.IndexStride;
|
||||
shaderParams[14] = srcIndexBufferOffset;
|
||||
shaderParams[15] = primitivesCount;
|
||||
shaderParams[16] = 1;
|
||||
shaderParams[17] = 1;
|
||||
shaderParams[18] = 1;
|
||||
shaderParams[19] = hasDrawCount ? 1 : 0;
|
||||
shaderParams[20] = maxDrawCount;
|
||||
shaderParams[21] = (drawCountBuffer.Offset & (UniformBufferAlignment - 1)) / 4;
|
||||
shaderParams[22] = indirectDataStride / 4;
|
||||
shaderParams[23] = srcIndirectBufferOffset / 4;
|
||||
|
||||
pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length));
|
||||
|
||||
var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false, out var patternBuffer);
|
||||
var patternBufferAuto = patternBuffer.GetBuffer();
|
||||
|
||||
gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams);
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
srcIndirectBuffer.GetBuffer().Get(cbs, srcIndirectBufferOffset, indirectDataSize).Value,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
AccessFlags.ShaderReadBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
PipelineStageFlags.ComputeShaderBit,
|
||||
srcIndirectBufferOffset,
|
||||
indirectDataSize);
|
||||
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, drawCountBufferAligned) });
|
||||
_pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() });
|
||||
|
||||
_pipeline.SetProgram(_programConvertIndirectData);
|
||||
_pipeline.DispatchCompute(1, 1, 1);
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value,
|
||||
AccessFlags.ShaderWriteBit,
|
||||
AccessFlags.IndirectCommandReadBit,
|
||||
PipelineStageFlags.ComputeShaderBit,
|
||||
PipelineStageFlags.DrawIndirectBit,
|
||||
ParamsIndirectDispatchOffset,
|
||||
ParamsIndirectDispatchSize);
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
AccessFlags.TransferWriteBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
PipelineStageFlags.TransferBit,
|
||||
0,
|
||||
convertedCount * outputIndexSize);
|
||||
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(patternBufferHandle, 0, ParamsBufferSize)) });
|
||||
_pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() });
|
||||
|
||||
_pipeline.SetProgram(_programConvertIndexBuffer);
|
||||
_pipeline.DispatchComputeIndirect(patternBufferAuto, ParamsIndirectDispatchOffset);
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
AccessFlags.TransferWriteBit,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
PipelineStageFlags.TransferBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
0,
|
||||
convertedCount * outputIndexSize);
|
||||
|
||||
gd.BufferManager.Delete(patternBufferHandle);
|
||||
|
||||
_pipeline.Finish(gd, cbs);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_programColorBlitClearAlpha.Dispose();
|
||||
_programColorBlit.Dispose();
|
||||
_programColorClear.Dispose();
|
||||
_programStrideChange.Dispose();
|
||||
_programConvertIndexBuffer.Dispose();
|
||||
_programConvertIndirectData.Dispose();
|
||||
_programColorCopyToNonMs.Dispose();
|
||||
_programColorDrawToMs.Dispose();
|
||||
_samplerNearest.Dispose();
|
||||
_samplerLinear.Dispose();
|
||||
_pipeline.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,635 +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;
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
|
||||
var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
|
||||
PNext = pExtendedFeatures,
|
||||
NullDescriptor = true
|
||||
};
|
||||
|
||||
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,900 +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)
|
||||
{
|
||||
if (romFsFile != null)
|
||||
{
|
||||
_device.Configuration.VirtualFileSystem.LoadRomFs(romFsFile);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
LoadExeFs(codeFs, string.Empty, metaData);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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(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));
|
||||
}
|
||||
|
||||
LoadExeFs(codeFs, displayVersion, metaData);
|
||||
|
||||
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 void 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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
_device.Configuration.VirtualFileSystem.SetRomFs(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);
|
||||
|
||||
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,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Pm
|
||||
{
|
||||
[Service("pm:info")]
|
||||
class IInformationInterface : IpcService
|
||||
{
|
||||
public IInformationInterface(ServiceCtx context) { }
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user