Compare commits
181 Commits
Author | SHA1 | Date | |
---|---|---|---|
986ac9ff83 | |||
42b9c1e8fe | |||
3b375525fb | |||
e6658c133c | |||
5b42a4d2c4 | |||
8f0c89ffd6 | |||
2c9715acf6 | |||
274af65f69 | |||
4ca78eded5 | |||
6cb6b15612 | |||
2725e40838 | |||
c2e4c8f98e | |||
b53e7ffd46 | |||
ac66643346 | |||
21e88f17f6 | |||
5626f2ca1c | |||
402f05b8ef | |||
fb27042e01 | |||
69a9de33d3 | |||
bba51c2eeb | |||
fc26189fe1 | |||
a40c90e7dd | |||
f864a49014 | |||
ecbf303266 | |||
b3bf05356b | |||
cb4b58052f | |||
f8cdd5f484 | |||
22202be394 | |||
17ba217940 | |||
aae4595bdb | |||
880fd3cfcb | |||
f679f25e08 | |||
c2709b3bdd | |||
2b6e81deea | |||
7271f1b18e | |||
5fda543f84 | |||
95c06de4c1 | |||
49c63ea077 | |||
531da8a1c0 | |||
5cbdfbc7a4 | |||
e0544dd9c7 | |||
aa784c3e5e | |||
9205077590 | |||
0ed40c7175 | |||
40d47b7aa2 | |||
ec0bb74968 | |||
42f7f98666 | |||
95bad6995c | |||
3d42995822 | |||
9095941fd1 | |||
ba71141bdc | |||
0a0675a7f6 | |||
a7c6e6a8cf | |||
0bc8151c7e | |||
40c17673f5 | |||
a8950d6ac4 | |||
162798b026 | |||
1b28ecd63e | |||
895d9b53bc | |||
0e06aace45 | |||
adf4ebcd60 | |||
470a8031a4 | |||
5440d4ad5c | |||
dde208b480 | |||
4c3d2d5d75 | |||
fab11ba3f1 | |||
332891b5ff | |||
7df4fcada7 | |||
d6698680be | |||
e5c9838b0b | |||
f8ec878796 | |||
9ff21f9ab6 | |||
aa021085cf | |||
1f5d881860 | |||
1f664100bd | |||
1f5e1ffa80 | |||
264438ff19 | |||
3b8ac1641a | |||
4250732353 | |||
4d1579acbf | |||
6279f5e430 | |||
b7d2bff6aa | |||
7c327fecb3 | |||
cc1a933a2f | |||
dd574146fb | |||
2c94ac455e | |||
e18d258fa0 | |||
36f10df775 | |||
680e548022 | |||
21c4176157 | |||
3b4ff2d6d9 | |||
12504f280c | |||
250fc51374 | |||
206e0882c2 | |||
609abc8b9b | |||
cee7121058 | |||
cd124bda58 | |||
9f12e50a54 | |||
097562bc6c | |||
db4242c5dc | |||
4dd77316f7 | |||
3f98369a17 | |||
c26aeefe03 | |||
666e05f5cb | |||
8d9d508dc7 | |||
e27f5522e2 | |||
add2a9d151 | |||
9e50dd99d7 | |||
0dec91bb42 | |||
d9b63353b0 | |||
eabd0ec93f | |||
138d5dc64a | |||
3e68a87d63 | |||
69b6ef7a4a | |||
40e87c634e | |||
79d1c190db | |||
2bc88467eb | |||
baf8752e74 | |||
d5e4378aea | |||
6dbcdfea47 | |||
c5258cf082 | |||
5c89e22bb9 | |||
11ecff2ff0 | |||
4c3f09644a | |||
e187a8870a | |||
a64fee29dc | |||
9ef94c8292 | |||
915d6d044c | |||
a4780ab33b | |||
a947a45d81 | |||
9db73f74cf | |||
a1efd87c45 | |||
49be977588 | |||
c95be55091 | |||
63dedbda86 | |||
c532118d94 | |||
52d6f2e656 | |||
c9bc4eaf58 | |||
3249f8ff41 | |||
1b41b285ac | |||
f5a6f45b27 | |||
210557951b | |||
4c2d9ff3ff | |||
8198b99935 | |||
460f96967d | |||
7ca779a26d | |||
b5032b3c91 | |||
f0a3dff136 | |||
f659dcb9d8 | |||
a34fb0e939 | |||
21ce8a9b80 | |||
9ecbee8032 | |||
80519af67d | |||
26e30faff3 | |||
0992310b76 | |||
009c1101d2 | |||
ba95ee54ab | |||
4ce4299ca2 | |||
17620d18db | |||
9f1cf6458c | |||
67b4e63cff | |||
c05c688ee8 | |||
b2623dc27d | |||
5131b71437 | |||
7870423671 | |||
b72916fbc1 | |||
da073fce61 | |||
1fc90e57d2 | |||
eafcc314a9 | |||
6e9bd4de13 | |||
05a41b31bc | |||
eed17f963e | |||
c09c0c002d | |||
d56d335c0b | |||
23c844b2aa | |||
81691b9e37 | |||
2dc422bc14 | |||
a80fa5e33f | |||
954e995321 | |||
dad9ab6bb6 | |||
f0562b9c75 |
@ -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
|
||||
|
98
.github/workflows/build.yml
vendored
98
.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: ${{ vars.JOB_TIMEOUT }}
|
||||
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,47 +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'
|
||||
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: ${{ vars.JOB_TIMEOUT }}
|
||||
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: ${{ vars.JOB_TIMEOUT }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||
GIT_COMMITTER_NAME: "RyujinxBot"
|
||||
GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com"
|
||||
RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj"
|
||||
NUGET_SOURCES_DESTDIR: "nuget-sources"
|
||||
RYUJINX_VERSION: "${{ inputs.ryujinx_version }}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: Ryujinx
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: Ryujinx/global.json
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
working-directory: Ryujinx
|
||||
run: |
|
||||
echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: flathub/org.ryujinx.Ryujinx
|
||||
token: ${{ secrets.RYUJINX_BOT_PAT }}
|
||||
submodules: recursive
|
||||
path: flathub
|
||||
|
||||
- name: Install dependencies
|
||||
run: python -m pip install PyYAML lxml
|
||||
|
||||
- name: Restore Nuget packages
|
||||
run: dotnet restore Ryujinx/${{ env.RYUJINX_PROJECT_FILE }}
|
||||
|
||||
- name: Generate nuget_sources.json
|
||||
shell: python
|
||||
run: |
|
||||
from pathlib import Path
|
||||
import base64
|
||||
import binascii
|
||||
import json
|
||||
import os
|
||||
|
||||
sources = []
|
||||
|
||||
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||
name = path.parent.parent.name
|
||||
version = path.parent.name
|
||||
filename = '{}.{}.nupkg'.format(name, version)
|
||||
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||
|
||||
with path.open() as fp:
|
||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||
|
||||
sources.append({
|
||||
'type': 'file',
|
||||
'url': url,
|
||||
'sha512': sha512,
|
||||
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||
'dest-filename': filename,
|
||||
})
|
||||
|
||||
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||
json.dump(sources, fp, indent=4)
|
||||
|
||||
- name: Update flatpak metadata
|
||||
id: metadata
|
||||
env:
|
||||
RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }}
|
||||
shell: python
|
||||
run: |
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
import yaml
|
||||
from datetime import datetime
|
||||
from lxml import etree
|
||||
|
||||
|
||||
# Ensure we don't destroy multiline strings
|
||||
def str_presenter(dumper, data):
|
||||
if len(data.splitlines()) > 1:
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||||
|
||||
|
||||
yaml.representer.SafeRepresenter.add_representer(str, str_presenter)
|
||||
|
||||
yaml_file = "flathub/org.ryujinx.Ryujinx.yml"
|
||||
xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml"
|
||||
|
||||
with open(yaml_file, "r") as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
for source in data["modules"][0]["sources"]:
|
||||
if type(source) is str:
|
||||
continue
|
||||
if (
|
||||
source["type"] == "git"
|
||||
and source["url"] == "https://github.com/Ryujinx/Ryujinx.git"
|
||||
):
|
||||
source["commit"] = os.environ['RYUJINX_GIT_HASH']
|
||||
|
||||
is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION']
|
||||
|
||||
with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out:
|
||||
if is_same_version:
|
||||
gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}")
|
||||
else:
|
||||
gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}")
|
||||
|
||||
if not is_same_version:
|
||||
data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION']
|
||||
|
||||
with open(yaml_file, "w") as f:
|
||||
yaml.safe_dump(data, f, sort_keys=False)
|
||||
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(xml_file, parser)
|
||||
|
||||
root = tree.getroot()
|
||||
|
||||
releases = root.find("releases")
|
||||
|
||||
element = etree.Element("release")
|
||||
element.set("version", os.environ['RYUJINX_VERSION'])
|
||||
element.set("date", datetime.now().date().isoformat())
|
||||
releases.insert(0, element)
|
||||
|
||||
# Ensure 4 spaces
|
||||
etree.indent(root, space=" ")
|
||||
|
||||
with open(xml_file, "wb") as f:
|
||||
f.write(
|
||||
etree.tostring(
|
||||
tree,
|
||||
pretty_print=True,
|
||||
encoding="UTF-8",
|
||||
doctype='<?xml version="1.0" encoding="UTF-8"?>',
|
||||
)
|
||||
)
|
||||
|
||||
- name: Push flatpak update
|
||||
working-directory: flathub
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }}
|
||||
run: |
|
||||
git config user.name "${{ env.GIT_COMMITTER_NAME }}"
|
||||
git config user.email "${{ env.GIT_COMMITTER_EMAIL }}"
|
||||
git add .
|
||||
git commit -m "$COMMIT_MESSAGE"
|
||||
git push origin master
|
3
.github/workflows/nightly_pr_comment.yml
vendored
3
.github/workflows/nightly_pr_comment.yml
vendored
@ -7,6 +7,7 @@ jobs:
|
||||
pr_comment:
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: ${{ vars.JOB_TIMEOUT }}
|
||||
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});
|
||||
}
|
||||
}
|
192
.github/workflows/release.yml
vendored
192
.github/workflows/release.yml
vendored
@ -13,89 +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@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.git.createRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||
sha: context.sha
|
||||
})
|
||||
|
||||
release:
|
||||
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: ${{ vars.JOB_TIMEOUT }}
|
||||
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
|
||||
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
|
||||
|
||||
- name: Packing Linux builds
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
pushd publish_linux
|
||||
tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -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"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
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" --exclude "publish/Ryujinx.sh" -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"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
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.Ava" --exclude "publish/Ryujinx.sh" -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.Ava" "publish/Ryujinx.Ava"
|
||||
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
|
||||
gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
|
||||
rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
|
||||
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
|
||||
|
||||
@ -105,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: ${{ vars.JOB_TIMEOUT }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Setup LLVM 14
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 14
|
||||
|
||||
- name: Install rcodesign
|
||||
run: |
|
||||
mkdir -p $HOME/.bin
|
||||
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||
rm apple-codesign.tar.gz
|
||||
mv rcodesign $HOME/.bin/
|
||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Configure for release
|
||||
run: |
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
shell: bash
|
||||
|
||||
- name: Publish macOS
|
||||
run: |
|
||||
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||
|
||||
- name: Pushing new release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: ${{ steps.version_info.outputs.build_version }}
|
||||
artifacts: "publish_ava/*.tar.gz"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}
|
||||
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
flatpak_release:
|
||||
uses: ./.github/workflows/flatpak.yml
|
||||
needs: release
|
||||
with:
|
||||
ryujinx_version: "1.1.${{ github.run_number }}"
|
||||
secrets: inherit
|
@ -1,6 +0,0 @@
|
||||
namespace ARMeilleure.Decoders;
|
||||
|
||||
interface IOpCode32Exception
|
||||
{
|
||||
int Id { get; }
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class DelegateHelper
|
||||
{
|
||||
private const string DelegateTypesAssemblyName = "JitDelegateTypes";
|
||||
|
||||
private static readonly ModuleBuilder _modBuilder;
|
||||
|
||||
private static readonly Dictionary<string, Type> _delegateTypesCache;
|
||||
|
||||
static DelegateHelper()
|
||||
{
|
||||
AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run);
|
||||
|
||||
_modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
|
||||
|
||||
_delegateTypesCache = new Dictionary<string, Type>();
|
||||
}
|
||||
|
||||
public static Delegate GetDelegate(MethodInfo info)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
|
||||
Type returnType = info.ReturnType;
|
||||
|
||||
Type delegateType = GetDelegateType(parameters, returnType);
|
||||
|
||||
return Delegate.CreateDelegate(delegateType, info);
|
||||
}
|
||||
|
||||
private static Type GetDelegateType(Type[] parameters, Type returnType)
|
||||
{
|
||||
string key = GetFunctionSignatureKey(parameters, returnType);
|
||||
|
||||
if (!_delegateTypesCache.TryGetValue(key, out Type delegateType))
|
||||
{
|
||||
delegateType = MakeDelegateType(parameters, returnType, key);
|
||||
|
||||
_delegateTypesCache.TryAdd(key, delegateType);
|
||||
}
|
||||
|
||||
return delegateType;
|
||||
}
|
||||
|
||||
private static string GetFunctionSignatureKey(Type[] parameters, Type returnType)
|
||||
{
|
||||
string sig = GetTypeName(returnType);
|
||||
|
||||
foreach (Type type in parameters)
|
||||
{
|
||||
sig += '_' + GetTypeName(type);
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
private static string GetTypeName(Type type)
|
||||
{
|
||||
return type.FullName.Replace(".", string.Empty);
|
||||
}
|
||||
|
||||
private const MethodAttributes CtorAttributes =
|
||||
MethodAttributes.RTSpecialName |
|
||||
MethodAttributes.HideBySig |
|
||||
MethodAttributes.Public;
|
||||
|
||||
private const TypeAttributes DelegateTypeAttributes =
|
||||
TypeAttributes.Class |
|
||||
TypeAttributes.Public |
|
||||
TypeAttributes.Sealed |
|
||||
TypeAttributes.AnsiClass |
|
||||
TypeAttributes.AutoClass;
|
||||
|
||||
private const MethodImplAttributes ImplAttributes =
|
||||
MethodImplAttributes.Runtime |
|
||||
MethodImplAttributes.Managed;
|
||||
|
||||
private const MethodAttributes InvokeAttributes =
|
||||
MethodAttributes.Public |
|
||||
MethodAttributes.HideBySig |
|
||||
MethodAttributes.NewSlot |
|
||||
MethodAttributes.Virtual;
|
||||
|
||||
private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) };
|
||||
|
||||
private static Type MakeDelegateType(Type[] parameters, Type returnType, string name)
|
||||
{
|
||||
TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate));
|
||||
|
||||
builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes);
|
||||
|
||||
builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);
|
||||
|
||||
return builder.CreateTypeInfo();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
using ARMeilleure.Instructions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class Delegates
|
||||
{
|
||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr)
|
||||
{
|
||||
if (index >= 0 && index < _delegates.Count)
|
||||
{
|
||||
funcPtr = _delegates.Values[index].FuncPtr; // O(1).
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
funcPtr = default;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static IntPtr GetDelegateFuncPtrByIndex(int index)
|
||||
{
|
||||
if (index < 0 || index >= _delegates.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"({nameof(index)} = {index})");
|
||||
}
|
||||
|
||||
return _delegates.Values[index].FuncPtr; // O(1).
|
||||
}
|
||||
|
||||
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
string key = GetKey(info);
|
||||
|
||||
if (!_delegates.TryGetValue(key, out DelegateInfo dlgInfo)) // O(log(n)).
|
||||
{
|
||||
throw new KeyNotFoundException($"({nameof(key)} = {key})");
|
||||
}
|
||||
|
||||
return dlgInfo.FuncPtr;
|
||||
}
|
||||
|
||||
public static int GetDelegateIndex(MethodInfo info)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
string key = GetKey(info);
|
||||
|
||||
int index = _delegates.IndexOfKey(key); // O(log(n)).
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
throw new KeyNotFoundException($"({nameof(key)} = {key})");
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private static void SetDelegateInfo(MethodInfo info)
|
||||
{
|
||||
string key = GetKey(info);
|
||||
|
||||
Delegate dlg = DelegateHelper.GetDelegate(info);
|
||||
|
||||
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
|
||||
}
|
||||
|
||||
private static string GetKey(MethodInfo info)
|
||||
{
|
||||
return $"{info.DeclaringType.Name}.{info.Name}";
|
||||
}
|
||||
|
||||
private static readonly SortedList<string, DelegateInfo> _delegates;
|
||||
|
||||
static Delegates()
|
||||
{
|
||||
_delegates = new SortedList<string, DelegateInfo>();
|
||||
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) }));
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) }));
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) }));
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }));
|
||||
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Truncate), new Type[] { typeof(double) }));
|
||||
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) }));
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Ceiling), new Type[] { typeof(float) }));
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) }));
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }));
|
||||
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Truncate), new Type[] { typeof(float) }));
|
||||
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)));
|
||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)));
|
||||
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
|
||||
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
|
||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only.
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
|
||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)));
|
||||
}
|
||||
}
|
||||
}
|
@ -3,18 +3,17 @@
|
||||
<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" />
|
||||
@ -23,6 +22,7 @@
|
||||
<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" />
|
||||
@ -34,7 +34,7 @@
|
||||
<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.2" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||
@ -44,12 +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.27.0" />
|
||||
<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="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" />
|
||||
<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>
|
||||
</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
|
||||
|
@ -1,177 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Controls.GameGridView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
<MenuFlyout x:Key="GameContextMenu">
|
||||
<MenuItem
|
||||
Command="{Binding ToggleFavorite}"
|
||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{Binding OpenUserSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledUserSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenDeviceSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledDeviceSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenBcatSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledBcatSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{Binding OpenTitleUpdateManager}"
|
||||
Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenDownloadableContentManager}"
|
||||
Header="{locale:Locale GameListContextMenuManageDlc}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenCheatManager}"
|
||||
Header="{locale:Locale GameListContextMenuManageCheat}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenModsDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenSdModsDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
|
||||
<Separator />
|
||||
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
|
||||
<MenuItem
|
||||
Command="{Binding PurgePtcCache}"
|
||||
Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding PurgeShaderCache}"
|
||||
Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenPtcDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenShaderCacheDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
|
||||
<MenuItem
|
||||
Command="{Binding ExtractExeFs}"
|
||||
Header="{locale:Locale GameListContextMenuExtractDataExeFS}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding ExtractRomFs}"
|
||||
Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding ExtractLogo}"
|
||||
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
|
||||
</MenuItem>
|
||||
</MenuFlyout>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListBox
|
||||
Grid.Row="0"
|
||||
Padding="8"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ContextFlyout="{StaticResource GameContextMenu}"
|
||||
DoubleTapped="GameList_DoubleTapped"
|
||||
Items="{Binding AppsObservableList}"
|
||||
SelectionChanged="GameList_SelectionChanged">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<flex:FlexPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
AlignContent="FlexStart"
|
||||
JustifyContent="Center" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem">
|
||||
<Setter Property="Margin" Value="5" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
|
||||
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" />
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Border
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
|
||||
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
|
||||
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
|
||||
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="4">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
|
||||
<Panel
|
||||
Grid.Row="1"
|
||||
Height="50"
|
||||
Margin="0 10 0 0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding TitleName}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</Panel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ui:SymbolIcon
|
||||
Margin="5,5,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="16"
|
||||
Foreground="{DynamicResource SystemAccentColor}"
|
||||
IsVisible="{Binding Favorite}"
|
||||
Symbol="StarFilled" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</UserControl>
|
@ -1,32 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Controls.InputDialog"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Grid
|
||||
Margin="5,10,5,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding Message}" />
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Width="300"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center"
|
||||
MaxLength="{Binding MaxLength}"
|
||||
Text="{Binding Input, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Margin="5,5,5,10"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding SubMessage}" />
|
||||
</Grid>
|
||||
</UserControl>
|
@ -1,57 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class InputDialog : UserControl
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public string Input { get; set; }
|
||||
public string SubMessage { get; set; }
|
||||
|
||||
public uint MaxLength { get; }
|
||||
|
||||
public InputDialog(string message, string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
||||
{
|
||||
Message = message;
|
||||
Input = input;
|
||||
SubMessage = subMessage;
|
||||
MaxLength = maxLength;
|
||||
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
public InputDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, string message,
|
||||
string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
||||
{
|
||||
UserResult result = UserResult.Cancel;
|
||||
|
||||
InputDialog content = new InputDialog(message, input, subMessage, maxLength);
|
||||
ContentDialog contentDialog = new ContentDialog
|
||||
{
|
||||
Title = title,
|
||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel],
|
||||
Content = content,
|
||||
PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.Ok;
|
||||
input = content.Input;
|
||||
})
|
||||
};
|
||||
await contentDialog.ShowAsync();
|
||||
|
||||
return (result, input);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
public class Amiibo
|
||||
{
|
||||
public struct AmiiboJson
|
||||
{
|
||||
[JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; }
|
||||
[JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; }
|
||||
}
|
||||
|
||||
public struct AmiiboApi
|
||||
{
|
||||
[JsonPropertyName("name")] public string Name { get; set; }
|
||||
[JsonPropertyName("head")] public string Head { get; set; }
|
||||
[JsonPropertyName("tail")] public string Tail { get; set; }
|
||||
[JsonPropertyName("image")] public string Image { get; set; }
|
||||
[JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; }
|
||||
[JsonPropertyName("character")] public string Character { get; set; }
|
||||
[JsonPropertyName("gameSeries")] public string GameSeries { get; set; }
|
||||
[JsonPropertyName("type")] public string Type { get; set; }
|
||||
|
||||
[JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; }
|
||||
|
||||
[JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public string GetId()
|
||||
{
|
||||
return Head + Tail;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is AmiiboApi amiibo)
|
||||
{
|
||||
return amiibo.Head + amiibo.Tail == Head + Tail;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public class AmiiboApiGamesSwitch
|
||||
{
|
||||
[JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; }
|
||||
|
||||
[JsonPropertyName("gameID")] public List<string> GameId { get; set; }
|
||||
|
||||
[JsonPropertyName("gameName")] public string GameName { get; set; }
|
||||
}
|
||||
|
||||
public class AmiiboApiUsage
|
||||
{
|
||||
[JsonPropertyName("Usage")] public string Usage { get; set; }
|
||||
|
||||
[JsonPropertyName("write")] public bool Write { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Path = System.IO.Path;
|
||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels;
|
||||
|
||||
public class TitleUpdateViewModel : BaseModel
|
||||
{
|
||||
public TitleUpdateMetadata _titleUpdateWindowData;
|
||||
public readonly string _titleUpdateJsonPath;
|
||||
private VirtualFileSystem _virtualFileSystem { get; }
|
||||
private ulong _titleId { get; }
|
||||
private string _titleName { get; }
|
||||
|
||||
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
||||
private AvaloniaList<object> _views = new();
|
||||
private object _selectedUpdate;
|
||||
|
||||
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
||||
{
|
||||
get => _titleUpdates;
|
||||
set
|
||||
{
|
||||
_titleUpdates = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public AvaloniaList<object> Views
|
||||
{
|
||||
get => _views;
|
||||
set
|
||||
{
|
||||
_views = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public object SelectedUpdate
|
||||
{
|
||||
get => _selectedUpdate;
|
||||
set
|
||||
{
|
||||
_selectedUpdate = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
|
||||
_titleId = titleId;
|
||||
_titleName = titleName;
|
||||
|
||||
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
||||
|
||||
try
|
||||
{
|
||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
||||
|
||||
_titleUpdateWindowData = new TitleUpdateMetadata
|
||||
{
|
||||
Selected = "",
|
||||
Paths = new List<string>()
|
||||
};
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
LoadUpdates();
|
||||
}
|
||||
|
||||
private void LoadUpdates()
|
||||
{
|
||||
foreach (string path in _titleUpdateWindowData.Paths)
|
||||
{
|
||||
AddUpdate(path);
|
||||
}
|
||||
|
||||
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
||||
|
||||
SelectedUpdate = selected;
|
||||
|
||||
// NOTE: Save the list again to remove leftovers.
|
||||
Save();
|
||||
|
||||
SortUpdates();
|
||||
}
|
||||
|
||||
public void SortUpdates()
|
||||
{
|
||||
var list = TitleUpdates.ToList();
|
||||
|
||||
list.Sort((first, second) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
||||
});
|
||||
|
||||
Views.Clear();
|
||||
Views.Add(new BaseModel());
|
||||
Views.AddRange(list);
|
||||
|
||||
if (SelectedUpdate == null)
|
||||
{
|
||||
SelectedUpdate = Views[0];
|
||||
}
|
||||
else if (!TitleUpdates.Contains(SelectedUpdate))
|
||||
{
|
||||
if (Views.Count > 1)
|
||||
{
|
||||
SelectedUpdate = Views[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedUpdate = Views[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddUpdate(string path)
|
||||
{
|
||||
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
||||
{
|
||||
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
|
||||
try
|
||||
{
|
||||
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
||||
|
||||
if (controlNca != null && patchNca != null)
|
||||
{
|
||||
ApplicationControlProperty controlData = new();
|
||||
|
||||
using UniqueRef<IFile> nacpFile = new();
|
||||
|
||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
||||
|
||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
||||
}
|
||||
else
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveUpdate(TitleUpdateModel update)
|
||||
{
|
||||
TitleUpdates.Remove(update);
|
||||
|
||||
SortUpdates();
|
||||
}
|
||||
|
||||
public async void Add()
|
||||
{
|
||||
OpenFileDialog dialog = new()
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
||||
AllowMultiple = true
|
||||
};
|
||||
|
||||
dialog.Filters.Add(new FileDialogFilter
|
||||
{
|
||||
Name = "NSP",
|
||||
Extensions = { "nsp" }
|
||||
});
|
||||
|
||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
||||
|
||||
if (files != null)
|
||||
{
|
||||
foreach (string file in files)
|
||||
{
|
||||
AddUpdate(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SortUpdates();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_titleUpdateWindowData.Paths.Clear();
|
||||
_titleUpdateWindowData.Selected = "";
|
||||
|
||||
foreach (TitleUpdateModel update in TitleUpdates)
|
||||
{
|
||||
_titleUpdateWindowData.Paths.Add(update.Path);
|
||||
|
||||
if (update == SelectedUpdate)
|
||||
{
|
||||
_titleUpdateWindowData.Selected = update.Path;
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<ScrollViewer
|
||||
Name="InputPage"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel Margin="4" Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
|
||||
IsChecked="{Binding EnableDockedMode}">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
|
||||
</CheckBox>
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
|
||||
IsChecked="{Binding EnableKeyboard}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
|
||||
</CheckBox>
|
||||
<CheckBox Margin="5,0"
|
||||
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
|
||||
IsChecked="{Binding EnableMouse}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0" MinHeight="600" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
@ -1,172 +0,0 @@
|
||||
<window:StyleableWindow
|
||||
x:Class="Ryujinx.Ava.UI.Windows.DownloadableContentManagerWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
Width="800"
|
||||
Height="500"
|
||||
MinWidth="800"
|
||||
MinHeight="500"
|
||||
MaxWidth="800"
|
||||
MaxHeight="500"
|
||||
SizeToContent="Height"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Grid Name="DownloadableContentGrid" Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Name="Heading"
|
||||
Grid.Row="1"
|
||||
MaxWidth="500"
|
||||
Margin="20,15,20,20"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
LineHeight="18"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<DockPanel
|
||||
Grid.Row="2"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left">
|
||||
<Button
|
||||
Name="EnableAllButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding EnableAll}">
|
||||
<TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="DisableAllButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding DisableAll}">
|
||||
<TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" />
|
||||
</Button>
|
||||
</DockPanel>
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1">
|
||||
<ScrollViewer
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<DataGrid
|
||||
Name="DlcDataGrid"
|
||||
MinHeight="200"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="True"
|
||||
CanUserSortColumns="True"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
Items="{Binding _downloadableContents}"
|
||||
SelectionMode="Extended"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<DataGrid.Styles>
|
||||
<Styles>
|
||||
<Style Selector="DataGridCell:nth-child(3), DataGridCell:nth-child(4)">
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
</Style>
|
||||
</Styles>
|
||||
<Styles>
|
||||
<Style Selector="DataGridCell:nth-child(1)">
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Right" />
|
||||
</Style>
|
||||
</Styles>
|
||||
</DataGrid.Styles>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Width="90">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox
|
||||
Width="50"
|
||||
MinWidth="40"
|
||||
HorizontalAlignment="Center"
|
||||
IsChecked="{Binding Enabled}" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
<DataGridTemplateColumn.Header>
|
||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" />
|
||||
</DataGridTemplateColumn.Header>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Width="140" Binding="{Binding TitleId}">
|
||||
<DataGridTextColumn.Header>
|
||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingTitleIdLabel}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Width="280" Binding="{Binding FullPath}">
|
||||
<DataGridTextColumn.Header>
|
||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingFullPathLabel}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Binding="{Binding ContainerPath}">
|
||||
<DataGridTextColumn.Header>
|
||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingContainerPathLabel}" />
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
<DockPanel
|
||||
Grid.Row="4"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch">
|
||||
<DockPanel Margin="0" HorizontalAlignment="Left">
|
||||
<Button
|
||||
Name="AddButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding Add}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="RemoveButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding RemoveSelected}">
|
||||
<TextBlock Text="{locale:Locale SettingsTabGeneralRemove}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="RemoveAllButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding RemoveAll}">
|
||||
<TextBlock Text="{locale:Locale DlcManagerRemoveAllButton}" />
|
||||
</Button>
|
||||
</DockPanel>
|
||||
<DockPanel Margin="0" HorizontalAlignment="Right">
|
||||
<Button
|
||||
Name="SaveButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding SaveAndClose}">
|
||||
<TextBlock Text="{locale:Locale SettingsButtonSave}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="CancelButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding Close}">
|
||||
<TextBlock Text="{locale:Locale InputDialogCancel}" />
|
||||
</Button>
|
||||
</DockPanel>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
@ -1,12 +0,0 @@
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
public enum AntiAliasing
|
||||
{
|
||||
None,
|
||||
Fxaa,
|
||||
SmaaLow,
|
||||
SmaaMedium,
|
||||
SmaaHigh,
|
||||
SmaaUltra
|
||||
}
|
||||
}
|
@ -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,9 +0,0 @@
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
public enum ScalingFilter
|
||||
{
|
||||
Bilinear,
|
||||
Nearest,
|
||||
Fsr
|
||||
}
|
||||
}
|
@ -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,155 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Common.SystemInterop
|
||||
{
|
||||
[SupportedOSPlatform("linux")]
|
||||
[SupportedOSPlatform("macos")]
|
||||
public partial class UnixStream : Stream, IDisposable
|
||||
{
|
||||
private const int InvalidFd = -1;
|
||||
|
||||
private int _fd;
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial long read(int fd, IntPtr buf, ulong count);
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial long write(int fd, IntPtr buf, ulong count);
|
||||
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int close(int fd);
|
||||
|
||||
public UnixStream(int fd)
|
||||
{
|
||||
if (InvalidFd == fd)
|
||||
{
|
||||
throw new ArgumentException("Invalid file descriptor");
|
||||
}
|
||||
|
||||
_fd = fd;
|
||||
|
||||
CanRead = read(fd, IntPtr.Zero, 0) != -1;
|
||||
CanWrite = write(fd, IntPtr.Zero, 0) != -1;
|
||||
}
|
||||
|
||||
~UnixStream()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public override bool CanRead { get; }
|
||||
public override bool CanWrite { get; }
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override long Length => throw new NotSupportedException();
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override unsafe int Read([In, Out] byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
long r = 0;
|
||||
fixed (byte* buf = &buffer[offset])
|
||||
{
|
||||
do
|
||||
{
|
||||
r = read(_fd, (IntPtr)buf, (ulong)count);
|
||||
} while (ShouldRetry(r));
|
||||
}
|
||||
|
||||
return (int)r;
|
||||
}
|
||||
|
||||
public override unsafe void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (byte* buf = &buffer[offset])
|
||||
{
|
||||
long r = 0;
|
||||
do {
|
||||
r = write(_fd, (IntPtr)buf, (ulong)count);
|
||||
} while (ShouldRetry(r));
|
||||
}
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (_fd == InvalidFd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Flush();
|
||||
|
||||
int r;
|
||||
do {
|
||||
r = close(_fd);
|
||||
} while (ShouldRetry(r));
|
||||
|
||||
_fd = InvalidFd;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private bool ShouldRetry(long r)
|
||||
{
|
||||
if (r == -1)
|
||||
{
|
||||
const int eintr = 4;
|
||||
|
||||
int errno = Marshal.GetLastPInvokeError();
|
||||
|
||||
if (errno == eintr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new SystemException($"Operation failed with error 0x{errno:X}");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public class JsonHelper
|
||||
{
|
||||
public static JsonNamingPolicy SnakeCase { get; }
|
||||
|
||||
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
||||
{
|
||||
public override string ConvertName(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
char c = name[i];
|
||||
|
||||
if (char.IsUpper(c))
|
||||
{
|
||||
if (i == 0 || char.IsUpper(name[i - 1]))
|
||||
{
|
||||
builder.Append(char.ToLowerInvariant(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append("_");
|
||||
builder.Append(char.ToLowerInvariant(c));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
static JsonHelper()
|
||||
{
|
||||
SnakeCase = new SnakeCaseNamingPolicy();
|
||||
}
|
||||
|
||||
public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false)
|
||||
{
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
DictionaryKeyPolicy = SnakeCase,
|
||||
PropertyNamingPolicy = SnakeCase,
|
||||
WriteIndented = prettyPrint,
|
||||
AllowTrailingCommas = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip
|
||||
};
|
||||
|
||||
options.Converters.Add(new JsonStringEnumConverter());
|
||||
options.Converters.Add(new JsonInputConfigConverter());
|
||||
options.Converters.Add(new JsonMotionConfigControllerConverter());
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public static T Deserialize<T>(Stream stream)
|
||||
{
|
||||
using (BinaryReader reader = new BinaryReader(stream))
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions());
|
||||
}
|
||||
}
|
||||
|
||||
public static T DeserializeFromFile<T>(string path)
|
||||
{
|
||||
return Deserialize<T>(File.ReadAllText(path));
|
||||
}
|
||||
|
||||
public static T Deserialize<T>(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions());
|
||||
}
|
||||
|
||||
public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false)
|
||||
{
|
||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write(SerializeToUtf8Bytes(obj, prettyPrint));
|
||||
}
|
||||
}
|
||||
|
||||
public static string Serialize<TValue>(TValue obj, bool prettyPrint = false)
|
||||
{
|
||||
return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint));
|
||||
}
|
||||
|
||||
public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false)
|
||||
{
|
||||
return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public static class StreamUtils
|
||||
{
|
||||
public static byte[] StreamToBytes(Stream input)
|
||||
{
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
input.CopyTo(stream);
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Cpu.Tracking
|
||||
{
|
||||
public class CpuMultiRegionHandle : IMultiRegionHandle
|
||||
{
|
||||
private readonly MultiRegionHandle _impl;
|
||||
|
||||
public bool Dirty => _impl.Dirty;
|
||||
|
||||
internal CpuMultiRegionHandle(MultiRegionHandle impl)
|
||||
{
|
||||
_impl = impl;
|
||||
}
|
||||
|
||||
public void Dispose() => _impl.Dispose();
|
||||
public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size);
|
||||
public IEnumerable<IRegionHandle> GetHandles() => _impl.GetHandles();
|
||||
public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction);
|
||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction);
|
||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber);
|
||||
public void RegisterAction(ulong address, ulong size, RegionSignal action) => _impl.RegisterAction(address, size, action);
|
||||
public void RegisterPreciseAction(ulong address, ulong size, PreciseRegionSignal action) => _impl.RegisterPreciseAction(address, size, action);
|
||||
public void SignalWrite() => _impl.SignalWrite();
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Tracking
|
||||
{
|
||||
public class CpuRegionHandle : IRegionHandle
|
||||
{
|
||||
private readonly RegionHandle _impl;
|
||||
|
||||
public bool Dirty => _impl.Dirty;
|
||||
public bool Unmapped => _impl.Unmapped;
|
||||
public ulong Address => _impl.Address;
|
||||
public ulong Size => _impl.Size;
|
||||
public ulong EndAddress => _impl.EndAddress;
|
||||
|
||||
internal CpuRegionHandle(RegionHandle impl)
|
||||
{
|
||||
_impl = impl;
|
||||
}
|
||||
|
||||
public void Dispose() => _impl.Dispose();
|
||||
public bool DirtyOrVolatile() => _impl.DirtyOrVolatile();
|
||||
public void ForceDirty() => _impl.ForceDirty();
|
||||
public IRegionHandle GetHandle() => _impl;
|
||||
public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action);
|
||||
public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action);
|
||||
public void RegisterDirtyEvent(Action action) => _impl.RegisterDirtyEvent(action);
|
||||
public void Reprotect(bool asDirty = false) => _impl.Reprotect(asDirty);
|
||||
|
||||
public bool OverlapsWith(ulong address, ulong size) => _impl.OverlapsWith(address, size);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Tracking
|
||||
{
|
||||
public class CpuSmartMultiRegionHandle : IMultiRegionHandle
|
||||
{
|
||||
private readonly SmartMultiRegionHandle _impl;
|
||||
|
||||
public bool Dirty => _impl.Dirty;
|
||||
|
||||
internal CpuSmartMultiRegionHandle(SmartMultiRegionHandle impl)
|
||||
{
|
||||
_impl = impl;
|
||||
}
|
||||
|
||||
public void Dispose() => _impl.Dispose();
|
||||
public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size);
|
||||
public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action);
|
||||
public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action);
|
||||
public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction);
|
||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction);
|
||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber);
|
||||
public void SignalWrite() => _impl.SignalWrite();
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Model
|
||||
{
|
||||
unsafe struct PinnedSpan<T> where T : unmanaged
|
||||
{
|
||||
private void* _ptr;
|
||||
private int _size;
|
||||
|
||||
public PinnedSpan(ReadOnlySpan<T> span)
|
||||
{
|
||||
_ptr = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
|
||||
_size = span.Length;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<T> Get()
|
||||
{
|
||||
return new ReadOnlySpan<T>(_ptr, _size * Unsafe.SizeOf<T>());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public readonly struct ShaderBindings
|
||||
{
|
||||
public IReadOnlyCollection<int> UniformBufferBindings { get; }
|
||||
public IReadOnlyCollection<int> StorageBufferBindings { get; }
|
||||
public IReadOnlyCollection<int> TextureBindings { get; }
|
||||
public IReadOnlyCollection<int> ImageBindings { get; }
|
||||
|
||||
public ShaderBindings(
|
||||
IReadOnlyCollection<int> uniformBufferBindings,
|
||||
IReadOnlyCollection<int> storageBufferBindings,
|
||||
IReadOnlyCollection<int> textureBindings,
|
||||
IReadOnlyCollection<int> imageBindings)
|
||||
{
|
||||
UniformBufferBindings = uniformBufferBindings;
|
||||
StorageBufferBindings = storageBufferBindings;
|
||||
TextureBindings = textureBindings;
|
||||
ImageBindings = imageBindings;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
public class UnmapEventArgs
|
||||
{
|
||||
public ulong Address { get; }
|
||||
public ulong Size { get; }
|
||||
|
||||
public UnmapEventArgs(ulong address, ulong size)
|
||||
{
|
||||
Address = address;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
||||
{
|
||||
float scale = s_render_scale[samplerIndex];
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return inputVec;
|
||||
}
|
||||
return ivec2(vec2(inputVec) * scale);
|
||||
}
|
||||
|
||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
||||
{
|
||||
float scale = s_render_scale[samplerIndex];
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
return int(float(size) / scale);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
||||
{
|
||||
float scale = s_render_scale[1 + samplerIndex];
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return inputVec;
|
||||
}
|
||||
if (scale < 0.0) // If less than 0, try interpolate between texels by using the screen position.
|
||||
{
|
||||
return ivec2(vec2(inputVec) * (-scale) + mod(gl_FragCoord.xy, 0.0 - scale));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ivec2(vec2(inputVec) * scale);
|
||||
}
|
||||
}
|
||||
|
||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
||||
{
|
||||
float scale = abs(s_render_scale[1 + samplerIndex]);
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
return int(float(size) / scale);
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
||||
{
|
||||
float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]);
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return inputVec;
|
||||
}
|
||||
|
||||
return ivec2(vec2(inputVec) * scale);
|
||||
}
|
||||
|
||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
||||
{
|
||||
float scale = abs(s_render_scale[1 + samplerIndex + s_frag_scale_count]);
|
||||
if (scale == 1.0)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
return int(float(size) / scale);
|
||||
}
|
@ -1,505 +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();
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.Fragment && isAsgDest &&
|
||||
operand.Value >= AttributeConsts.FragmentOutputColorBase &&
|
||||
operand.Value < AttributeConsts.FragmentOutputColorEnd)
|
||||
{
|
||||
int location = (operand.Value - AttributeConsts.FragmentOutputColorBase) / 16;
|
||||
|
||||
AttributeType type = context.Config.GpuAccessor.QueryFragmentOutputType(location);
|
||||
|
||||
return type.ToAggregateType();
|
||||
}
|
||||
}
|
||||
|
||||
return OperandInfo.GetVarType(operand);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,784 +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);
|
||||
|
||||
if (iaIndexing && !perPatch)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (oaIndexing && !perPatch)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DeclareOutputAttribute(context, attr, perPatch);
|
||||
}
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
DeclareOutputAttribute(context, AttributeConsts.PositionX, perPatch: false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareOutputAttribute(CodeGenContext context, int attr, bool perPatch)
|
||||
{
|
||||
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: true);
|
||||
}
|
||||
|
||||
public static void DeclareInvocationId(CodeGenContext context)
|
||||
{
|
||||
DeclareInputOrOutput(context, AttributeConsts.LaneId, perPatch: false, isOutAttr: false);
|
||||
}
|
||||
|
||||
private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool perPatch, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
||||
{
|
||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
||||
if (isUserAttr && context.Config.TransformFeedbackEnabled && !perPatch &&
|
||||
((isOutAttr && context.Config.LastInVertexPipeline) ||
|
||||
(!isOutAttr && context.Config.Stage == ShaderStage.Fragment)))
|
||||
{
|
||||
DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq);
|
||||
return;
|
||||
}
|
||||
|
||||
var dict = perPatch
|
||||
? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
|
||||
: (isOutAttr ? context.Outputs : context.Inputs);
|
||||
|
||||
var attrInfo = perPatch
|
||||
? AttributeInfo.FromPatch(context.Config, attr, isOutAttr)
|
||||
: AttributeInfo.From(context.Config, attr, isOutAttr);
|
||||
|
||||
if (dict.ContainsKey(attrInfo.BaseValue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
||||
var attrType = context.GetType(attrInfo.Type, attrInfo.Length);
|
||||
bool builtInPassthrough = false;
|
||||
|
||||
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && !perPatch && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
|
||||
{
|
||||
int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
|
||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
||||
|
||||
if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||
{
|
||||
builtInPassthrough = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch)
|
||||
{
|
||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
||||
}
|
||||
|
||||
var spvType = context.TypePointer(storageClass, attrType);
|
||||
var spvVar = context.Variable(spvType, storageClass);
|
||||
|
||||
if (builtInPassthrough)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||
}
|
||||
|
||||
if (attrInfo.IsBuiltin)
|
||||
{
|
||||
if (perPatch)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Patch);
|
||||
}
|
||||
|
||||
if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Invariant);
|
||||
}
|
||||
|
||||
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
|
||||
|
||||
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
|
||||
{
|
||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue);
|
||||
if (tfOutput.Valid)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
|
||||
context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride);
|
||||
context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (perPatch)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Patch);
|
||||
|
||||
int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16);
|
||||
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
}
|
||||
else if (isUserAttr)
|
||||
{
|
||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
||||
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
|
||||
if (!isOutAttr &&
|
||||
!perPatch &&
|
||||
(context.Config.PassthroughAttributes & (1 << location)) != 0 &&
|
||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||
}
|
||||
}
|
||||
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
||||
{
|
||||
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
}
|
||||
|
||||
if (!isOutAttr)
|
||||
{
|
||||
switch (iq)
|
||||
{
|
||||
case PixelImap.Constant:
|
||||
context.Decorate(spvVar, Decoration.Flat);
|
||||
break;
|
||||
case PixelImap.ScreenLinear:
|
||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
context.AddGlobalVariable(spvVar);
|
||||
dict.Add(attrInfo.BaseValue, spvVar);
|
||||
}
|
||||
|
||||
private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
||||
{
|
||||
var dict = isOutAttr ? context.Outputs : context.Inputs;
|
||||
var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
|
||||
|
||||
bool hasComponent = true;
|
||||
int component = (attr >> 2) & 3;
|
||||
int components = 1;
|
||||
var type = attrInfo.Type & AggregateType.ElementTypeMask;
|
||||
|
||||
if (context.Config.LastInPipeline && isOutAttr)
|
||||
{
|
||||
components = context.Info.GetTransformFeedbackOutputComponents(attr);
|
||||
|
||||
if (components > 1)
|
||||
{
|
||||
attr &= ~0xf;
|
||||
type = components switch
|
||||
{
|
||||
2 => AggregateType.Vector2 | AggregateType.FP32,
|
||||
3 => AggregateType.Vector3 | AggregateType.FP32,
|
||||
4 => AggregateType.Vector4 | AggregateType.FP32,
|
||||
_ => AggregateType.FP32
|
||||
};
|
||||
|
||||
hasComponent = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dict.ContainsKey(attr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
||||
var attrType = context.GetType(type, components);
|
||||
|
||||
if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
|
||||
{
|
||||
int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
|
||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
||||
}
|
||||
|
||||
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr)
|
||||
{
|
||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
||||
}
|
||||
|
||||
var spvType = context.TypePointer(storageClass, attrType);
|
||||
var spvVar = context.Variable(spvType, storageClass);
|
||||
|
||||
Debug.Assert(attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd);
|
||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
||||
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
|
||||
if (hasComponent)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
|
||||
}
|
||||
|
||||
if (isOutAttr)
|
||||
{
|
||||
var tfOutput = context.Info.GetTransformFeedbackOutput(attr);
|
||||
if (tfOutput.Valid)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
|
||||
context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride);
|
||||
context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((context.Config.PassthroughAttributes & (1 << location)) != 0 &&
|
||||
context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.PassthroughNV);
|
||||
}
|
||||
|
||||
switch (iq)
|
||||
{
|
||||
case PixelImap.Constant:
|
||||
context.Decorate(spvVar, Decoration.Flat);
|
||||
break;
|
||||
case PixelImap.ScreenLinear:
|
||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
context.AddGlobalVariable(spvVar);
|
||||
dict.Add(attr, spvVar);
|
||||
}
|
||||
|
||||
private static BuiltIn GetBuiltIn(CodeGenContext context, int attr)
|
||||
{
|
||||
return attr switch
|
||||
{
|
||||
AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter,
|
||||
AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner,
|
||||
AttributeConsts.Layer => BuiltIn.Layer,
|
||||
AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex,
|
||||
AttributeConsts.PointSize => BuiltIn.PointSize,
|
||||
AttributeConsts.PositionX => context.Config.Stage == ShaderStage.Fragment ? BuiltIn.FragCoord : BuiltIn.Position,
|
||||
AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance,
|
||||
AttributeConsts.PointCoordX => BuiltIn.PointCoord,
|
||||
AttributeConsts.TessCoordX => BuiltIn.TessCoord,
|
||||
AttributeConsts.InstanceId => BuiltIn.InstanceId,
|
||||
AttributeConsts.VertexId => BuiltIn.VertexId,
|
||||
AttributeConsts.BaseInstance => BuiltIn.BaseInstance,
|
||||
AttributeConsts.BaseVertex => BuiltIn.BaseVertex,
|
||||
AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex,
|
||||
AttributeConsts.VertexIndex => BuiltIn.VertexIndex,
|
||||
AttributeConsts.DrawIndex => BuiltIn.DrawIndex,
|
||||
AttributeConsts.FrontFacing => BuiltIn.FrontFacing,
|
||||
AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth,
|
||||
AttributeConsts.ThreadKill => BuiltIn.HelperInvocation,
|
||||
AttributeConsts.ThreadIdX => BuiltIn.LocalInvocationId,
|
||||
AttributeConsts.CtaIdX => BuiltIn.WorkgroupId,
|
||||
AttributeConsts.LaneId => BuiltIn.SubgroupLocalInvocationId,
|
||||
AttributeConsts.InvocationId => BuiltIn.InvocationId,
|
||||
AttributeConsts.PrimitiveId => BuiltIn.PrimitiveId,
|
||||
AttributeConsts.PatchVerticesIn => BuiltIn.PatchVertices,
|
||||
AttributeConsts.EqMask => BuiltIn.SubgroupEqMask,
|
||||
AttributeConsts.GeMask => BuiltIn.SubgroupGeMask,
|
||||
AttributeConsts.GtMask => BuiltIn.SubgroupGtMask,
|
||||
AttributeConsts.LeMask => BuiltIn.SubgroupLeMask,
|
||||
AttributeConsts.LtMask => BuiltIn.SubgroupLtMask,
|
||||
AttributeConsts.SupportBlockViewInverseX => BuiltIn.Position,
|
||||
AttributeConsts.SupportBlockViewInverseY => BuiltIn.Position,
|
||||
_ => throw new ArgumentException($"Invalid attribute number 0x{attr:X}.")
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetStagePrefix(ShaderStage stage)
|
||||
{
|
||||
return StagePrefixes[(int)stage];
|
||||
}
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
using SpvInstruction = Spv.Generator.Instruction;
|
||||
|
||||
static class ScalingHelpers
|
||||
{
|
||||
public static SpvInstruction ApplyScaling(
|
||||
CodeGenContext context,
|
||||
AstTextureOperation texOp,
|
||||
SpvInstruction vector,
|
||||
bool intCoords,
|
||||
bool isBindless,
|
||||
bool isIndexed,
|
||||
bool isArray,
|
||||
int pCount)
|
||||
{
|
||||
if (intCoords)
|
||||
{
|
||||
if (context.Config.Stage.SupportsRenderScale() &&
|
||||
!isBindless &&
|
||||
!isIndexed)
|
||||
{
|
||||
int index = texOp.Inst == Instruction.ImageLoad
|
||||
? context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp)
|
||||
: context.Config.FindTextureDescriptorIndex(texOp);
|
||||
|
||||
if (pCount == 3 && isArray)
|
||||
{
|
||||
return ApplyScaling2DArray(context, vector, index);
|
||||
}
|
||||
else if (pCount == 2 && !isArray)
|
||||
{
|
||||
return ApplyScaling2D(context, vector, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
private static SpvInstruction ApplyScaling2DArray(CodeGenContext context, SpvInstruction vector, int index)
|
||||
{
|
||||
// The array index is not scaled, just x and y.
|
||||
var vectorXY = context.VectorShuffle(context.TypeVector(context.TypeS32(), 2), vector, vector, 0, 1);
|
||||
var vectorZ = context.CompositeExtract(context.TypeS32(), vector, 2);
|
||||
var vectorXYScaled = ApplyScaling2D(context, vectorXY, index);
|
||||
var vectorScaled = context.CompositeConstruct(context.TypeVector(context.TypeS32(), 3), vectorXYScaled, vectorZ);
|
||||
|
||||
return vectorScaled;
|
||||
}
|
||||
|
||||
private static SpvInstruction ApplyScaling2D(CodeGenContext context, SpvInstruction vector, int index)
|
||||
{
|
||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
||||
var fieldIndex = context.Constant(context.TypeU32(), 4);
|
||||
var scaleIndex = context.Constant(context.TypeU32(), index);
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
|
||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
||||
|
||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
||||
}
|
||||
|
||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
||||
|
||||
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
|
||||
var scale = context.Load(context.TypeFP32(), scaleElemPointer);
|
||||
|
||||
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
|
||||
var localVector = context.CoordTemp;
|
||||
|
||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
||||
|
||||
var mergeLabel = context.Label();
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
var scaledInterpolatedLabel = context.Label();
|
||||
var scaledNoInterpolationLabel = context.Label();
|
||||
|
||||
var needsInterpolation = context.FOrdLessThan(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 0f));
|
||||
|
||||
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
|
||||
context.BranchConditional(needsInterpolation, scaledInterpolatedLabel, scaledNoInterpolationLabel);
|
||||
|
||||
// scale < 0.0
|
||||
context.AddLabel(scaledInterpolatedLabel);
|
||||
|
||||
ApplyScalingInterpolated(context, localVector, vector, scale);
|
||||
context.Branch(mergeLabel);
|
||||
|
||||
// scale >= 0.0
|
||||
context.AddLabel(scaledNoInterpolationLabel);
|
||||
|
||||
ApplyScalingNoInterpolation(context, localVector, vector, scale);
|
||||
context.Branch(mergeLabel);
|
||||
|
||||
context.AddLabel(mergeLabel);
|
||||
|
||||
var passthroughLabel = context.Label();
|
||||
var finalMergeLabel = context.Label();
|
||||
|
||||
context.SelectionMerge(finalMergeLabel, SelectionControlMask.MaskNone);
|
||||
context.BranchConditional(passthrough, passthroughLabel, finalMergeLabel);
|
||||
|
||||
context.AddLabel(passthroughLabel);
|
||||
|
||||
context.Store(localVector, vector);
|
||||
context.Branch(finalMergeLabel);
|
||||
|
||||
context.AddLabel(finalMergeLabel);
|
||||
|
||||
return context.Load(ivector2Type, localVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
var passthroughLabel = context.Label();
|
||||
var scaledLabel = context.Label();
|
||||
|
||||
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
|
||||
context.BranchConditional(passthrough, passthroughLabel, scaledLabel);
|
||||
|
||||
// scale == 1.0
|
||||
context.AddLabel(passthroughLabel);
|
||||
|
||||
context.Store(localVector, vector);
|
||||
context.Branch(mergeLabel);
|
||||
|
||||
// scale != 1.0
|
||||
context.AddLabel(scaledLabel);
|
||||
|
||||
ApplyScalingNoInterpolation(context, localVector, vector, scale);
|
||||
context.Branch(mergeLabel);
|
||||
|
||||
context.AddLabel(mergeLabel);
|
||||
|
||||
return context.Load(ivector2Type, localVector);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyScalingInterpolated(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
|
||||
{
|
||||
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
|
||||
|
||||
var scaleNegated = context.FNegate(context.TypeFP32(), scale);
|
||||
var scaleVector = context.CompositeConstruct(vector2Type, scaleNegated, scaleNegated);
|
||||
|
||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated);
|
||||
|
||||
var fragCoordPointer = context.Inputs[AttributeConsts.PositionX];
|
||||
var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer);
|
||||
var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1);
|
||||
|
||||
var scaleMod = context.FMod(vector2Type, fragCoordXY, scaleVector);
|
||||
var vectorInterpolated = context.FAdd(vector2Type, vectorScaled, scaleMod);
|
||||
|
||||
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorInterpolated));
|
||||
}
|
||||
|
||||
private static void ApplyScalingNoInterpolation(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
|
||||
{
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
scale = context.GlslFAbs(context.TypeFP32(), scale);
|
||||
}
|
||||
|
||||
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
|
||||
|
||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scale);
|
||||
|
||||
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorScaled));
|
||||
}
|
||||
|
||||
public static SpvInstruction ApplyUnscaling(
|
||||
CodeGenContext context,
|
||||
AstTextureOperation texOp,
|
||||
SpvInstruction size,
|
||||
bool isBindless,
|
||||
bool isIndexed)
|
||||
{
|
||||
if (context.Config.Stage.SupportsRenderScale() &&
|
||||
!isBindless &&
|
||||
!isIndexed)
|
||||
{
|
||||
int index = context.Config.FindTextureDescriptorIndex(texOp);
|
||||
|
||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
||||
var fieldIndex = context.Constant(context.TypeU32(), 4);
|
||||
var scaleIndex = context.Constant(context.TypeU32(), index);
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.SupportBuffer, context.Constant(context.TypeU32(), 3));
|
||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
||||
|
||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
||||
}
|
||||
|
||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
||||
|
||||
var scaleElemPointer = context.AccessChain(pointerType, context.SupportBuffer, fieldIndex, scaleIndex);
|
||||
var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer));
|
||||
|
||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
||||
|
||||
var sizeFloat = context.ConvertSToF(context.TypeFP32(), size);
|
||||
var sizeUnscaled = context.FDiv(context.TypeFP32(), sizeFloat, scale);
|
||||
var sizeUnscaledInt = context.ConvertFToS(context.TypeS32(), sizeUnscaled);
|
||||
|
||||
return context.Select(context.TypeS32(), passthrough, size, sizeUnscaledInt);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
|
||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Pset(EmitterContext context)
|
||||
{
|
||||
InstPset op = context.GetOp<InstPset>();
|
||||
|
||||
Operand srcA = context.BitwiseNot(Register(op.Src2Pred, RegisterType.Predicate), op.Src2PredInv);
|
||||
Operand srcB = context.BitwiseNot(Register(op.Src1Pred, RegisterType.Predicate), op.Src1PredInv);
|
||||
Operand srcC = context.BitwiseNot(Register(op.SrcPred, RegisterType.Predicate), op.SrcPredInv);
|
||||
|
||||
Operand res = GetPredLogicalOp(context, op.BoolOpAB, srcA, srcB);
|
||||
res = GetPredLogicalOp(context, op.BoolOpC, res, srcC);
|
||||
|
||||
Operand dest = GetDest(op.Dest);
|
||||
|
||||
if (op.BVal)
|
||||
{
|
||||
context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Copy(dest, res);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Psetp(EmitterContext context)
|
||||
{
|
||||
InstPsetp op = context.GetOp<InstPsetp>();
|
||||
|
||||
Operand srcA = context.BitwiseNot(Register(op.Src2Pred, RegisterType.Predicate), op.Src2PredInv);
|
||||
Operand srcB = context.BitwiseNot(Register(op.Src1Pred, RegisterType.Predicate), op.Src1PredInv);
|
||||
|
||||
Operand p0Res = GetPredLogicalOp(context, op.BoolOpAB, srcA, srcB);
|
||||
Operand p1Res = context.BitwiseNot(p0Res);
|
||||
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
|
||||
|
||||
p0Res = GetPredLogicalOp(context, op.BoolOpC, p0Res, srcPred);
|
||||
p1Res = GetPredLogicalOp(context, op.BoolOpC, p1Res, srcPred);
|
||||
|
||||
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
|
||||
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
{
|
||||
enum OperandType
|
||||
{
|
||||
Argument,
|
||||
Attribute,
|
||||
AttributePerPatch,
|
||||
Constant,
|
||||
ConstantBuffer,
|
||||
Label,
|
||||
LocalVariable,
|
||||
Register,
|
||||
Undefined
|
||||
}
|
||||
|
||||
static class OperandTypeExtensions
|
||||
{
|
||||
public static bool IsAttribute(this OperandType type)
|
||||
{
|
||||
return type == OperandType.Attribute || type == OperandType.AttributePerPatch;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
{
|
||||
readonly struct TransformFeedbackOutput
|
||||
{
|
||||
public readonly bool Valid;
|
||||
public readonly int Buffer;
|
||||
public readonly int Offset;
|
||||
public readonly int Stride;
|
||||
|
||||
public TransformFeedbackOutput(int buffer, int offset, int stride)
|
||||
{
|
||||
Valid = true;
|
||||
Buffer = buffer;
|
||||
Offset = offset;
|
||||
Stride = stride;
|
||||
}
|
||||
}
|
||||
|
||||
class StructuredProgramInfo
|
||||
{
|
||||
public List<StructuredFunction> Functions { get; }
|
||||
|
||||
public HashSet<int> Inputs { get; }
|
||||
public HashSet<int> Outputs { get; }
|
||||
public HashSet<int> InputsPerPatch { get; }
|
||||
public HashSet<int> OutputsPerPatch { get; }
|
||||
|
||||
public HelperFunctionsMask HelperFunctionsMask { get; set; }
|
||||
|
||||
public TransformFeedbackOutput[] TransformFeedbackOutputs { get; }
|
||||
|
||||
public StructuredProgramInfo()
|
||||
{
|
||||
Functions = new List<StructuredFunction>();
|
||||
|
||||
Inputs = new HashSet<int>();
|
||||
Outputs = new HashSet<int>();
|
||||
InputsPerPatch = new HashSet<int>();
|
||||
OutputsPerPatch = new HashSet<int>();
|
||||
|
||||
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
|
||||
}
|
||||
|
||||
public TransformFeedbackOutput GetTransformFeedbackOutput(int attr)
|
||||
{
|
||||
int index = attr / 4;
|
||||
return TransformFeedbackOutputs[index];
|
||||
}
|
||||
|
||||
public int GetTransformFeedbackOutputComponents(int attr)
|
||||
{
|
||||
int index = attr / 4;
|
||||
int baseIndex = index & ~3;
|
||||
|
||||
int count = 1;
|
||||
|
||||
for (; count < 4; count++)
|
||||
{
|
||||
ref var prev = ref TransformFeedbackOutputs[baseIndex + count - 1];
|
||||
ref var curr = ref TransformFeedbackOutputs[baseIndex + count];
|
||||
|
||||
int prevOffset = prev.Offset;
|
||||
int currOffset = curr.Offset;
|
||||
|
||||
if (!prev.Valid || !curr.Valid || prevOffset + 4 != currOffset)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (baseIndex + count <= index)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
enum AggregateType
|
||||
{
|
||||
Invalid,
|
||||
Void,
|
||||
Bool,
|
||||
FP32,
|
||||
FP64,
|
||||
S32,
|
||||
U32,
|
||||
|
||||
ElementTypeMask = 0xff,
|
||||
|
||||
ElementCountShift = 8,
|
||||
ElementCountMask = 3 << ElementCountShift,
|
||||
|
||||
Scalar = 0 << ElementCountShift,
|
||||
Vector2 = 1 << ElementCountShift,
|
||||
Vector3 = 2 << ElementCountShift,
|
||||
Vector4 = 3 << ElementCountShift,
|
||||
|
||||
Array = 1 << 10
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
static class AttributeConsts
|
||||
{
|
||||
public const int TessLevelOuter0 = 0x000;
|
||||
public const int TessLevelOuter1 = 0x004;
|
||||
public const int TessLevelOuter2 = 0x008;
|
||||
public const int TessLevelOuter3 = 0x00c;
|
||||
public const int TessLevelInner0 = 0x010;
|
||||
public const int TessLevelInner1 = 0x014;
|
||||
public const int PrimitiveId = 0x060;
|
||||
public const int Layer = 0x064;
|
||||
public const int ViewportIndex = 0x068;
|
||||
public const int PointSize = 0x06c;
|
||||
public const int PositionX = 0x070;
|
||||
public const int PositionY = 0x074;
|
||||
public const int PositionZ = 0x078;
|
||||
public const int PositionW = 0x07c;
|
||||
public const int FrontColorDiffuseR = 0x280;
|
||||
public const int FrontColorDiffuseG = 0x284;
|
||||
public const int FrontColorDiffuseB = 0x288;
|
||||
public const int FrontColorDiffuseA = 0x28c;
|
||||
public const int FrontColorSpecularR = 0x290;
|
||||
public const int FrontColorSpecularG = 0x294;
|
||||
public const int FrontColorSpecularB = 0x298;
|
||||
public const int FrontColorSpecularA = 0x29c;
|
||||
public const int BackColorDiffuseR = 0x2a0;
|
||||
public const int BackColorDiffuseG = 0x2a4;
|
||||
public const int BackColorDiffuseB = 0x2a8;
|
||||
public const int BackColorDiffuseA = 0x2ac;
|
||||
public const int BackColorSpecularR = 0x2b0;
|
||||
public const int BackColorSpecularG = 0x2b4;
|
||||
public const int BackColorSpecularB = 0x2b8;
|
||||
public const int BackColorSpecularA = 0x2bc;
|
||||
public const int ClipDistance0 = 0x2c0;
|
||||
public const int ClipDistance1 = 0x2c4;
|
||||
public const int ClipDistance2 = 0x2c8;
|
||||
public const int ClipDistance3 = 0x2cc;
|
||||
public const int ClipDistance4 = 0x2d0;
|
||||
public const int ClipDistance5 = 0x2d4;
|
||||
public const int ClipDistance6 = 0x2d8;
|
||||
public const int ClipDistance7 = 0x2dc;
|
||||
public const int PointCoordX = 0x2e0;
|
||||
public const int PointCoordY = 0x2e4;
|
||||
public const int TessCoordX = 0x2f0;
|
||||
public const int TessCoordY = 0x2f4;
|
||||
public const int InstanceId = 0x2f8;
|
||||
public const int VertexId = 0x2fc;
|
||||
public const int TexCoordCount = 10;
|
||||
public const int TexCoordBase = 0x300;
|
||||
public const int TexCoordEnd = TexCoordBase + TexCoordCount * 16;
|
||||
public const int FrontFacing = 0x3fc;
|
||||
|
||||
public const int UserAttributesCount = 32;
|
||||
public const int UserAttributeBase = 0x80;
|
||||
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
|
||||
|
||||
public const int UserAttributePerPatchBase = 0x18;
|
||||
public const int UserAttributePerPatchEnd = 0x200;
|
||||
|
||||
public const int LoadOutputMask = 1 << 30;
|
||||
public const int Mask = 0x3fffffff;
|
||||
|
||||
|
||||
// Note: Those attributes are used internally by the translator
|
||||
// only, they don't exist on Maxwell.
|
||||
public const int SpecialMask = 0xf << 24;
|
||||
public const int FragmentOutputDepth = 0x1000000;
|
||||
public const int FragmentOutputColorBase = 0x1000010;
|
||||
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
||||
|
||||
public const int FragmentOutputIsBgraBase = 0x1000100;
|
||||
public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4;
|
||||
|
||||
public const int SupportBlockViewInverseX = 0x1000200;
|
||||
public const int SupportBlockViewInverseY = 0x1000204;
|
||||
|
||||
public const int ThreadIdX = 0x2000000;
|
||||
public const int ThreadIdY = 0x2000004;
|
||||
public const int ThreadIdZ = 0x2000008;
|
||||
|
||||
public const int CtaIdX = 0x2000010;
|
||||
public const int CtaIdY = 0x2000014;
|
||||
public const int CtaIdZ = 0x2000018;
|
||||
|
||||
public const int LaneId = 0x2000020;
|
||||
|
||||
public const int InvocationId = 0x2000024;
|
||||
public const int PatchVerticesIn = 0x2000028;
|
||||
|
||||
public const int EqMask = 0x2000030;
|
||||
public const int GeMask = 0x2000034;
|
||||
public const int GtMask = 0x2000038;
|
||||
public const int LeMask = 0x200003c;
|
||||
public const int LtMask = 0x2000040;
|
||||
|
||||
public const int ThreadKill = 0x2000044;
|
||||
|
||||
public const int BaseInstance = 0x2000050;
|
||||
public const int BaseVertex = 0x2000054;
|
||||
public const int InstanceIndex = 0x2000058;
|
||||
public const int VertexIndex = 0x200005c;
|
||||
public const int DrawIndex = 0x2000060;
|
||||
}
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
readonly struct AttributeInfo
|
||||
{
|
||||
private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = new Dictionary<int, AttributeInfo>()
|
||||
{
|
||||
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 0, 1, AggregateType.FP32) },
|
||||
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 0, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
||||
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionX, 1, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
||||
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionX, 2, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
||||
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionX, 3, 4, AggregateType.Vector4 | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 0, 2, AggregateType.Vector4 | AggregateType.FP32) },
|
||||
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordX, 1, 2, AggregateType.Vector4 | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 0, 3, AggregateType.Vector4 | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordX, 1, 3, AggregateType.Vector4 | AggregateType.FP32) },
|
||||
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.BaseInstance, new AttributeInfo(AttributeConsts.BaseInstance, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
|
||||
|
||||
// Special.
|
||||
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },
|
||||
{ AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 0, 1, AggregateType.Bool) },
|
||||
{ AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
|
||||
{ AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
|
||||
{ AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
|
||||
{ AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 0, 3, AggregateType.Vector3 | AggregateType.U32) },
|
||||
{ AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdX, 1, 3, AggregateType.Vector3 | AggregateType.U32) },
|
||||
{ AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdX, 2, 3, AggregateType.Vector3 | AggregateType.U32) },
|
||||
{ AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 0, 1, AggregateType.U32) },
|
||||
{ AttributeConsts.InvocationId, new AttributeInfo(AttributeConsts.InvocationId, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.PrimitiveId, new AttributeInfo(AttributeConsts.PrimitiveId, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.PatchVerticesIn, new AttributeInfo(AttributeConsts.PatchVerticesIn, 0, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
||||
{ AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
||||
{ AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
||||
{ AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
||||
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 0, 4, AggregateType.Vector4 | AggregateType.U32) },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<int, AttributeInfo> _builtInAttributesPerPatch = new Dictionary<int, AttributeInfo>()
|
||||
{
|
||||
{ AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array | AggregateType.FP32) },
|
||||
};
|
||||
|
||||
public int BaseValue { get; }
|
||||
public int Value { get; }
|
||||
public int Length { get; }
|
||||
public AggregateType Type { get; }
|
||||
public bool IsBuiltin { get; }
|
||||
public bool IsValid => Type != AggregateType.Invalid;
|
||||
|
||||
public AttributeInfo(int baseValue, int index, int length, AggregateType type, bool isBuiltin = true)
|
||||
{
|
||||
BaseValue = baseValue;
|
||||
Value = baseValue + index * 4;
|
||||
Length = length;
|
||||
Type = type;
|
||||
IsBuiltin = isBuiltin;
|
||||
}
|
||||
|
||||
public int GetInnermostIndex()
|
||||
{
|
||||
return (Value - BaseValue) / 4;
|
||||
}
|
||||
|
||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr, bool perPatch)
|
||||
{
|
||||
return perPatch ? ValidatePerPatch(config, value, isOutAttr) : Validate(config, value, isOutAttr);
|
||||
}
|
||||
|
||||
public static bool Validate(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
if (value == AttributeConsts.ViewportIndex && !config.GpuAccessor.QueryHostSupportsViewportIndex())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return From(config, value, isOutAttr).IsValid;
|
||||
}
|
||||
|
||||
public static bool ValidatePerPatch(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
return FromPatch(config, value, isOutAttr).IsValid;
|
||||
}
|
||||
|
||||
public static AttributeInfo From(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
value &= ~3;
|
||||
|
||||
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
int location = (value - AttributeConsts.UserAttributeBase) / 16;
|
||||
|
||||
AggregateType elemType;
|
||||
|
||||
if (config.Stage == ShaderStage.Vertex && !isOutAttr)
|
||||
{
|
||||
elemType = config.GpuAccessor.QueryAttributeType(location).ToAggregateType();
|
||||
}
|
||||
else
|
||||
{
|
||||
elemType = AggregateType.FP32;
|
||||
}
|
||||
|
||||
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
|
||||
}
|
||||
else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
|
||||
{
|
||||
int location = (value - AttributeConsts.FragmentOutputColorBase) / 16;
|
||||
var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch
|
||||
{
|
||||
AttributeType.Sint => AggregateType.S32,
|
||||
AttributeType.Uint => AggregateType.U32,
|
||||
_ => AggregateType.FP32
|
||||
};
|
||||
|
||||
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
|
||||
}
|
||||
else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY)
|
||||
{
|
||||
return new AttributeInfo(value, 0, 1, AggregateType.FP32);
|
||||
}
|
||||
else if (_builtInAttributes.TryGetValue(value, out AttributeInfo info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
|
||||
}
|
||||
|
||||
public static AttributeInfo FromPatch(ShaderConfig config, int value, bool isOutAttr)
|
||||
{
|
||||
value &= ~3;
|
||||
|
||||
if (value >= AttributeConsts.UserAttributePerPatchBase && value < AttributeConsts.UserAttributePerPatchEnd)
|
||||
{
|
||||
int offset = (value - AttributeConsts.UserAttributePerPatchBase) & 0xf;
|
||||
return new AttributeInfo(value - offset, offset >> 2, 4, AggregateType.Vector4 | AggregateType.FP32, false);
|
||||
}
|
||||
else if (_builtInAttributesPerPatch.TryGetValue(value, out AttributeInfo info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
return new AttributeInfo(value, 0, 0, AggregateType.Invalid);
|
||||
}
|
||||
|
||||
public static bool IsArrayBuiltIn(int attr)
|
||||
{
|
||||
if (attr <= AttributeConsts.TessLevelInner1 ||
|
||||
attr == AttributeConsts.TessCoordX ||
|
||||
attr == AttributeConsts.TessCoordY)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (attr & AttributeConsts.SpecialMask) == 0;
|
||||
}
|
||||
|
||||
public static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr)
|
||||
{
|
||||
if (isOutAttr)
|
||||
{
|
||||
return stage == ShaderStage.TessellationControl;
|
||||
}
|
||||
else
|
||||
{
|
||||
return stage == ShaderStage.TessellationControl ||
|
||||
stage == ShaderStage.TessellationEvaluation ||
|
||||
stage == ShaderStage.Geometry;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsArrayAttributeSpirv(ShaderStage stage, bool isOutAttr)
|
||||
{
|
||||
if (isOutAttr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return stage == ShaderStage.TessellationControl ||
|
||||
stage == ShaderStage.TessellationEvaluation ||
|
||||
stage == ShaderStage.Geometry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class PipelineLayoutCache
|
||||
{
|
||||
private readonly PipelineLayoutCacheEntry[] _plce;
|
||||
private readonly List<PipelineLayoutCacheEntry> _plceMinimal;
|
||||
|
||||
public PipelineLayoutCache()
|
||||
{
|
||||
_plce = new PipelineLayoutCacheEntry[1 << Constants.MaxShaderStages];
|
||||
_plceMinimal = new List<PipelineLayoutCacheEntry>();
|
||||
}
|
||||
|
||||
public PipelineLayoutCacheEntry Create(VulkanRenderer gd, Device device, ShaderSource[] shaders)
|
||||
{
|
||||
var plce = new PipelineLayoutCacheEntry(gd, device, shaders);
|
||||
_plceMinimal.Add(plce);
|
||||
return plce;
|
||||
}
|
||||
|
||||
public PipelineLayoutCacheEntry GetOrCreate(VulkanRenderer gd, Device device, uint stages, bool usePd)
|
||||
{
|
||||
if (_plce[stages] == null)
|
||||
{
|
||||
_plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd);
|
||||
}
|
||||
|
||||
return _plce[stages];
|
||||
}
|
||||
|
||||
protected virtual unsafe void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
for (int i = 0; i < _plce.Length; i++)
|
||||
{
|
||||
_plce[i]?.Dispose();
|
||||
}
|
||||
|
||||
foreach (var plce in _plceMinimal)
|
||||
{
|
||||
plce.Dispose();
|
||||
}
|
||||
|
||||
_plceMinimal.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
static class PipelineLayoutFactory
|
||||
{
|
||||
private const ShaderStageFlags SupportBufferStages =
|
||||
ShaderStageFlags.VertexBit |
|
||||
ShaderStageFlags.FragmentBit |
|
||||
ShaderStageFlags.ComputeBit;
|
||||
|
||||
public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
|
||||
{
|
||||
int stagesCount = BitOperations.PopCount(stages);
|
||||
|
||||
int uCount = Constants.MaxUniformBuffersPerStage * stagesCount + 1;
|
||||
int tCount = Constants.MaxTexturesPerStage * 2 * stagesCount;
|
||||
int iCount = Constants.MaxImagesPerStage * 2 * stagesCount;
|
||||
|
||||
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
|
||||
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount];
|
||||
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
|
||||
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
|
||||
|
||||
uLayoutBindings[0] = new DescriptorSetLayoutBinding
|
||||
{
|
||||
Binding = 0,
|
||||
DescriptorType = DescriptorType.UniformBuffer,
|
||||
DescriptorCount = 1,
|
||||
StageFlags = SupportBufferStages
|
||||
};
|
||||
|
||||
int iter = 0;
|
||||
|
||||
while (stages != 0)
|
||||
{
|
||||
int stage = BitOperations.TrailingZeroCount(stages);
|
||||
stages &= ~(1u << stage);
|
||||
|
||||
var stageFlags = stage switch
|
||||
{
|
||||
1 => ShaderStageFlags.FragmentBit,
|
||||
2 => ShaderStageFlags.GeometryBit,
|
||||
3 => ShaderStageFlags.TessellationControlBit,
|
||||
4 => ShaderStageFlags.TessellationEvaluationBit,
|
||||
_ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
|
||||
};
|
||||
|
||||
void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType type, int start, int skip)
|
||||
{
|
||||
int totalPerStage = maxPerStage * skip;
|
||||
|
||||
for (int i = 0; i < maxPerStage; i++)
|
||||
{
|
||||
bindings[start + iter * totalPerStage + i] = new DescriptorSetLayoutBinding
|
||||
{
|
||||
Binding = (uint)(start + stage * totalPerStage + i),
|
||||
DescriptorType = type,
|
||||
DescriptorCount = 1,
|
||||
StageFlags = stageFlags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0)
|
||||
{
|
||||
bindings[start + iter] = new DescriptorSetLayoutBinding
|
||||
{
|
||||
Binding = (uint)(start + stage * maxPerStage),
|
||||
DescriptorType = DescriptorType.StorageBuffer,
|
||||
DescriptorCount = (uint)maxPerStage,
|
||||
StageFlags = stageFlags
|
||||
};
|
||||
}
|
||||
|
||||
Set(uLayoutBindings, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer, 1, 1);
|
||||
SetStorage(sLayoutBindings, Constants.MaxStorageBuffersPerStage);
|
||||
Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.CombinedImageSampler, 0, 2);
|
||||
Set(tLayoutBindings, Constants.MaxTexturesPerStage, DescriptorType.UniformTexelBuffer, Constants.MaxTexturesPerStage, 2);
|
||||
Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageImage, 0, 2);
|
||||
Set(iLayoutBindings, Constants.MaxImagesPerStage, DescriptorType.StorageTexelBuffer, Constants.MaxImagesPerStage, 2);
|
||||
|
||||
iter++;
|
||||
}
|
||||
|
||||
DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
|
||||
|
||||
var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = uLayoutBindings,
|
||||
BindingCount = (uint)uCount,
|
||||
Flags = usePd ? DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr : 0
|
||||
};
|
||||
|
||||
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = sLayoutBindings,
|
||||
BindingCount = (uint)stagesCount
|
||||
};
|
||||
|
||||
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = tLayoutBindings,
|
||||
BindingCount = (uint)tCount
|
||||
};
|
||||
|
||||
var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = iLayoutBindings,
|
||||
BindingCount = (uint)iCount
|
||||
};
|
||||
|
||||
gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
|
||||
gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
|
||||
gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
|
||||
gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
|
||||
|
||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
||||
{
|
||||
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.PipelineLayoutCreateInfo,
|
||||
PSetLayouts = pLayouts,
|
||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
||||
};
|
||||
|
||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
||||
}
|
||||
|
||||
return layouts;
|
||||
}
|
||||
|
||||
public static unsafe DescriptorSetLayout[] CreateMinimal(VulkanRenderer gd, Device device, ShaderSource[] shaders, out PipelineLayout layout)
|
||||
{
|
||||
int stagesCount = shaders.Length;
|
||||
|
||||
int uCount = 0;
|
||||
int sCount = 0;
|
||||
int tCount = 0;
|
||||
int iCount = 0;
|
||||
|
||||
foreach (var shader in shaders)
|
||||
{
|
||||
uCount += shader.Bindings.UniformBufferBindings.Count;
|
||||
sCount += shader.Bindings.StorageBufferBindings.Count;
|
||||
tCount += shader.Bindings.TextureBindings.Count;
|
||||
iCount += shader.Bindings.ImageBindings.Count;
|
||||
}
|
||||
|
||||
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
|
||||
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount];
|
||||
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
|
||||
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
|
||||
|
||||
int uIndex = 0;
|
||||
int sIndex = 0;
|
||||
int tIndex = 0;
|
||||
int iIndex = 0;
|
||||
|
||||
foreach (var shader in shaders)
|
||||
{
|
||||
var stageFlags = shader.Stage.Convert();
|
||||
|
||||
void Set(DescriptorSetLayoutBinding* bindings, DescriptorType type, ref int start, IEnumerable<int> bds)
|
||||
{
|
||||
foreach (var b in bds)
|
||||
{
|
||||
bindings[start++] = new DescriptorSetLayoutBinding
|
||||
{
|
||||
Binding = (uint)b,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = 1,
|
||||
StageFlags = stageFlags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support buffer textures and images here.
|
||||
// This is only used for the helper shaders on the backend, and we don't use buffer textures on them
|
||||
// so far, so it's not really necessary right now.
|
||||
Set(uLayoutBindings, DescriptorType.UniformBuffer, ref uIndex, shader.Bindings.UniformBufferBindings);
|
||||
Set(sLayoutBindings, DescriptorType.StorageBuffer, ref sIndex, shader.Bindings.StorageBufferBindings);
|
||||
Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings);
|
||||
Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings);
|
||||
}
|
||||
|
||||
DescriptorSetLayout[] layouts = new DescriptorSetLayout[PipelineBase.DescriptorSetLayouts];
|
||||
|
||||
var uDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = uLayoutBindings,
|
||||
BindingCount = (uint)uCount
|
||||
};
|
||||
|
||||
var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = sLayoutBindings,
|
||||
BindingCount = (uint)sCount
|
||||
};
|
||||
|
||||
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = tLayoutBindings,
|
||||
BindingCount = (uint)tCount
|
||||
};
|
||||
|
||||
var iDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||
PBindings = iLayoutBindings,
|
||||
BindingCount = (uint)iCount
|
||||
};
|
||||
|
||||
gd.Api.CreateDescriptorSetLayout(device, uDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.UniformSetIndex]).ThrowOnError();
|
||||
gd.Api.CreateDescriptorSetLayout(device, sDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.StorageSetIndex]).ThrowOnError();
|
||||
gd.Api.CreateDescriptorSetLayout(device, tDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.TextureSetIndex]).ThrowOnError();
|
||||
gd.Api.CreateDescriptorSetLayout(device, iDescriptorSetLayoutCreateInfo, null, out layouts[PipelineBase.ImageSetIndex]).ThrowOnError();
|
||||
|
||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
||||
{
|
||||
var pipelineLayoutCreateInfo = new PipelineLayoutCreateInfo()
|
||||
{
|
||||
SType = StructureType.PipelineLayoutCreateInfo,
|
||||
PSetLayouts = pLayouts,
|
||||
SetLayoutCount = PipelineBase.DescriptorSetLayouts
|
||||
};
|
||||
|
||||
gd.Api.CreatePipelineLayout(device, &pipelineLayoutCreateInfo, null, out layout).ThrowOnError();
|
||||
}
|
||||
|
||||
return layouts;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,908 +0,0 @@
|
||||
using LibHac;
|
||||
using LibHac.Account;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Loader;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Tools.Fs;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using static Ryujinx.HLE.HOS.ModLoader;
|
||||
using ApplicationId = LibHac.Ncm.ApplicationId;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Ryujinx.HLE.HOS
|
||||
{
|
||||
using JsonHelper = Common.Utilities.JsonHelper;
|
||||
|
||||
public class ApplicationLoader
|
||||
{
|
||||
// Binaries from exefs are loaded into mem in this order. Do not change.
|
||||
internal static readonly string[] ExeFsPrefixes =
|
||||
{
|
||||
"rtld",
|
||||
"main",
|
||||
"subsdk0",
|
||||
"subsdk1",
|
||||
"subsdk2",
|
||||
"subsdk3",
|
||||
"subsdk4",
|
||||
"subsdk5",
|
||||
"subsdk6",
|
||||
"subsdk7",
|
||||
"subsdk8",
|
||||
"subsdk9",
|
||||
"sdk"
|
||||
};
|
||||
|
||||
private readonly Switch _device;
|
||||
private string _titleName;
|
||||
private string _displayVersion;
|
||||
private BlitStruct<ApplicationControlProperty> _controlData;
|
||||
|
||||
public BlitStruct<ApplicationControlProperty> ControlData => _controlData;
|
||||
public string TitleName => _titleName;
|
||||
public string DisplayVersion => _displayVersion;
|
||||
|
||||
public ulong TitleId { get; private set; }
|
||||
public bool TitleIs64Bit { get; private set; }
|
||||
|
||||
public string TitleIdText => TitleId.ToString("x16");
|
||||
|
||||
public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
|
||||
|
||||
public ApplicationLoader(Switch device)
|
||||
{
|
||||
_device = device;
|
||||
_controlData = new BlitStruct<ApplicationControlProperty>(1);
|
||||
}
|
||||
|
||||
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||
{
|
||||
LocalFileSystem codeFs = new LocalFileSystem(exeFsDir);
|
||||
|
||||
MetaLoader metaData = ReadNpdm(codeFs);
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
|
||||
new[] { TitleId },
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
|
||||
|
||||
if (TitleId != 0)
|
||||
{
|
||||
EnsureSaveData(new ApplicationId(TitleId));
|
||||
}
|
||||
|
||||
ulong pid = LoadExeFs(codeFs, string.Empty, metaData);
|
||||
|
||||
if (romFsFile != null)
|
||||
{
|
||||
_device.Configuration.VirtualFileSystem.LoadRomFs(pid, romFsFile);
|
||||
}
|
||||
}
|
||||
|
||||
public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex)
|
||||
{
|
||||
Nca mainNca = null;
|
||||
Nca patchNca = null;
|
||||
Nca controlNca = null;
|
||||
|
||||
fileSystem.ImportTickets(pfs);
|
||||
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
using var ncaFile = new UniqueRef<IFile>();
|
||||
|
||||
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
||||
|
||||
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
|
||||
|
||||
if (ncaProgramIndex != programIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nca.Header.ContentType == NcaContentType.Program)
|
||||
{
|
||||
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
|
||||
|
||||
if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
|
||||
{
|
||||
patchNca = nca;
|
||||
}
|
||||
else
|
||||
{
|
||||
mainNca = nca;
|
||||
}
|
||||
}
|
||||
else if (nca.Header.ContentType == NcaContentType.Control)
|
||||
{
|
||||
controlNca = nca;
|
||||
}
|
||||
}
|
||||
|
||||
return (mainNca, patchNca, controlNca);
|
||||
}
|
||||
|
||||
public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex)
|
||||
{
|
||||
Nca patchNca = null;
|
||||
Nca controlNca = null;
|
||||
|
||||
fileSystem.ImportTickets(pfs);
|
||||
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
using var ncaFile = new UniqueRef<IFile>();
|
||||
|
||||
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
||||
|
||||
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
|
||||
|
||||
if (ncaProgramIndex != programIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (nca.Header.ContentType == NcaContentType.Program)
|
||||
{
|
||||
patchNca = nca;
|
||||
}
|
||||
else if (nca.Header.ContentType == NcaContentType.Control)
|
||||
{
|
||||
controlNca = nca;
|
||||
}
|
||||
}
|
||||
|
||||
return (patchNca, controlNca);
|
||||
}
|
||||
|
||||
public static (Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath)
|
||||
{
|
||||
updatePath = null;
|
||||
|
||||
if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase))
|
||||
{
|
||||
// Clear the program index part.
|
||||
titleIdBase &= 0xFFFFFFFFFFFFFFF0;
|
||||
|
||||
// Load update informations if existing.
|
||||
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
|
||||
|
||||
if (File.Exists(titleUpdateMetadataPath))
|
||||
{
|
||||
updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
|
||||
|
||||
if (File.Exists(updatePath))
|
||||
{
|
||||
FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
|
||||
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
|
||||
|
||||
return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
public void LoadXci(string xciFile)
|
||||
{
|
||||
FileStream file = new FileStream(xciFile, FileMode.Open, FileAccess.Read);
|
||||
Xci xci = new Xci(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage());
|
||||
|
||||
if (!xci.HasPartition(XciPartitionType.Secure))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find XCI secure partition");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PartitionFileSystem securePartition = xci.OpenPartition(XciPartitionType.Secure);
|
||||
|
||||
Nca mainNca;
|
||||
Nca patchNca;
|
||||
Nca controlNca;
|
||||
|
||||
try
|
||||
{
|
||||
(mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, securePartition, _device.Configuration.UserChannelPersistence.Index);
|
||||
|
||||
RegisterProgramMapInfo(securePartition).ThrowIfFailure();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, $"Unable to load XCI: {e.Message}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mainNca == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find Main NCA");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_device.Configuration.ContentManager.LoadEntries(_device);
|
||||
_device.Configuration.ContentManager.ClearAocData();
|
||||
_device.Configuration.ContentManager.AddAocData(securePartition, xciFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel);
|
||||
|
||||
LoadNca(mainNca, patchNca, controlNca);
|
||||
}
|
||||
|
||||
public void LoadNsp(string nspFile)
|
||||
{
|
||||
FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read);
|
||||
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
|
||||
|
||||
Nca mainNca;
|
||||
Nca patchNca;
|
||||
Nca controlNca;
|
||||
|
||||
try
|
||||
{
|
||||
(mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, nsp, _device.Configuration.UserChannelPersistence.Index);
|
||||
|
||||
RegisterProgramMapInfo(nsp).ThrowIfFailure();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, $"Unable to load NSP: {e.Message}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mainNca != null)
|
||||
{
|
||||
_device.Configuration.ContentManager.ClearAocData();
|
||||
_device.Configuration.ContentManager.AddAocData(nsp, nspFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel);
|
||||
|
||||
LoadNca(mainNca, patchNca, controlNca);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// This is not a normal NSP, it's actually a ExeFS as a NSP
|
||||
LoadExeFs(nsp, null, isHomebrew: true);
|
||||
}
|
||||
|
||||
public void LoadNca(string ncaFile)
|
||||
{
|
||||
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
||||
Nca nca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||
|
||||
LoadNca(nca, null, null);
|
||||
}
|
||||
|
||||
public void LoadServiceNca(string ncaFile)
|
||||
{
|
||||
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
||||
Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||
|
||||
if (mainNca.Header.ContentType != NcaContentType.Program)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IFileSystem codeFs = null;
|
||||
|
||||
if (mainNca.CanOpenSection(NcaSectionType.Code))
|
||||
{
|
||||
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
|
||||
}
|
||||
|
||||
if (codeFs == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
using var npdmFile = new UniqueRef<IFile>();
|
||||
|
||||
Result result = codeFs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
|
||||
|
||||
MetaLoader metaData;
|
||||
|
||||
npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure();
|
||||
|
||||
var npdmBuffer = new byte[fileSize];
|
||||
npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure();
|
||||
|
||||
metaData = new MetaLoader();
|
||||
metaData.Load(npdmBuffer).ThrowIfFailure();
|
||||
|
||||
NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length];
|
||||
|
||||
for (int i = 0; i < nsos.Length; i++)
|
||||
{
|
||||
string name = ExeFsPrefixes[i];
|
||||
|
||||
if (!codeFs.FileExists($"/{name}"))
|
||||
{
|
||||
continue; // File doesn't exist, skip.
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
|
||||
|
||||
using var nsoFile = new UniqueRef<IFile>();
|
||||
|
||||
codeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
|
||||
}
|
||||
|
||||
// Collect the nsos, ignoring ones that aren't used.
|
||||
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
|
||||
|
||||
string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString;
|
||||
bool usePtc = _device.System.EnablePtc;
|
||||
|
||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false);
|
||||
ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
|
||||
|
||||
string titleIdText = npdm.Aci.ProgramId.Value.ToString("x16");
|
||||
bool titleIs64Bit = (npdm.Meta.Flags & 1) != 0;
|
||||
|
||||
string programName = Encoding.ASCII.GetString(npdm.Meta.ProgramName).TrimEnd('\0');
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]");
|
||||
}
|
||||
|
||||
private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
|
||||
{
|
||||
if (mainNca.Header.ContentType != NcaContentType.Program)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IStorage dataStorage = null;
|
||||
IFileSystem codeFs = null;
|
||||
|
||||
(Nca updatePatchNca, Nca updateControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), _device.Configuration.UserChannelPersistence.Index, out _);
|
||||
|
||||
if (updatePatchNca != null)
|
||||
{
|
||||
patchNca = updatePatchNca;
|
||||
}
|
||||
|
||||
if (updateControlNca != null)
|
||||
{
|
||||
controlNca = updateControlNca;
|
||||
}
|
||||
|
||||
// Load program 0 control NCA as we are going to need it for display version.
|
||||
(_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
|
||||
|
||||
// Load Aoc
|
||||
string titleAocMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
|
||||
|
||||
if (File.Exists(titleAocMetadataPath))
|
||||
{
|
||||
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(titleAocMetadataPath);
|
||||
|
||||
foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
|
||||
{
|
||||
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
|
||||
{
|
||||
if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled)
|
||||
{
|
||||
_device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Cannot find AddOnContent file {downloadableContentContainer.ContainerPath}. It may have been moved or renamed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (patchNca == null)
|
||||
{
|
||||
if (mainNca.CanOpenSection(NcaSectionType.Data))
|
||||
{
|
||||
dataStorage = mainNca.OpenStorage(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
|
||||
}
|
||||
|
||||
if (mainNca.CanOpenSection(NcaSectionType.Code))
|
||||
{
|
||||
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (patchNca.CanOpenSection(NcaSectionType.Data))
|
||||
{
|
||||
dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
|
||||
}
|
||||
|
||||
if (patchNca.CanOpenSection(NcaSectionType.Code))
|
||||
{
|
||||
codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (codeFs == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MetaLoader metaData = ReadNpdm(codeFs);
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
|
||||
_device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId),
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
|
||||
|
||||
string displayVersion = string.Empty;
|
||||
|
||||
if (controlNca != null)
|
||||
{
|
||||
ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlData.ByteSpan.Clear();
|
||||
}
|
||||
|
||||
// NOTE: Nintendo doesn't guarantee that the display version will be updated on sub programs when updating a multi program application.
|
||||
// BODY: As such, to avoid PTC cache confusion, we only trust the the program 0 display version when launching a sub program.
|
||||
if (updateProgram0ControlNca != null && _device.Configuration.UserChannelPersistence.Index != 0)
|
||||
{
|
||||
string dummyTitleName = "";
|
||||
BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1);
|
||||
|
||||
ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion);
|
||||
}
|
||||
|
||||
_displayVersion = displayVersion;
|
||||
|
||||
ulong pid = LoadExeFs(codeFs, displayVersion, metaData);
|
||||
|
||||
if (dataStorage == null)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA");
|
||||
}
|
||||
else
|
||||
{
|
||||
IStorage newStorage = _device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage);
|
||||
|
||||
_device.Configuration.VirtualFileSystem.SetRomFs(pid, newStorage.AsStream(FileAccess.Read));
|
||||
}
|
||||
|
||||
// Don't create save data for system programs.
|
||||
if (TitleId != 0 && (TitleId < SystemProgramId.Start.Value || TitleId > SystemAppletId.End.Value))
|
||||
{
|
||||
// Multi-program applications can technically use any program ID for the main program, but in practice they always use 0 in the low nibble.
|
||||
// We'll know if this changes in the future because stuff will get errors when trying to mount the correct save.
|
||||
EnsureSaveData(new ApplicationId(TitleId & ~0xFul));
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]");
|
||||
}
|
||||
|
||||
// Sets TitleId, so be sure to call before using it
|
||||
private MetaLoader ReadNpdm(IFileSystem fs)
|
||||
{
|
||||
using var npdmFile = new UniqueRef<IFile>();
|
||||
|
||||
Result result = fs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
|
||||
|
||||
MetaLoader metaData;
|
||||
|
||||
if (ResultFs.PathNotFound.Includes(result))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Loader, "NPDM file not found, using default values!");
|
||||
|
||||
metaData = GetDefaultNpdm();
|
||||
}
|
||||
else
|
||||
{
|
||||
npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure();
|
||||
|
||||
var npdmBuffer = new byte[fileSize];
|
||||
npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure();
|
||||
|
||||
metaData = new MetaLoader();
|
||||
metaData.Load(npdmBuffer).ThrowIfFailure();
|
||||
}
|
||||
|
||||
metaData.GetNpdm(out var npdm).ThrowIfFailure();
|
||||
|
||||
TitleId = npdm.Aci.ProgramId.Value;
|
||||
TitleIs64Bit = (npdm.Meta.Flags & 1) != 0;
|
||||
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
|
||||
|
||||
return metaData;
|
||||
}
|
||||
|
||||
private static void ReadControlData(Switch device, Nca controlNca, ref BlitStruct<ApplicationControlProperty> controlData, ref string titleName, ref string displayVersion)
|
||||
{
|
||||
using var controlFile = new UniqueRef<IFile>();
|
||||
|
||||
IFileSystem controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel);
|
||||
Result result = controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read);
|
||||
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
result = controlFile.Get.Read(out long bytesRead, 0, controlData.ByteSpan, ReadOption.None);
|
||||
|
||||
if (result.IsSuccess() && bytesRead == controlData.ByteSpan.Length)
|
||||
{
|
||||
titleName = controlData.Value.Title[(int)device.System.State.DesiredTitleLanguage].NameString.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(titleName))
|
||||
{
|
||||
titleName = controlData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString();
|
||||
}
|
||||
|
||||
displayVersion = controlData.Value.DisplayVersionString.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
controlData.ByteSpan.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private ulong LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false)
|
||||
{
|
||||
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
|
||||
{
|
||||
metaData = null; // TODO: Check if we should retain old npdm.
|
||||
}
|
||||
|
||||
metaData ??= ReadNpdm(codeFs);
|
||||
|
||||
NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length];
|
||||
|
||||
for (int i = 0; i < nsos.Length; i++)
|
||||
{
|
||||
string name = ExeFsPrefixes[i];
|
||||
|
||||
if (!codeFs.FileExists($"/{name}"))
|
||||
{
|
||||
continue; // File doesn't exist, skip.
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
|
||||
|
||||
using var nsoFile = new UniqueRef<IFile>();
|
||||
|
||||
codeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
|
||||
}
|
||||
|
||||
// ExeFs file replacements.
|
||||
ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
|
||||
|
||||
// Collect the nsos, ignoring ones that aren't used.
|
||||
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
|
||||
|
||||
// Take the npdm from mods if present.
|
||||
if (modLoadResult.Npdm != null)
|
||||
{
|
||||
metaData = modLoadResult.Npdm;
|
||||
}
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(TitleId, programs);
|
||||
|
||||
_device.Configuration.ContentManager.LoadEntries(_device);
|
||||
|
||||
bool usePtc = _device.System.EnablePtc;
|
||||
|
||||
// Don't use PPTC if ExeFs files have been replaced.
|
||||
usePtc &= !modLoadResult.Modified;
|
||||
|
||||
if (_device.System.EnablePtc && !usePtc)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PPTC disabled.");
|
||||
}
|
||||
|
||||
Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;
|
||||
_device.Gpu.HostInitalized.Set();
|
||||
|
||||
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
|
||||
|
||||
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
|
||||
{
|
||||
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
|
||||
}
|
||||
|
||||
// We allow it for nx-hbloader because it can be used to launch homebrew.
|
||||
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew;
|
||||
|
||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit);
|
||||
ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
|
||||
|
||||
DiskCacheLoadState = result.DiskCacheLoadState;
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
|
||||
|
||||
return result.ProcessId;
|
||||
}
|
||||
|
||||
public void LoadProgram(string filePath)
|
||||
{
|
||||
MetaLoader metaData = GetDefaultNpdm();
|
||||
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
|
||||
ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true);
|
||||
|
||||
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
|
||||
|
||||
IExecutable executable;
|
||||
Stream romfsStream = null;
|
||||
|
||||
if (isNro)
|
||||
{
|
||||
FileStream input = new FileStream(filePath, FileMode.Open);
|
||||
NroExecutable obj = new NroExecutable(input.AsStorage());
|
||||
|
||||
executable = obj;
|
||||
|
||||
// Homebrew NRO can actually have some data after the actual NRO.
|
||||
if (input.Length > obj.FileSize)
|
||||
{
|
||||
input.Position = obj.FileSize;
|
||||
|
||||
BinaryReader reader = new BinaryReader(input);
|
||||
|
||||
uint asetMagic = reader.ReadUInt32();
|
||||
if (asetMagic == 0x54455341)
|
||||
{
|
||||
uint asetVersion = reader.ReadUInt32();
|
||||
if (asetVersion == 0)
|
||||
{
|
||||
ulong iconOffset = reader.ReadUInt64();
|
||||
ulong iconSize = reader.ReadUInt64();
|
||||
|
||||
ulong nacpOffset = reader.ReadUInt64();
|
||||
ulong nacpSize = reader.ReadUInt64();
|
||||
|
||||
ulong romfsOffset = reader.ReadUInt64();
|
||||
ulong romfsSize = reader.ReadUInt64();
|
||||
|
||||
if (romfsSize != 0)
|
||||
{
|
||||
romfsStream = new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset);
|
||||
}
|
||||
|
||||
if (nacpSize != 0)
|
||||
{
|
||||
input.Seek(obj.FileSize + (long)nacpOffset, SeekOrigin.Begin);
|
||||
|
||||
reader.Read(ControlData.ByteSpan);
|
||||
|
||||
ref ApplicationControlProperty nacp = ref ControlData.Value;
|
||||
|
||||
programInfo.Name = nacp.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(programInfo.Name))
|
||||
{
|
||||
programInfo.Name = nacp.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString();
|
||||
}
|
||||
|
||||
if (nacp.PresenceGroupId != 0)
|
||||
{
|
||||
programInfo.ProgramId = nacp.PresenceGroupId;
|
||||
}
|
||||
else if (nacp.SaveDataOwnerId != 0)
|
||||
{
|
||||
programInfo.ProgramId = nacp.SaveDataOwnerId;
|
||||
}
|
||||
else if (nacp.AddOnContentBaseId != 0)
|
||||
{
|
||||
programInfo.ProgramId = nacp.AddOnContentBaseId - 0x1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
programInfo.ProgramId = 0000000000000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Loader, $"Unsupported ASET header version found \"{asetVersion}\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
executable = new NsoExecutable(new LocalStorage(filePath, FileAccess.Read), Path.GetFileNameWithoutExtension(filePath));
|
||||
}
|
||||
|
||||
_device.Configuration.ContentManager.LoadEntries(_device);
|
||||
|
||||
_titleName = programInfo.Name;
|
||||
TitleId = programInfo.ProgramId;
|
||||
TitleIs64Bit = (npdm.Meta.Flags & 1) != 0;
|
||||
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
|
||||
|
||||
// Explicitly null titleid to disable the shader cache.
|
||||
Graphics.Gpu.GraphicsConfig.TitleId = null;
|
||||
_device.Gpu.HostInitalized.Set();
|
||||
|
||||
ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable);
|
||||
|
||||
if (romfsStream != null)
|
||||
{
|
||||
_device.Configuration.VirtualFileSystem.SetRomFs(result.ProcessId, romfsStream);
|
||||
}
|
||||
|
||||
DiskCacheLoadState = result.DiskCacheLoadState;
|
||||
|
||||
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
|
||||
}
|
||||
|
||||
private MetaLoader GetDefaultNpdm()
|
||||
{
|
||||
Assembly asm = Assembly.GetCallingAssembly();
|
||||
|
||||
using (Stream npdmStream = asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"))
|
||||
{
|
||||
var npdmBuffer = new byte[npdmStream.Length];
|
||||
npdmStream.Read(npdmBuffer);
|
||||
|
||||
var metaLoader = new MetaLoader();
|
||||
metaLoader.Load(npdmBuffer).ThrowIfFailure();
|
||||
|
||||
return metaLoader;
|
||||
}
|
||||
}
|
||||
|
||||
private static (ulong applicationId, int programCount) GetMultiProgramInfo(VirtualFileSystem fileSystem, PartitionFileSystem pfs)
|
||||
{
|
||||
ulong mainProgramId = 0;
|
||||
Span<bool> hasIndex = stackalloc bool[0x10];
|
||||
|
||||
fileSystem.ImportTickets(pfs);
|
||||
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
using var ncaFile = new UniqueRef<IFile>();
|
||||
|
||||
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
||||
|
||||
if (nca.Header.ContentType != NcaContentType.Program)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
|
||||
|
||||
if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong currentProgramId = nca.Header.TitleId;
|
||||
ulong currentMainProgramId = currentProgramId & ~0xFFFul;
|
||||
|
||||
if (mainProgramId == 0 && currentMainProgramId != 0)
|
||||
{
|
||||
mainProgramId = currentMainProgramId;
|
||||
}
|
||||
|
||||
if (mainProgramId != currentMainProgramId)
|
||||
{
|
||||
// As far as I know there aren't any multi-application game cards containing multi-program applications,
|
||||
// so because multi-application game cards are the only way we should run into multiple applications
|
||||
// we'll just return that there's a single program.
|
||||
return (mainProgramId, 1);
|
||||
}
|
||||
|
||||
hasIndex[(int)(currentProgramId & 0xF)] = true;
|
||||
}
|
||||
|
||||
int programCount = 0;
|
||||
|
||||
for (int i = 0; i < hasIndex.Length && hasIndex[i]; i++)
|
||||
{
|
||||
programCount++;
|
||||
}
|
||||
|
||||
return (mainProgramId, programCount);
|
||||
}
|
||||
|
||||
private Result RegisterProgramMapInfo(PartitionFileSystem pfs)
|
||||
{
|
||||
(ulong applicationId, int programCount) = GetMultiProgramInfo(_device.Configuration.VirtualFileSystem, pfs);
|
||||
|
||||
if (programCount <= 0)
|
||||
return Result.Success;
|
||||
|
||||
Span<ProgramIndexMapInfo> mapInfo = stackalloc ProgramIndexMapInfo[0x10];
|
||||
|
||||
for (int i = 0; i < programCount; i++)
|
||||
{
|
||||
mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i);
|
||||
mapInfo[i].MainProgramId = new ApplicationId(applicationId);
|
||||
mapInfo[i].ProgramIndex = (byte)i;
|
||||
}
|
||||
|
||||
return _device.System.LibHacHorizonManager.NsClient.Fs.RegisterProgramIndexMapInfo(mapInfo.Slice(0, programCount));
|
||||
}
|
||||
|
||||
private Result EnsureSaveData(ApplicationId applicationId)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, "Ensuring required savedata exists.");
|
||||
|
||||
Uid user = _device.System.AccountManager.LastOpenedUser.UserId.ToLibHacUid();
|
||||
|
||||
ref ApplicationControlProperty control = ref ControlData.Value;
|
||||
|
||||
if (LibHac.Common.Utilities.IsZeros(ControlData.ByteSpan))
|
||||
{
|
||||
// If the current application doesn't have a loaded control property, create a dummy one
|
||||
// and set the savedata sizes so a user savedata will be created.
|
||||
control = ref new BlitStruct<ApplicationControlProperty>(1).Value;
|
||||
|
||||
// The set sizes don't actually matter as long as they're non-zero because we use directory savedata.
|
||||
control.UserAccountSaveDataSize = 0x4000;
|
||||
control.UserAccountSaveDataJournalSize = 0x4000;
|
||||
control.SaveDataOwnerId = applicationId.Value;
|
||||
|
||||
Logger.Warning?.Print(LogClass.Application,
|
||||
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
|
||||
}
|
||||
|
||||
HorizonClient hos = _device.System.LibHacHorizonManager.RyujinxClient;
|
||||
Result resultCode = hos.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, in control);
|
||||
|
||||
if (resultCode.IsFailure())
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationCacheStorage. Result code {resultCode.ToStringWithName()}");
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
resultCode = hos.Fs.EnsureApplicationSaveData(out _, applicationId, in control, in user);
|
||||
|
||||
if (resultCode.IsFailure())
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationSaveData. Result code {resultCode.ToStringWithName()}");
|
||||
}
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,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.Arp;
|
||||
using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator;
|
||||
|
||||
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,384 +0,0 @@
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services
|
||||
{
|
||||
class ServerBase : IDisposable
|
||||
{
|
||||
// Must be the maximum value used by services (highest one know is the one used by nvservices = 0x8000).
|
||||
// Having a size that is too low will cause failures as data copy will fail if the receiving buffer is
|
||||
// not large enough.
|
||||
private const int PointerBufferSize = 0x8000;
|
||||
|
||||
private readonly static int[] DefaultCapabilities = new int[]
|
||||
{
|
||||
0x030363F7,
|
||||
0x1FFFFFCF,
|
||||
0x207FFFEF,
|
||||
0x47E0060F,
|
||||
0x0048BFFF,
|
||||
0x01007FFF
|
||||
};
|
||||
|
||||
private readonly KernelContext _context;
|
||||
private KProcess _selfProcess;
|
||||
|
||||
private readonly List<int> _sessionHandles = new List<int>();
|
||||
private readonly List<int> _portHandles = new List<int>();
|
||||
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
|
||||
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
|
||||
|
||||
public ManualResetEvent InitDone { get; }
|
||||
public string Name { get; }
|
||||
public Func<IpcService> SmObjectFactory { get; }
|
||||
|
||||
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
||||
{
|
||||
InitDone = new ManualResetEvent(false);
|
||||
_context = context;
|
||||
Name = name;
|
||||
SmObjectFactory = smObjectFactory;
|
||||
|
||||
const ProcessCreationFlags flags =
|
||||
ProcessCreationFlags.EnableAslr |
|
||||
ProcessCreationFlags.AddressSpace64Bit |
|
||||
ProcessCreationFlags.Is64Bit |
|
||||
ProcessCreationFlags.PoolPartitionSystem;
|
||||
|
||||
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
|
||||
|
||||
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main);
|
||||
}
|
||||
|
||||
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
||||
{
|
||||
_portHandles.Add(serverPortHandle);
|
||||
_ports.Add(serverPortHandle, objectFactory);
|
||||
}
|
||||
|
||||
public void AddSessionObj(KServerSession serverSession, IpcService obj)
|
||||
{
|
||||
// Ensure that the sever loop is running.
|
||||
InitDone.WaitOne();
|
||||
|
||||
_selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle);
|
||||
AddSessionObj(serverSessionHandle, obj);
|
||||
}
|
||||
|
||||
public void AddSessionObj(int serverSessionHandle, IpcService obj)
|
||||
{
|
||||
_sessionHandles.Add(serverSessionHandle);
|
||||
_sessions.Add(serverSessionHandle, obj);
|
||||
}
|
||||
|
||||
private void Main()
|
||||
{
|
||||
ServerLoop();
|
||||
}
|
||||
|
||||
private void ServerLoop()
|
||||
{
|
||||
_selfProcess = KernelStatic.GetCurrentProcess();
|
||||
|
||||
if (SmObjectFactory != null)
|
||||
{
|
||||
_context.Syscall.ManageNamedPort(out int serverPortHandle, "sm:", 50);
|
||||
|
||||
AddPort(serverPortHandle, SmObjectFactory);
|
||||
}
|
||||
|
||||
InitDone.Set();
|
||||
|
||||
KThread thread = KernelStatic.GetCurrentThread();
|
||||
ulong messagePtr = thread.TlsAddress;
|
||||
_context.Syscall.SetHeapSize(out ulong heapAddr, 0x200000);
|
||||
|
||||
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
|
||||
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
||||
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
||||
|
||||
int replyTargetHandle = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int[] portHandles = _portHandles.ToArray();
|
||||
int[] sessionHandles = _sessionHandles.ToArray();
|
||||
int[] handles = new int[portHandles.Length + sessionHandles.Length];
|
||||
|
||||
portHandles.CopyTo(handles, 0);
|
||||
sessionHandles.CopyTo(handles, portHandles.Length);
|
||||
|
||||
// We still need a timeout here to allow the service to pick up and listen new sessions...
|
||||
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles, replyTargetHandle, 1000000L);
|
||||
|
||||
thread.HandlePostSyscall();
|
||||
|
||||
if (!thread.Context.Running)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
replyTargetHandle = 0;
|
||||
|
||||
if (rc == Result.Success && signaledIndex >= portHandles.Length)
|
||||
{
|
||||
// We got a IPC request, process it, pass to the appropriate service if needed.
|
||||
int signaledHandle = handles[signaledIndex];
|
||||
|
||||
if (Process(signaledHandle, heapAddr))
|
||||
{
|
||||
replyTargetHandle = signaledHandle;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rc == Result.Success)
|
||||
{
|
||||
// We got a new connection, accept the session to allow servicing future requests.
|
||||
if (_context.Syscall.AcceptSession(out int serverSessionHandle, handles[signaledIndex]) == Result.Success)
|
||||
{
|
||||
IpcService obj = _ports[handles[signaledIndex]].Invoke();
|
||||
|
||||
AddSessionObj(serverSessionHandle, obj);
|
||||
}
|
||||
}
|
||||
|
||||
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
|
||||
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
||||
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
||||
}
|
||||
}
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private bool Process(int serverSessionHandle, ulong recvListAddr)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
KThread thread = KernelStatic.GetCurrentThread();
|
||||
ulong messagePtr = thread.TlsAddress;
|
||||
ulong messageSize = 0x100;
|
||||
|
||||
byte[] reqData = new byte[messageSize];
|
||||
|
||||
process.CpuMemory.Read(messagePtr, reqData);
|
||||
|
||||
IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
|
||||
IpcMessage response = new IpcMessage();
|
||||
|
||||
ulong tempAddr = recvListAddr;
|
||||
int sizesOffset = request.RawData.Length - ((request.RecvListBuff.Count * 2 + 3) & ~3);
|
||||
|
||||
bool noReceive = true;
|
||||
|
||||
for (int i = 0; i < request.ReceiveBuff.Count; i++)
|
||||
{
|
||||
noReceive &= (request.ReceiveBuff[i].Position == 0);
|
||||
}
|
||||
|
||||
if (noReceive)
|
||||
{
|
||||
for (int i = 0; i < request.RecvListBuff.Count; i++)
|
||||
{
|
||||
ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan(sizesOffset + i * 2, 2));
|
||||
|
||||
response.PtrBuff.Add(new IpcPtrBuffDesc(tempAddr, (uint)i, size));
|
||||
|
||||
request.RecvListBuff[i] = new IpcRecvListBuffDesc(tempAddr, size);
|
||||
|
||||
tempAddr += size;
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldReply = true;
|
||||
bool isTipcCommunication = false;
|
||||
|
||||
using (MemoryStream raw = new MemoryStream(request.RawData))
|
||||
{
|
||||
BinaryReader reqReader = new BinaryReader(raw);
|
||||
|
||||
if (request.Type == IpcMessageType.HipcRequest ||
|
||||
request.Type == IpcMessageType.HipcRequestWithContext)
|
||||
{
|
||||
response.Type = IpcMessageType.HipcResponse;
|
||||
|
||||
using (MemoryStream resMs = new MemoryStream())
|
||||
{
|
||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
||||
|
||||
ServiceCtx context = new ServiceCtx(
|
||||
_context.Device,
|
||||
process,
|
||||
process.CpuMemory,
|
||||
thread,
|
||||
request,
|
||||
response,
|
||||
reqReader,
|
||||
resWriter);
|
||||
|
||||
_sessions[serverSessionHandle].CallHipcMethod(context);
|
||||
|
||||
response.RawData = resMs.ToArray();
|
||||
}
|
||||
}
|
||||
else if (request.Type == IpcMessageType.HipcControl ||
|
||||
request.Type == IpcMessageType.HipcControlWithContext)
|
||||
{
|
||||
uint magic = (uint)reqReader.ReadUInt64();
|
||||
uint cmdId = (uint)reqReader.ReadUInt64();
|
||||
|
||||
switch (cmdId)
|
||||
{
|
||||
case 0:
|
||||
request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
|
||||
break;
|
||||
|
||||
case 3:
|
||||
request = FillResponse(response, 0, PointerBufferSize);
|
||||
break;
|
||||
|
||||
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
||||
case 2:
|
||||
case 4:
|
||||
int unknown = reqReader.ReadInt32();
|
||||
|
||||
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
|
||||
|
||||
AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
|
||||
|
||||
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
|
||||
|
||||
request = FillResponse(response, 0);
|
||||
|
||||
break;
|
||||
|
||||
default: throw new NotImplementedException(cmdId.ToString());
|
||||
}
|
||||
}
|
||||
else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
|
||||
{
|
||||
_context.Syscall.CloseHandle(serverSessionHandle);
|
||||
_sessionHandles.Remove(serverSessionHandle);
|
||||
IpcService service = _sessions[serverSessionHandle];
|
||||
if (service is IDisposable disposableObj)
|
||||
{
|
||||
disposableObj.Dispose();
|
||||
}
|
||||
_sessions.Remove(serverSessionHandle);
|
||||
shouldReply = false;
|
||||
}
|
||||
// If the type is past 0xF, we are using TIPC
|
||||
else if (request.Type > IpcMessageType.TipcCloseSession)
|
||||
{
|
||||
isTipcCommunication = true;
|
||||
|
||||
// Response type is always the same as request on TIPC.
|
||||
response.Type = request.Type;
|
||||
|
||||
using (MemoryStream resMs = new MemoryStream())
|
||||
{
|
||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
||||
|
||||
ServiceCtx context = new ServiceCtx(
|
||||
_context.Device,
|
||||
process,
|
||||
process.CpuMemory,
|
||||
thread,
|
||||
request,
|
||||
response,
|
||||
reqReader,
|
||||
resWriter);
|
||||
|
||||
_sessions[serverSessionHandle].CallTipcMethod(context);
|
||||
|
||||
response.RawData = resMs.ToArray();
|
||||
}
|
||||
|
||||
process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(request.Type.ToString());
|
||||
}
|
||||
|
||||
if (!isTipcCommunication)
|
||||
{
|
||||
process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
|
||||
}
|
||||
|
||||
return shouldReply;
|
||||
}
|
||||
}
|
||||
|
||||
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
BinaryWriter writer = new BinaryWriter(ms);
|
||||
|
||||
foreach (int value in values)
|
||||
{
|
||||
writer.Write(value);
|
||||
}
|
||||
|
||||
return FillResponse(response, result, ms.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
|
||||
{
|
||||
response.Type = IpcMessageType.HipcResponse;
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
BinaryWriter writer = new BinaryWriter(ms);
|
||||
|
||||
writer.Write(IpcMagic.Sfco);
|
||||
writer.Write(result);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
writer.Write(data);
|
||||
}
|
||||
|
||||
response.RawData = ms.ToArray();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (IpcService service in _sessions.Values)
|
||||
{
|
||||
if (service is IDisposable disposableObj)
|
||||
{
|
||||
disposableObj.Dispose();
|
||||
}
|
||||
|
||||
service.DestroyAtExit();
|
||||
}
|
||||
|
||||
_sessions.Clear();
|
||||
|
||||
InitDone.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
public enum HideCursor
|
||||
{
|
||||
Never,
|
||||
OnIdle,
|
||||
Always
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Horizon.Generators.Kernel
|
||||
{
|
||||
class SyscallSyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
public List<MethodDeclarationSyntax> SvcImplementations { get; }
|
||||
|
||||
public SyscallSyntaxReceiver()
|
||||
{
|
||||
SvcImplementations = new List<MethodDeclarationSyntax>();
|
||||
}
|
||||
|
||||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
if (syntaxNode is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Count != 0)
|
||||
{
|
||||
foreach (var attributeList in classDeclaration.AttributeLists)
|
||||
{
|
||||
if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "SvcImpl"))
|
||||
{
|
||||
foreach (var memberDeclaration in classDeclaration.Members)
|
||||
{
|
||||
if (memberDeclaration is MethodDeclarationSyntax methodDeclaration)
|
||||
{
|
||||
VisitMethod(methodDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void VisitMethod(MethodDeclarationSyntax methodDeclaration)
|
||||
{
|
||||
if (methodDeclaration.AttributeLists.Count != 0)
|
||||
{
|
||||
foreach (var attributeList in methodDeclaration.AttributeLists)
|
||||
{
|
||||
if (attributeList.Attributes.Any(x => x.Name.GetText().ToString() == "Svc"))
|
||||
{
|
||||
SvcImplementations.Add(methodDeclaration);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
|
||||
namespace Ryujinx.Ui.App.Common
|
||||
{
|
||||
public class ApplicationData
|
||||
{
|
||||
public bool Favorite { get; set; }
|
||||
public byte[] Icon { get; set; }
|
||||
public string TitleName { get; set; }
|
||||
public string TitleId { get; set; }
|
||||
public string Developer { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string TimePlayed { get; set; }
|
||||
public double TimePlayedNum { get; set; }
|
||||
public string LastPlayed { get; set; }
|
||||
public string FileExtension { get; set; }
|
||||
public string FileSize { get; set; }
|
||||
public double FileSizeBytes { get; set; }
|
||||
public string Path { get; set; }
|
||||
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
namespace Ryujinx.Ui.App.Common
|
||||
{
|
||||
public class ApplicationMetadata
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public bool Favorite { get; set; }
|
||||
public double TimePlayed { get; set; }
|
||||
public string LastPlayed { get; set; } = "Never";
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
namespace Ryujinx.Ui.Common.Configuration
|
||||
{
|
||||
public enum AudioBackend
|
||||
{
|
||||
Dummy,
|
||||
OpenAl,
|
||||
SoundIo,
|
||||
SDL2
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
namespace Ryujinx.Ui.Common.Configuration.System
|
||||
{
|
||||
public enum Region
|
||||
{
|
||||
Japan,
|
||||
USA,
|
||||
Europe,
|
||||
Australia,
|
||||
China,
|
||||
Korea,
|
||||
Taiwan
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 59 KiB |
78
Ryujinx.sln
78
Ryujinx.sln
@ -3,33 +3,33 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32228.430
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "src\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "src\Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "src\Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "src\Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ARMeilleure", "ARMeilleure\ARMeilleure.csproj", "{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ARMeilleure", "src\ARMeilleure\ARMeilleure.csproj", "{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Gpu", "Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj", "{ADA7EA87-0D63-4D97-9433-922A2124401F}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Gpu", "src\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj", "{ADA7EA87-0D63-4D97-9433-922A2124401F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.GAL", "Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj", "{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.GAL", "src\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj", "{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.OpenGL", "Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj", "{9558FB96-075D-4219-8FFF-401979DC0B69}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.OpenGL", "src\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj", "{9558FB96-075D-4219-8FFF-401979DC0B69}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Texture", "Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj", "{E1B1AD28-289D-47B7-A106-326972240207}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Texture", "src\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj", "{E1B1AD28-289D-47B7-A106-326972240207}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "src\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj", "{85A0FA56-DC01-4A42-8808-70DAC76BD66D}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "src\Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj", "{85A0FA56-DC01-4A42-8808-70DAC76BD66D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "src\Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
@ -37,55 +37,55 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
Directory.Packages.props = Directory.Packages.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory.Tests", "Ryujinx.Memory.Tests\Ryujinx.Memory.Tests.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "src\Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Device", "Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj", "{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Device", "src\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj", "{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Host1x", "Ryujinx.Graphics.Host1x\Ryujinx.Graphics.Host1x.csproj", "{C35F1536-7DE5-4F9D-9604-B5B4E1561947}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Host1x", "src\Ryujinx.Graphics.Host1x\Ryujinx.Graphics.Host1x.csproj", "{C35F1536-7DE5-4F9D-9604-B5B4E1561947}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.Vp9", "Ryujinx.Graphics.Nvdec.Vp9\Ryujinx.Graphics.Nvdec.Vp9.csproj", "{B9AECA11-E248-4886-A10B-81B631CAAF29}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.Vp9", "src\Ryujinx.Graphics.Nvdec.Vp9\Ryujinx.Graphics.Nvdec.Vp9.csproj", "{B9AECA11-E248-4886-A10B-81B631CAAF29}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vic", "Ryujinx.Graphics.Vic\Ryujinx.Graphics.Vic.csproj", "{81BB2C11-9408-4EA3-822E-42987AF54429}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vic", "src\Ryujinx.Graphics.Vic\Ryujinx.Graphics.Vic.csproj", "{81BB2C11-9408-4EA3-822E-42987AF54429}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "src\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "src\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "src\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "src\Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL2", "Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL2", "src\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj", "{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj", "{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "src\Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "src\Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.Common", "Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.Common", "src\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "src\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "src\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "Ryujinx.Horizon\Ryujinx.Horizon.csproj", "{AF34127A-3A92-43E5-8496-14960A50B1F1}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryujinx.Horizon\Ryujinx.Horizon.csproj", "{AF34127A-3A92-43E5-8496-14960A50B1F1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
SCRIPT_DIR=$(dirname $(realpath $0))
|
||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||
RYUJINX_BIN="Ryujinx"
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then
|
||||
|
@ -6,31 +6,31 @@ PUBLISH_DIRECTORY=$1
|
||||
OUTPUT_DIRECTORY=$2
|
||||
ENTITLEMENTS_FILE_PATH=$3
|
||||
|
||||
APP_BUNDLE_DIRECTORY=$OUTPUT_DIRECTORY/Ryujinx.app
|
||||
APP_BUNDLE_DIRECTORY="$OUTPUT_DIRECTORY/Ryujinx.app"
|
||||
|
||||
rm -rf $APP_BUNDLE_DIRECTORY
|
||||
mkdir -p $APP_BUNDLE_DIRECTORY/Contents
|
||||
mkdir $APP_BUNDLE_DIRECTORY/Contents/Frameworks
|
||||
mkdir $APP_BUNDLE_DIRECTORY/Contents/MacOS
|
||||
mkdir $APP_BUNDLE_DIRECTORY/Contents/Resources
|
||||
rm -rf "$APP_BUNDLE_DIRECTORY"
|
||||
mkdir -p "$APP_BUNDLE_DIRECTORY/Contents"
|
||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
|
||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS"
|
||||
mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
||||
|
||||
# Copy executables first
|
||||
cp $PUBLISH_DIRECTORY/Ryujinx.Ava $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx
|
||||
chmod u+x $APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx
|
||||
cp "$PUBLISH_DIRECTORY/Ryujinx.Ava" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||
chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx"
|
||||
|
||||
# Then all libraries
|
||||
cp $PUBLISH_DIRECTORY/*.dylib $APP_BUNDLE_DIRECTORY/Contents/Frameworks
|
||||
cp "$PUBLISH_DIRECTORY"/*.dylib "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
|
||||
|
||||
# Then resources
|
||||
cp Info.plist $APP_BUNDLE_DIRECTORY/Contents
|
||||
cp Ryujinx.icns $APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns
|
||||
cp updater.sh $APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh
|
||||
cp -r $PUBLISH_DIRECTORY/THIRDPARTY.md $APP_BUNDLE_DIRECTORY/Contents/Resources
|
||||
cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents"
|
||||
cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns"
|
||||
cp updater.sh "$APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh"
|
||||
cp -r "$PUBLISH_DIRECTORY/THIRDPARTY.md" "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
||||
|
||||
echo -n "APPL????" > $APP_BUNDLE_DIRECTORY/Contents/PkgInfo
|
||||
echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo"
|
||||
|
||||
# Fixup libraries and executable
|
||||
python3 bundle_fix_up.py $APP_BUNDLE_DIRECTORY MacOS/Ryujinx
|
||||
python3 bundle_fix_up.py "$APP_BUNDLE_DIRECTORY" MacOS/Ryujinx
|
||||
|
||||
# Now sign it
|
||||
if ! [ -x "$(command -v codesign)" ];
|
||||
@ -41,12 +41,10 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes.
|
||||
# cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
|
||||
# cargo install apple-codesign
|
||||
echo "Usign rcodesign for ad-hoc signing"
|
||||
rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $APP_BUNDLE_DIRECTORY
|
||||
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY"
|
||||
else
|
||||
echo "Usign codesign for ad-hoc signing"
|
||||
codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $APP_BUNDLE_DIRECTORY
|
||||
fi
|
||||
|
||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$APP_BUNDLE_DIRECTORY"
|
||||
fi
|
113
distribution/macos/create_macos_build.sh
Executable file
113
distribution/macos/create_macos_build.sh
Executable file
@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$#" -lt 7 ]; then
|
||||
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <EXTRA_ARGS>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$1"
|
||||
mkdir -p "$2"
|
||||
mkdir -p "$3"
|
||||
|
||||
BASE_DIR=$(readlink -f "$1")
|
||||
TEMP_DIRECTORY=$(readlink -f "$2")
|
||||
OUTPUT_DIRECTORY=$(readlink -f "$3")
|
||||
ENTITLEMENTS_FILE_PATH=$(readlink -f "$4")
|
||||
VERSION=$5
|
||||
SOURCE_REVISION_ID=$6
|
||||
CONFIGURATION=$7
|
||||
EXTRA_ARGS=$8
|
||||
|
||||
if [ "$VERSION" == "1.1.0" ];
|
||||
then
|
||||
RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
||||
else
|
||||
RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$VERSION-macos_universal.app.tar
|
||||
fi
|
||||
|
||||
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
|
||||
X64_APP_BUNDLE="$TEMP_DIRECTORY/output_x64/Ryujinx.app"
|
||||
UNIVERSAL_APP_BUNDLE="$OUTPUT_DIRECTORY/Ryujinx.app"
|
||||
EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx
|
||||
|
||||
rm -rf "$TEMP_DIRECTORY"
|
||||
mkdir -p "$TEMP_DIRECTORY"
|
||||
|
||||
DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true $EXTRA_ARGS"
|
||||
|
||||
dotnet restore
|
||||
dotnet build -c $CONFIGURATION src/Ryujinx.Ava
|
||||
dotnet publish -c $CONFIGURATION -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava
|
||||
dotnet publish -c $CONFIGURATION -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava
|
||||
|
||||
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
||||
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
||||
|
||||
# Get rid of libsoundio from arm64 builds as we don't have a arm64 variant
|
||||
# TODO: remove this once done
|
||||
rm -rf "$TEMP_DIRECTORY/publish_arm64/libsoundio.dylib"
|
||||
|
||||
pushd "$BASE_DIR/distribution/macos"
|
||||
./create_app_bundle.sh "$TEMP_DIRECTORY/publish_x64" "$TEMP_DIRECTORY/output_x64" "$ENTITLEMENTS_FILE_PATH"
|
||||
./create_app_bundle.sh "$TEMP_DIRECTORY/publish_arm64" "$TEMP_DIRECTORY/output_arm64" "$ENTITLEMENTS_FILE_PATH"
|
||||
popd
|
||||
|
||||
rm -rf "$UNIVERSAL_APP_BUNDLE"
|
||||
mkdir -p "$OUTPUT_DIRECTORY"
|
||||
|
||||
# Let's copy one of the two different app bundle and remove the executable
|
||||
cp -R "$ARM64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE"
|
||||
rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH"
|
||||
|
||||
# Make it libraries universal
|
||||
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_BUNDLE" "$X64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" "**/*.dylib"
|
||||
|
||||
if ! [ -x "$(command -v lipo)" ];
|
||||
then
|
||||
if ! [ -x "$(command -v llvm-lipo-14)" ];
|
||||
then
|
||||
LIPO=llvm-lipo
|
||||
else
|
||||
LIPO=llvm-lipo-14
|
||||
fi
|
||||
else
|
||||
LIPO=lipo
|
||||
fi
|
||||
|
||||
# Make the executable universal
|
||||
$LIPO "$ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" "$X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -create
|
||||
|
||||
# Patch up the Info.plist to have appropriate version
|
||||
sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist"
|
||||
sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist"
|
||||
rm "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck"
|
||||
|
||||
# Now sign it
|
||||
if ! [ -x "$(command -v codesign)" ];
|
||||
then
|
||||
if ! [ -x "$(command -v rcodesign)" ];
|
||||
then
|
||||
echo "Cannot find rcodesign on your system, please install rcodesign."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes.
|
||||
# cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
|
||||
echo "Using rcodesign for ad-hoc signing"
|
||||
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE"
|
||||
else
|
||||
echo "Using codesign for ad-hoc signing"
|
||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$UNIVERSAL_APP_BUNDLE"
|
||||
fi
|
||||
|
||||
echo "Creating archive"
|
||||
pushd "$OUTPUT_DIRECTORY"
|
||||
tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf $RELEASE_TAR_FILE_NAME Ryujinx.app 1> /dev/null
|
||||
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" $RELEASE_TAR_FILE_NAME "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx"
|
||||
gzip -9 < $RELEASE_TAR_FILE_NAME > $RELEASE_TAR_FILE_NAME.gz
|
||||
rm $RELEASE_TAR_FILE_NAME
|
||||
popd
|
||||
|
||||
echo "Done"
|
@ -1,105 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$#" -ne 6 ]; then
|
||||
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p $1
|
||||
mkdir -p $2
|
||||
mkdir -p $3
|
||||
|
||||
BASE_DIR=$(readlink -f $1)
|
||||
TEMP_DIRECTORY=$(readlink -f $2)
|
||||
OUTPUT_DIRECTORY=$(readlink -f $3)
|
||||
ENTITLEMENTS_FILE_PATH=$(readlink -f $4)
|
||||
VERSION=$5
|
||||
SOURCE_REVISION_ID=$6
|
||||
|
||||
RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar
|
||||
ARM64_APP_BUNDLE=$TEMP_DIRECTORY/output_arm64/Ryujinx.app
|
||||
X64_APP_BUNDLE=$TEMP_DIRECTORY/output_x64/Ryujinx.app
|
||||
UNIVERSAL_APP_BUNDLE=$OUTPUT_DIRECTORY/Ryujinx.app
|
||||
EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx
|
||||
|
||||
rm -rf $TEMP_DIRECTORY
|
||||
mkdir -p $TEMP_DIRECTORY
|
||||
|
||||
DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true"
|
||||
|
||||
dotnet restore
|
||||
dotnet build -c Release Ryujinx.Ava
|
||||
dotnet publish -c Release -r osx-arm64 -o $TEMP_DIRECTORY/publish_arm64 $DOTNET_COMMON_ARGS Ryujinx.Ava
|
||||
dotnet publish -c Release -r osx-x64 -o $TEMP_DIRECTORY/publish_x64 $DOTNET_COMMON_ARGS Ryujinx.Ava
|
||||
|
||||
# Get ride of the support library for ARMeilleur for x64 (that's only for arm64)
|
||||
rm -rf $TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib
|
||||
|
||||
# Get ride of libsoundio from arm64 builds as we don't have a arm64 variant
|
||||
# TODO: remove this once done
|
||||
rm -rf $TEMP_DIRECTORY/publish_arm64/libsoundio.dylib
|
||||
|
||||
pushd $BASE_DIR/distribution/macos
|
||||
./create_app_bundle.sh $TEMP_DIRECTORY/publish_x64 $TEMP_DIRECTORY/output_x64 $ENTITLEMENTS_FILE_PATH
|
||||
./create_app_bundle.sh $TEMP_DIRECTORY/publish_arm64 $TEMP_DIRECTORY/output_arm64 $ENTITLEMENTS_FILE_PATH
|
||||
popd
|
||||
|
||||
rm -rf $UNIVERSAL_APP_BUNDLE
|
||||
mkdir -p $OUTPUT_DIRECTORY
|
||||
|
||||
# Let's copy one of the two different app bundle and remove the executable
|
||||
cp -R $ARM64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE
|
||||
rm $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH
|
||||
|
||||
# Make it libraries universal
|
||||
python3 $BASE_DIR/distribution/macos/construct_universal_dylib.py $ARM64_APP_BUNDLE $X64_APP_BUNDLE $UNIVERSAL_APP_BUNDLE "**/*.dylib"
|
||||
|
||||
if ! [ -x "$(command -v lipo)" ];
|
||||
then
|
||||
if ! [ -x "$(command -v llvm-lipo-14)" ];
|
||||
then
|
||||
LIPO=llvm-lipo
|
||||
else
|
||||
LIPO=llvm-lipo-14
|
||||
fi
|
||||
else
|
||||
LIPO=lipo
|
||||
fi
|
||||
|
||||
# Make it the executable universal
|
||||
$LIPO $ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH $X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH -output $UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH -create
|
||||
|
||||
# Patch up the Info.plist to have appropriate version
|
||||
sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist
|
||||
sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" $UNIVERSAL_APP_BUNDLE/Contents/Info.plist
|
||||
rm $UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck
|
||||
|
||||
# Now sign it
|
||||
if ! [ -x "$(command -v codesign)" ];
|
||||
then
|
||||
if ! [ -x "$(command -v rcodesign)" ];
|
||||
then
|
||||
echo "Cannot find rcodesign on your system, please install rcodesign."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes.
|
||||
# cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
|
||||
echo "Usign rcodesign for ad-hoc signing"
|
||||
rcodesign sign --entitlements-xml-path $ENTITLEMENTS_FILE_PATH $UNIVERSAL_APP_BUNDLE
|
||||
else
|
||||
echo "Usign codesign for ad-hoc signing"
|
||||
codesign --entitlements $ENTITLEMENTS_FILE_PATH -f --deep -s - $UNIVERSAL_APP_BUNDLE
|
||||
fi
|
||||
|
||||
echo "Creating archive"
|
||||
pushd $OUTPUT_DIRECTORY
|
||||
tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf $RELEASE_TAR_FILE_NAME Ryujinx.app 1> /dev/null
|
||||
python3 $BASE_DIR/distribution/misc/add_tar_exec.py $RELEASE_TAR_FILE_NAME "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx"
|
||||
gzip -9 < $RELEASE_TAR_FILE_NAME > $RELEASE_TAR_FILE_NAME.gz
|
||||
rm $RELEASE_TAR_FILE_NAME
|
||||
popd
|
||||
|
||||
echo "Done"
|
@ -25,15 +25,20 @@ error_handler() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap 'error_handler ${LINENO}' ERR
|
||||
|
||||
# Wait for Ryujinx to exit
|
||||
# NOTE: in case no fds are open, lsof could be returning with a process still living.
|
||||
# We wait 1s and assume the process stopped after that
|
||||
lsof -p $APP_PID +r 1 &>/dev/null
|
||||
sleep 1
|
||||
|
||||
trap 'error_handler ${LINENO}' ERR
|
||||
|
||||
# Now replace and reopen.
|
||||
rm -rf "$INSTALL_DIRECTORY"
|
||||
mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY"
|
||||
open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS"
|
||||
|
||||
if [ "$#" -le 3 ]; then
|
||||
open -a "$INSTALL_DIRECTORY"
|
||||
else
|
||||
open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS"
|
||||
fi
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "7.0.100",
|
||||
"version": "7.0.200",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
@ -1,6 +1,7 @@
|
||||
using ARMeilleure.CodeGen.Linking;
|
||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -59,7 +60,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
|
||||
{
|
||||
_stream = new MemoryStream();
|
||||
_stream = MemoryStreamManager.Shared.GetStream();
|
||||
|
||||
AllocResult = allocResult;
|
||||
|
||||
@ -265,7 +266,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
}
|
||||
else
|
||||
{
|
||||
relocInfo = new RelocInfo(new RelocEntry[0]);
|
||||
relocInfo = new RelocInfo(Array.Empty<RelocEntry>());
|
||||
}
|
||||
|
||||
return (code, relocInfo);
|
@ -226,6 +226,8 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem));
|
||||
Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd));
|
||||
Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi));
|
||||
Add(Intrinsic.Arm64MrsFpcr, new IntrinsicInfo(0xd53b4400u, IntrinsicType.GetRegister));
|
||||
Add(Intrinsic.Arm64MsrFpcr, new IntrinsicInfo(0xd51b4400u, IntrinsicType.SetRegister));
|
||||
Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister));
|
||||
Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister));
|
||||
Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem));
|
@ -9,7 +9,7 @@ using static ARMeilleure.IntermediateRepresentation.Operation.Factory;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Arm64
|
||||
{
|
||||
class PreAllocator
|
||||
static class PreAllocator
|
||||
{
|
||||
private class ConstantDict
|
||||
{
|
||||
@ -54,8 +54,8 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
continue;
|
||||
}
|
||||
|
||||
HandleConstantRegCopy(constants, block.Operations, node);
|
||||
HandleDestructiveRegCopy(block.Operations, node);
|
||||
InsertConstantRegCopies(constants, block.Operations, node);
|
||||
InsertDestructiveRegCopies(block.Operations, node);
|
||||
|
||||
switch (node.Instruction)
|
||||
{
|
||||
@ -78,28 +78,28 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
// Copy values to registers expected by the function
|
||||
// being called, as mandated by the ABI.
|
||||
HandleCall(constants, block.Operations, node);
|
||||
InsertCallCopies(constants, block.Operations, node);
|
||||
break;
|
||||
case Instruction.CompareAndSwap:
|
||||
case Instruction.CompareAndSwap16:
|
||||
case Instruction.CompareAndSwap8:
|
||||
nextNode = HandleCompareAndSwap(block.Operations, node);
|
||||
nextNode = GenerateCompareAndSwap(block.Operations, node);
|
||||
break;
|
||||
case Instruction.LoadArgument:
|
||||
nextNode = HandleLoadArgument(cctx, ref buffer, block.Operations, preservedArgs, node);
|
||||
nextNode = InsertLoadArgumentCopy(cctx, ref buffer, block.Operations, preservedArgs, node);
|
||||
break;
|
||||
case Instruction.Return:
|
||||
HandleReturn(block.Operations, node);
|
||||
InsertReturnCopy(block.Operations, node);
|
||||
break;
|
||||
case Instruction.Tailcall:
|
||||
HandleTailcall(constants, block.Operations, stackAlloc, node, node);
|
||||
InsertTailcallCopies(constants, block.Operations, stackAlloc, node, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleConstantRegCopy(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node)
|
||||
private static void InsertConstantRegCopies(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
if (node.SourcesCount == 0 || IsIntrinsicWithConst(node))
|
||||
{
|
||||
@ -211,7 +211,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleDestructiveRegCopy(IntrusiveList<Operation> nodes, Operation node)
|
||||
private static void InsertDestructiveRegCopies(IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
if (node.Destination == default || node.SourcesCount == 0)
|
||||
{
|
||||
@ -259,7 +259,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleCall(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node)
|
||||
private static void InsertCallCopies(ConstantDict constants, IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
Operation operation = node;
|
||||
|
||||
@ -319,7 +319,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
Operation copyOp = Operation(Instruction.Copy, argReg, source);
|
||||
|
||||
HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, copyOp));
|
||||
InsertConstantRegCopies(constants, nodes, nodes.AddBefore(node, copyOp));
|
||||
|
||||
sources.Add(argReg);
|
||||
}
|
||||
@ -329,7 +329,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
Operation spillOp = Operation(Instruction.SpillArg, default, offset, source);
|
||||
|
||||
HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, spillOp));
|
||||
InsertConstantRegCopies(constants, nodes, nodes.AddBefore(node, spillOp));
|
||||
|
||||
stackOffset += source.Type.GetSizeInBytes();
|
||||
}
|
||||
@ -364,7 +364,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
operation.SetSources(sources.ToArray());
|
||||
}
|
||||
|
||||
private static void HandleTailcall(
|
||||
private static void InsertTailcallCopies(
|
||||
ConstantDict constants,
|
||||
IntrusiveList<Operation> nodes,
|
||||
StackAllocator stackAlloc,
|
||||
@ -420,7 +420,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
|
||||
Operation copyOp = Operation(Instruction.Copy, argReg, source);
|
||||
|
||||
HandleConstantRegCopy(constants, nodes, nodes.AddBefore(node, copyOp));
|
||||
InsertConstantRegCopies(constants, nodes, nodes.AddBefore(node, copyOp));
|
||||
|
||||
sources.Add(argReg);
|
||||
}
|
||||
@ -444,7 +444,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
operation.SetSources(sources.ToArray());
|
||||
}
|
||||
|
||||
private static Operation HandleCompareAndSwap(IntrusiveList<Operation> nodes, Operation node)
|
||||
private static Operation GenerateCompareAndSwap(IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
Operand expected = node.GetSource(1);
|
||||
|
||||
@ -508,7 +508,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
return node.ListNext;
|
||||
}
|
||||
|
||||
private static void HandleReturn(IntrusiveList<Operation> nodes, Operation node)
|
||||
private static void InsertReturnCopy(IntrusiveList<Operation> nodes, Operation node)
|
||||
{
|
||||
if (node.SourcesCount == 0)
|
||||
{
|
||||
@ -537,7 +537,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
}
|
||||
}
|
||||
|
||||
private static Operation HandleLoadArgument(
|
||||
private static Operation InsertLoadArgumentCopy(
|
||||
CompilerContext cctx,
|
||||
ref Span<Operation> buffer,
|
||||
IntrusiveList<Operation> nodes,
|
||||
@ -629,7 +629,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
if (dest.AssignmentsCount == 1)
|
||||
{
|
||||
// Let's propagate the argument if we can to avoid copies.
|
||||
Propagate(ref buffer, dest, preservedArgs[index]);
|
||||
PreAllocatorCommon.Propagate(ref buffer, dest, preservedArgs[index]);
|
||||
nextNode = node.ListNext;
|
||||
}
|
||||
else
|
||||
@ -648,54 +648,6 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
}
|
||||
}
|
||||
|
||||
private static void Propagate(ref Span<Operation> buffer, Operand dest, Operand value)
|
||||
{
|
||||
ReadOnlySpan<Operation> uses = dest.GetUses(ref buffer);
|
||||
|
||||
foreach (Operation use in uses)
|
||||
{
|
||||
for (int srcIndex = 0; srcIndex < use.SourcesCount; srcIndex++)
|
||||
{
|
||||
Operand useSrc = use.GetSource(srcIndex);
|
||||
|
||||
if (useSrc == dest)
|
||||
{
|
||||
use.SetSource(srcIndex, value);
|
||||
}
|
||||
else if (useSrc.Kind == OperandKind.Memory)
|
||||
{
|
||||
MemoryOperand memoryOp = useSrc.GetMemory();
|
||||
|
||||
Operand baseAddr = memoryOp.BaseAddress;
|
||||
Operand index = memoryOp.Index;
|
||||
bool changed = false;
|
||||
|
||||
if (baseAddr == dest)
|
||||
{
|
||||
baseAddr = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (index == dest)
|
||||
{
|
||||
index = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
use.SetSource(srcIndex, MemoryOp(
|
||||
useSrc.Type,
|
||||
baseAddr,
|
||||
index,
|
||||
memoryOp.Scale,
|
||||
memoryOp.Displacement));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand AddFloatConstantCopy(
|
||||
ConstantDict constants,
|
||||
IntrusiveList<Operation> nodes,
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user