Compare commits

...

26 Commits

Author SHA1 Message Date
OpaqueReptile
cb4b58052f Start GPU performance counter at 0 instead of host GPU value (#4992)
* Start performance counter at 0 instead of host perf counter value

* whitespace

* init _firstTimestamp in constructer per feedback

* change comment

* punctuation

* address feedback

* revise comment
2023-05-17 15:38:59 -03:00
Mary
f8cdd5f484 macos: Fix relaunch with updater when no arguments were provided to the emulator (#4987)
The updater still seems to be erroring when replacing installed folder under normal operations.
2023-05-17 19:02:15 +02:00
TSRBerry
22202be394 [GUI] Fix always hide cursor mode not hiding the cursor until it was moved (#4927)
* gtk: Add missing isMouseInClient check for hide-cursor

* ava: Add missing events and default isCursorInRenderer to true

This is necessary because we don't receive a initial PointerEnter event for some reason.
2023-05-14 16:34:31 +02:00
riperiperi
17ba217940 Vulkan: Device map buffers written more than flushed (#4911) 2023-05-13 15:15:05 +02:00
TSRBerry
aae4595bdb Add timeout of 35 minutes to workflow jobs (#4928) 2023-05-13 13:24:43 +02:00
Mary
880fd3cfcb audio: sdl2: Do not report 5.1 if the device doesn't support it (#4908)
* amadeus: adjust VirtualDevice channel configuration reporting with HardwareDevice

* audio: sdl2: Do not report 5.1 if device doesn't support it

SDL2 5.1 to Stereo conversion is terrible and make everything sound
quiet.

Let's not expose 5.1 if not truly supported by the device.
2023-05-13 07:15:16 +00:00
gdkchan
f679f25e08 Set OpenGL PixelPackBuffer to 0 when done (#4921) 2023-05-13 00:59:46 +00:00
Isaac Marovitz
c2709b3bdd macOS CI Adjustments (#4910)
* Fix macOS build name in CI

Fixes updater

* Update build.yml

Don't publish x86 Mac builds

* Naming nitpick

* Berry changes

* Use the same prefix for PR and release build archives

---------

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2023-05-13 00:53:52 +02:00
Ac_K
2b6e81deea Ava: Fix wrong MouseButton (#4900) 2023-05-12 21:39:42 +02:00
Mary
7271f1b18e Bump shader cache codegen version
That was missing from #4892
2023-05-12 18:53:14 +02:00
riperiperi
5fda543f84 Vulkan: Partially workaround MoltenVK InvalidResource error (#4880)
* Add MVK stage flags workaround

* Actually do the workaround

* Remove GS on VS stuff

* Address feedback
2023-05-11 22:06:15 -03:00
riperiperi
95c06de4c1 GPU: Remove swizzle undefined matching and rework depth aliasing (#4896)
* GPU: Remove swizzle undefined matching and rework depth aliasing

@gdkchan pointed out that UI textures in TOTK seemed to be setting their texture swizzle incorrectly (texture was RGB but was sampling A, swizzle for A was wrong), so I determined that SwizzleComponentMatches was the problem and set on eliminating it. This PR combines existing work to select the most recently modified texture (now used when selecting which aliased texture to use) with some additional changes to remove the swizzle check and support aliased view creation.

The original observation (#1538) was that we wanted to match depth textures for the purposes of aliasing with color textures, but they often had different swizzle from what was sampled (as it's generally the identity swizzle once rendered). At the time, I decided to allow swizzles to match if only the defined components matched, which fixed the issue in all known cases but could easily be broken by a game _expecting_ a given swizzle, such as a 1/0 value on a component.

This error case could also occur in textures that don't even depth alias, such as R11G11B10, as the rule was created to generally apply to all cases.

The solution is now to fail this exact match test, and allow the search for an R32 texture to create a swizzled view of a D32 texture (and other such cases). This allows the creation of a view that mismatches the requested format, which wasn't present before and was the reason for the swizzle matching approach.

The exact match and view creation rules now follow the same rules over what textures to select when there are multiple options (such as a "perfect" match and an "aliased" match at the same time). It now selects the most recently modified texture, which is done with a new sequence number in the GpuContext (because we don't have enough of these).

Reportedly fixes UI having weird coloured backgrounds in TOTK. This also fixes an issue in MK8D where returning from a race resulted in the character selection cubemaps being broken. May work around issues introduced by the "short texture cache" PR due to modification ordering, though they won't be truly fixed.

Should allow (#4365) to avoid copies in more cases. Need to test that.

I tested a bunch of games #1538 originally affected and they seem to be fine. This change affects all games so it would be good to get some wide testing on it.

* Address feedback 1, fix an issue

* Workaround: Do not allow copies for format alias.

These should be removed when D32<->R32 copy dependencies become legal
2023-05-11 21:30:47 -03:00
John
49c63ea077 Fix the restart after an update. (#4869)
* Fix the restart after an update.

* Fix the updater for the Ava UI too.

* Fixing up the code after some change requests.
Removed a line of code that was accidentally left in.

* Fix restarting on Linux Avalonia.

* Fix issues with escaped arguments.
2023-05-12 02:14:29 +02:00
SamusAranX
531da8a1c0 Changed LastPlayed field from string to nullable DateTime (#4861)
* Changed LastPlayed field from string to nullable DateTime

Added ApplicationData.LastPlayedString property
Added NullableDateTimeConverter for the DateTime->string conversion in Avalonia

* Added migration from string-based last_played to DateTime-based last_played_utc

* Updated comment style

* Added MarkupExtension to NullableDateTimeConverter and changed its usage

Cleaned up leftover usings

* Missed one comment
2023-05-12 01:56:37 +02:00
Mary
5cbdfbc7a4 amadeus: Allow 5.1 sink output (#4894)
* amadeus: Allow 5.1 sink output

Also add a simple Stereo to 5.1 change for device sink.

Tested against NES - Nintendo Switch Online that output stereo on the
audio renderer.

* Remove outdated comment
2023-05-12 00:19:19 +02:00
Nico
e0544dd9c7 UI: Adjust input mapping view (#4866)
* refactor: clean up controller settings ui

- Remove inconsistencies between left and right side
- Use style to set ToggleButton properties (since they are all the same)
- Move topmost controller settings from one line to 2x2 grid for improved clarity
- Properly adjust borders, text widths, etc. to neighboring elements to eliminate misaligned visual lines

* fix: merge issues

* fix: prevent sliders from jumping by giving text block fixed width

* refactor: add more separators and increase margin

* refactor: center deadzone and range descriptions

* refactor: move rumble border top margin to -1 and prevent double border

* refactor: remove margins & double borders + switch profile & input selection

* style: apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-05-11 21:10:57 +00:00
dependabot[bot]
aa784c3e5e nuget: bump System.IdentityModel.Tokens.Jwt from 6.30.0 to 6.30.1 (#4886)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.30.0 to 6.30.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.30.0...6.30.1)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-11 21:42:46 +02:00
gdkchan
9205077590 Enable explicit LOD for array textures with depth compare on SPIR-V (#4892) 2023-05-11 21:35:36 +02:00
gdkchan
0ed40c7175 Fix incorrect ASTC endpoint color when using LuminanceDelta mode (#4890) 2023-05-11 20:47:55 +02:00
Mary
40d47b7aa2 amadeus: Fix wrong channel mapping check and an old typo (#4888)
* amadeus: Fix wrong channel mapping check

This was always going to happens, as a result quadratic would break and
move index after the channel count point, effectively breaking
input/output indices.

* amadeus: Fix reverb 3d early delay wrong output index
2023-05-11 20:14:02 +02:00
John
ec0bb74968 Stop SDL from inhibiting sleep. (#4842) 2023-05-11 20:13:01 +02:00
2435043xia
42f7f98666 Fix the issue of unequal check for amiibo file date due to the lack o… (#4832)
* Fix the issue of unequal check for amiibo file date due to the lack of sub-second units in the header, causing slow opening of the amiibo interface.

* Supplement the unrepaired.
2023-05-11 19:10:24 +02:00
riperiperi
95bad6995c GPU: Fix shader cache assuming past shader data was mapped (#4885)
This fixes a potential issue where a shader lookup could match the address of a previous _different_ shader, but that shader is now partially unmapped. This would just crash with an invalid region exception.

To compare a shader in the address cache with one in memory, we get the memory at the location with the previous shader's size. However, it's possible it has been unmapped and then remapped with a smaller size. In this case, we should just get back the mapped portion of the shader, which will then fail the comparison immediately and get to compile/lookup for the new one.

This might fix a random crash in TOTK that was reported by Piplup. I don't know if it does, because I don't have the game yet.
2023-05-11 18:41:34 +02:00
Mary
3d42995822 Attempt to fix release.yml after merge 2023-05-11 17:42:33 +02:00
TSRBerry
9095941fd1 Update release workflow & Add jobs for macOS (#4837)
* Add build config and extra args to create_macos_build.sh

* Use matrix strategy for releases

* Add macOS jobs

Co-authored-by: Mary <thog@protonmail.com>

* Fix wrong version argument

* Fix check for the correct amount of args

* Install latest rcodesign release

Co-authored-by: Mary <thog@protonmail.com>

* Set executable bits for PR builds on linux

---------

Co-authored-by: Mary <thog@protonmail.com>
2023-05-11 17:36:53 +02:00
gdkchan
ba71141bdc Ensure background translation threads exited before disposing JIT (#4874) 2023-05-10 21:46:38 -03:00
50 changed files with 1075 additions and 717 deletions

View File

@@ -18,10 +18,16 @@ on:
- '*.yml' - '*.yml'
- 'README.md' - 'README.md'
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.1.0"
jobs: jobs:
build: build:
name: ${{ matrix.os }} (${{ matrix.configuration }}) name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 35
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macOS-latest, windows-latest] os: [ubuntu-latest, macOS-latest, windows-latest]
@@ -33,7 +39,7 @@ jobs:
RELEASE_ZIP_OS_NAME: linux_x64 RELEASE_ZIP_OS_NAME: linux_x64
- os: macOS-latest - os: macOS-latest
OS_NAME: MacOS x64 OS_NAME: macOS x64
DOTNET_RUNTIME_IDENTIFIER: osx-x64 DOTNET_RUNTIME_IDENTIFIER: osx-x64
RELEASE_ZIP_OS_NAME: osx_x64 RELEASE_ZIP_OS_NAME: osx_x64
@@ -43,47 +49,107 @@ jobs:
RELEASE_ZIP_OS_NAME: win_x64 RELEASE_ZIP_OS_NAME: win_x64
fail-fast: false fail-fast: false
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.1.0"
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3 - uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 7.0.x global-json-file: global.json
- name: Get git short hash - name: Get git short hash
id: git_short_hash id: git_short_hash
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Build - name: Build
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
- name: Test - name: Test
run: dotnet test --no-build -c "${{ matrix.configuration }}" run: dotnet test --no-build -c "${{ matrix.configuration }}"
- name: Publish Ryujinx - name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
- name: Publish Ryujinx.Headless.SDL2 - name: Publish Ryujinx.Headless.SDL2
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
- name: Publish Ryujinx.Ava - name: Publish Ryujinx.Ava
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
- name: Set executable bit
run: |
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh
chmod +x ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx.sh
if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest'
- name: Upload Ryujinx artifact - name: Upload Ryujinx artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish path: publish
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
- name: Upload Ryujinx.Headless.SDL2 artifact - name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish_sdl2_headless path: publish_sdl2_headless
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
- name: Upload Ryujinx.Ava artifact - name: Upload Ryujinx.Ava artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish_ava path: publish_ava
if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest'
build_macos:
name: macOS Universal (${{ matrix.configuration }})
runs-on: ubuntu-latest
timeout-minutes: 35
strategy:
matrix:
configuration: [ Debug, Release ]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
global-json-file: global.json
- name: Setup LLVM 14
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 14
- name: Install rcodesign
run: |
mkdir -p $HOME/.bin
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
rm apple-codesign.tar.gz
mv rcodesign $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get git short hash
id: git_short_hash
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
- name: Publish macOS
run: |
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
- name: Upload Ryujinx.Ava artifact
uses: actions/upload-artifact@v3
with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_ava/*.tar.gz"
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'

View File

@@ -12,6 +12,7 @@ concurrency: flatpak-release
jobs: jobs:
release: release:
timeout-minutes: 35
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:

View File

@@ -7,6 +7,7 @@ jobs:
pr_comment: pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 35
steps: steps:
- uses: actions/github-script@v6 - uses: actions/github-script@v6
with: with:

View File

@@ -22,95 +22,15 @@ env:
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master" RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
jobs: jobs:
release: tag:
runs-on: windows-latest name: Create tag
runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
global-json-file: global.json
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- 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: Create output dir
run: "mkdir release_output"
- name: Publish Windows
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 src/Ryujinx --self-contained true
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Ava --self-contained true
- name: Packing Windows builds
run: |
pushd publish_windows
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
popd
pushd publish_windows_sdl2_headless
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
popd
pushd publish_windows_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 src/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 src/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 src/Ryujinx.Ava --self-contained true
- name: Packing Linux builds
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
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
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
popd
shell: bash
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
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 information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
allowUpdates: true
removeArtifacts: true
replacesArtifacts: true
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
token: ${{ secrets.RELEASE_TOKEN }}
- name: Create tag - name: Create tag
uses: actions/github-script@v5 uses: actions/github-script@v5
@@ -123,6 +43,165 @@ jobs:
sha: context.sha sha: context.sha
}) })
release:
name: Release ${{ matrix.OS_NAME }}
runs-on: ${{ matrix.os }}
timeout-minutes: 35
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
include:
- os: ubuntu-latest
OS_NAME: Linux x64
DOTNET_RUNTIME_IDENTIFIER: linux-x64
RELEASE_ZIP_OS_NAME: linux_x64
- os: windows-latest
OS_NAME: Windows x64
DOTNET_RUNTIME_IDENTIFIER: win10-x64
RELEASE_ZIP_OS_NAME: win_x64
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
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;' 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
run: |
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_gtk
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
popd
pushd publish_sdl2_headless
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
popd
pushd publish_ava
7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip publish
popd
shell: bash
- name: Packing Linux builds
if: matrix.os == 'ubuntu-latest'
run: |
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_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_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
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
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 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 }}
macos_release:
name: Release MacOS universal
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
global-json-file: global.json
- name: Setup LLVM 14
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 14
- name: Install rcodesign
run: |
mkdir -p $HOME/.bin
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
rm apple-codesign.tar.gz
mv rcodesign $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info
id: version_info
run: |
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
- name: Configure for release
run: |
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
- name: Publish macOS
run: |
./distribution/macos/create_macos_build.sh . publish_tmp publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
- name: Pushing new release
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version_info.outputs.build_version }}
artifacts: "publish_ava/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }}
body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)."
omitBodyDuringUpdate: true
allowUpdates: true
replacesArtifacts: true
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
token: ${{ secrets.RELEASE_TOKEN }}
flatpak_release: flatpak_release:
uses: ./.github/workflows/flatpak.yml uses: ./.github/workflows/flatpak.yml
needs: release needs: release

View File

@@ -44,7 +44,7 @@
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.0" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" />
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
<PackageVersion Include="System.Management" Version="7.0.1" /> <PackageVersion Include="System.Management" Version="7.0.1" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />

View File

@@ -2,8 +2,8 @@
set -e set -e
if [ "$#" -ne 6 ]; then if [ "$#" -lt 7 ]; then
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID>" echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <EXTRA_ARGS>"
exit 1 exit 1
fi fi
@@ -17,8 +17,16 @@ OUTPUT_DIRECTORY=$(readlink -f "$3")
ENTITLEMENTS_FILE_PATH=$(readlink -f "$4") ENTITLEMENTS_FILE_PATH=$(readlink -f "$4")
VERSION=$5 VERSION=$5
SOURCE_REVISION_ID=$6 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
RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app" ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
X64_APP_BUNDLE="$TEMP_DIRECTORY/output_x64/Ryujinx.app" X64_APP_BUNDLE="$TEMP_DIRECTORY/output_x64/Ryujinx.app"
UNIVERSAL_APP_BUNDLE="$OUTPUT_DIRECTORY/Ryujinx.app" UNIVERSAL_APP_BUNDLE="$OUTPUT_DIRECTORY/Ryujinx.app"
@@ -27,12 +35,12 @@ EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx
rm -rf "$TEMP_DIRECTORY" rm -rf "$TEMP_DIRECTORY"
mkdir -p "$TEMP_DIRECTORY" mkdir -p "$TEMP_DIRECTORY"
DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true" DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true $EXTRA_ARGS"
dotnet restore dotnet restore
dotnet build -c Release src/Ryujinx.Ava dotnet build -c $CONFIGURATION src/Ryujinx.Ava
dotnet publish -c Release -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava dotnet publish -c $CONFIGURATION -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" $DOTNET_COMMON_ARGS src/Ryujinx.Ava
dotnet publish -c Release -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" $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) # Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib" rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
@@ -68,7 +76,7 @@ else
LIPO=lipo LIPO=lipo
fi fi
# Make it the executable universal # 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 $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 # Patch up the Info.plist to have appropriate version
@@ -87,10 +95,10 @@ then
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes. # 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 --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
echo "Usign rcodesign for ad-hoc signing" echo "Using rcodesign for ad-hoc signing"
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE" rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE"
else else
echo "Usign codesign for ad-hoc signing" echo "Using codesign for ad-hoc signing"
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$UNIVERSAL_APP_BUNDLE" codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$UNIVERSAL_APP_BUNDLE"
fi fi

View File

@@ -36,4 +36,9 @@ sleep 1
# Now replace and reopen. # Now replace and reopen.
rm -rf "$INSTALL_DIRECTORY" rm -rf "$INSTALL_DIRECTORY"
mv "$NEW_APP_DIRECTORY" "$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

View File

@@ -54,6 +54,7 @@ namespace ARMeilleure.Translation
internal TranslatorQueue Queue { get; } internal TranslatorQueue Queue { get; }
internal IMemoryManager Memory { get; } internal IMemoryManager Memory { get; }
private Thread[] _backgroundTranslationThreads;
private volatile int _threadCount; private volatile int _threadCount;
// FIXME: Remove this once the init logic of the emulator will be redone. // FIXME: Remove this once the init logic of the emulator will be redone.
@@ -127,18 +128,22 @@ namespace ARMeilleure.Translation
int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3); int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3);
int threadCount = Math.Min(4, unboundedThreadCount); int threadCount = Math.Min(4, unboundedThreadCount);
Thread[] backgroundTranslationThreads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) for (int i = 0; i < threadCount; i++)
{ {
bool last = i != 0 && i == unboundedThreadCount - 1; bool last = i != 0 && i == unboundedThreadCount - 1;
Thread backgroundTranslatorThread = new Thread(BackgroundTranslate) backgroundTranslationThreads[i] = new Thread(BackgroundTranslate)
{ {
Name = "CPU.BackgroundTranslatorThread." + i, Name = "CPU.BackgroundTranslatorThread." + i,
Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal Priority = last ? ThreadPriority.Lowest : ThreadPriority.Normal
}; };
backgroundTranslatorThread.Start(); backgroundTranslationThreads[i].Start();
} }
Interlocked.Exchange(ref _backgroundTranslationThreads, backgroundTranslationThreads);
} }
Statistics.InitializeTimer(); Statistics.InitializeTimer();
@@ -162,9 +167,20 @@ namespace ARMeilleure.Translation
if (Interlocked.Decrement(ref _threadCount) == 0) if (Interlocked.Decrement(ref _threadCount) == 0)
{ {
Queue.Dispose();
Thread[] backgroundTranslationThreads = Interlocked.Exchange(ref _backgroundTranslationThreads, null);
if (backgroundTranslationThreads != null)
{
foreach (Thread thread in backgroundTranslationThreads)
{
thread.Join();
}
}
ClearJitCache(); ClearJitCache();
Queue.Dispose();
Stubs.Dispose(); Stubs.Dispose();
FunctionTable.Dispose(); FunctionTable.Dispose();
CountTable.Dispose(); CountTable.Dispose();

View File

@@ -5,6 +5,7 @@ using Ryujinx.Memory;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
@@ -18,6 +19,13 @@ namespace Ryujinx.Audio.Backends.SDL2
private readonly ManualResetEvent _pauseEvent; private readonly ManualResetEvent _pauseEvent;
private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions; private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions;
private bool _supportSurroundConfiguration;
// TODO: Add this to SDL2-CS
// NOTE: We use a DllImport here because of marshaling issue for spec.
[DllImport("SDL2")]
private static extern int SDL_GetDefaultAudioInfo(IntPtr name, out SDL_AudioSpec spec, int isCapture);
public SDL2HardwareDeviceDriver() public SDL2HardwareDeviceDriver()
{ {
_updateRequiredEvent = new ManualResetEvent(false); _updateRequiredEvent = new ManualResetEvent(false);
@@ -25,6 +33,20 @@ namespace Ryujinx.Audio.Backends.SDL2
_sessions = new ConcurrentDictionary<SDL2HardwareDeviceSession, byte>(); _sessions = new ConcurrentDictionary<SDL2HardwareDeviceSession, byte>();
SDL2Driver.Instance.Initialize(); SDL2Driver.Instance.Initialize();
int res = SDL_GetDefaultAudioInfo(IntPtr.Zero, out var spec, 0);
if (res != 0)
{
Logger.Error?.Print(LogClass.Application,
$"SDL_GetDefaultAudioInfo failed with error \"{SDL_GetError()}\"");
_supportSurroundConfiguration = true;
}
else
{
_supportSurroundConfiguration = spec.channels == 6;
}
} }
public static bool IsSupported => IsSupportedInternal(); public static bool IsSupported => IsSupportedInternal();
@@ -164,6 +186,11 @@ namespace Ryujinx.Audio.Backends.SDL2
public bool SupportsChannelCount(uint channelCount) public bool SupportsChannelCount(uint channelCount)
{ {
if (channelCount == 6)
{
return _supportSurroundConfiguration;
}
return true; return true;
} }

View File

@@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Device
/// <param name="name">The name of the <see cref="VirtualDevice"/>.</param> /// <param name="name">The name of the <see cref="VirtualDevice"/>.</param>
/// <param name="channelCount">The count of channels supported by the <see cref="VirtualDevice"/>.</param> /// <param name="channelCount">The count of channels supported by the <see cref="VirtualDevice"/>.</param>
/// <param name="isExternalOutput">Indicate if the <see cref="VirtualDevice"/> is provided by an external interface.</param> /// <param name="isExternalOutput">Indicate if the <see cref="VirtualDevice"/> is provided by an external interface.</param>
private VirtualDevice(string name, uint channelCount, bool isExternalOutput) public VirtualDevice(string name, uint channelCount, bool isExternalOutput)
{ {
Name = name; Name = name;
ChannelCount = channelCount; ChannelCount = channelCount;

View File

@@ -1,3 +1,4 @@
using Ryujinx.Audio.Integration;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Audio.Renderer.Device namespace Ryujinx.Audio.Renderer.Device
@@ -22,7 +23,23 @@ namespace Ryujinx.Audio.Renderer.Device
/// The current active <see cref="VirtualDevice"/>. /// The current active <see cref="VirtualDevice"/>.
/// </summary> /// </summary>
// TODO: make this configurable // TODO: make this configurable
public VirtualDevice ActiveDevice = VirtualDevice.Devices[2]; public VirtualDevice ActiveDevice { get; }
public VirtualDeviceSessionRegistry(IHardwareDeviceDriver driver)
{
uint channelCount;
if (driver.GetRealDeviceDriver().SupportsChannelCount(6))
{
channelCount = 6;
}
else
{
channelCount = 2;
}
ActiveDevice = new VirtualDevice("AudioTvOutput", channelCount, false);
}
/// <summary> /// <summary>
/// Get the associated <see cref="T:VirtualDeviceSession[]"/> from an AppletResourceId. /// Get the associated <see cref="T:VirtualDeviceSession[]"/> from an AppletResourceId.

View File

@@ -65,9 +65,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
{ {
OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax]; OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax];
// TODO: Before enabling this, we need up-mixing from stereo to 5.1. uint channelCount = GetHardwareChannelCount(deviceDriver);
// uint channelCount = GetHardwareChannelCount(deviceDriver);
uint channelCount = 2;
for (int i = 0; i < OutputDevices.Length; i++) for (int i = 0; i < OutputDevices.Length; i++)
{ {

View File

@@ -49,8 +49,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]); OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
} }
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices); DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount);
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices); DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices, Parameter.ChannelCount);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]

View File

@@ -67,7 +67,19 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
const int sampleCount = Constants.TargetSampleCount; const int sampleCount = Constants.TargetSampleCount;
short[] outputBuffer = new short[bufferCount * sampleCount]; uint inputCount;
// In case of upmixing to 5.1, we allocate the right amount.
if (bufferCount != channelCount && channelCount == 6)
{
inputCount = (uint)channelCount;
}
else
{
inputCount = bufferCount;
}
short[] outputBuffer = new short[inputCount * sampleCount];
for (int i = 0; i < bufferCount; i++) for (int i = 0; i < bufferCount; i++)
{ {
@@ -79,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
} }
} }
device.AppendBuffer(outputBuffer, InputCount); device.AppendBuffer(outputBuffer, inputCount);
} }
else else
{ {

View File

@@ -66,8 +66,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour // NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
// TODO: Update reverb 3d processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping. // TODO: Update reverb 3d processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping.
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices); DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount);
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices); DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices, Parameter.ChannelCount);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -116,7 +116,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
for (int i = 0; i < targetEarlyDelayLineIndicesTable.Length; i++) for (int i = 0; i < targetEarlyDelayLineIndicesTable.Length; i++)
{ {
int earlyDelayIndex = targetEarlyDelayLineIndicesTable[i]; int earlyDelayIndex = targetEarlyDelayLineIndicesTable[i];
int outputIndex = outputEarlyIndicesTable[i]; int outputIndex = outputEarlyIndicesTable[earlyDelayIndex];
float tempTapOut = state.PreDelayLine.TapUnsafe(state.EarlyDelayTime[earlyDelayIndex], delayLineSampleIndexOffset); float tempTapOut = state.PreDelayLine.TapUnsafe(state.EarlyDelayTime[earlyDelayIndex], delayLineSampleIndexOffset);

View File

@@ -71,8 +71,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour // NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
// TODO: Update reverb processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping. // TODO: Update reverb processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping.
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices); DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount);
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices); DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices, Parameter.ChannelCount);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -430,9 +430,9 @@ namespace Ryujinx.Audio.Renderer.Dsp
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool isSupported, Span<ushort> bufferIndices) public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool isSupported, Span<ushort> bufferIndices, uint channelCount)
{ {
if (!isSupported && bufferIndices.Length == 6) if (!isSupported && channelCount == 6)
{ {
ushort backLeft = bufferIndices[2]; ushort backLeft = bufferIndices[2];
ushort backRight = bufferIndices[3]; ushort backRight = bufferIndices[3];
@@ -447,9 +447,9 @@ namespace Ryujinx.Audio.Renderer.Dsp
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemapChannelResourceMappingToLegacy(bool isSupported, Span<ushort> bufferIndices) public static void RemapChannelResourceMappingToLegacy(bool isSupported, Span<ushort> bufferIndices, uint channelCount)
{ {
if (isSupported && bufferIndices.Length == 6) if (isSupported && channelCount == 6)
{ {
ushort frontCenter = bufferIndices[2]; ushort frontCenter = bufferIndices[2];
ushort lowFrequency = bufferIndices[3]; ushort lowFrequency = bufferIndices[3];

View File

@@ -86,7 +86,7 @@ namespace Ryujinx.Ava
private KeyboardHotkeyState _prevHotkeyState; private KeyboardHotkeyState _prevHotkeyState;
private long _lastCursorMoveTime; private long _lastCursorMoveTime;
private bool _isCursorInRenderer; private bool _isCursorInRenderer = true;
private bool _isStopped; private bool _isStopped;
private bool _isActive; private bool _isActive;
@@ -160,7 +160,9 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed;
_topLevel.PointerMoved += TopLevel_PointerMoved; _topLevel.PointerMoved += TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter += TopLevel_PointerEnterOrMoved;
_topLevel.PointerLeave += TopLevel_PointerLeave;
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
@@ -183,7 +185,7 @@ namespace Ryujinx.Ava
_gpuCancellationTokenSource = new CancellationTokenSource(); _gpuCancellationTokenSource = new CancellationTokenSource();
} }
private void TopLevel_PointerMoved(object sender, PointerEventArgs e) private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
{ {
if (sender is MainWindow window) if (sender is MainWindow window)
{ {
@@ -201,6 +203,12 @@ namespace Ryujinx.Ava
} }
} }
} }
private void TopLevel_PointerLeave(object sender, PointerEventArgs e)
{
_isCursorInRenderer = false;
}
private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e) private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
{ {
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
@@ -446,7 +454,9 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
_topLevel.PointerMoved -= TopLevel_PointerMoved; _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerLeave -= TopLevel_PointerLeave;
_gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Cancel();
_gpuCancellationTokenSource.Dispose(); _gpuCancellationTokenSource.Dispose();
@@ -671,7 +681,7 @@ namespace Ryujinx.Ava
_viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata =>
{ {
appMetadata.LastPlayed = DateTime.UtcNow.ToString(); appMetadata.LastPlayed = DateTime.UtcNow;
}); });
return true; return true;

View File

@@ -70,13 +70,16 @@ namespace Ryujinx.Ava.Input
private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args) private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args)
{ {
int button = (int)args.InitialPressMouseButton - 1; if (args.InitialPressMouseButton != Avalonia.Input.MouseButton.None)
{
int button = (int)args.InitialPressMouseButton;
if (PressedButtons.Count() >= button) if (PressedButtons.Count() >= button)
{ {
PressedButtons[button] = false; PressedButtons[button] = false;
} }
} }
}
private void Parent_PointerPressEvent(object o, PointerPressedEventArgs args) private void Parent_PointerPressEvent(object o, PointerPressedEventArgs args)
{ {

View File

@@ -295,14 +295,7 @@ namespace Ryujinx.Modules
if (shouldRestart) if (shouldRestart)
{ {
List<string> arguments = CommandLineState.Arguments.ToList(); List<string> arguments = CommandLineState.Arguments.ToList();
string ryuName = Path.GetFileName(Environment.ProcessPath);
string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; string executableDirectory = AppDomain.CurrentDomain.BaseDirectory;
string executablePath = Path.Combine(executableDirectory, ryuName);
if (!Path.Exists(executablePath))
{
executablePath = Path.Combine(executableDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
}
// On macOS we perform the update at relaunch. // On macOS we perform the update at relaunch.
if (OperatingSystem.IsMacOS()) if (OperatingSystem.IsMacOS())
@@ -310,13 +303,42 @@ namespace Ryujinx.Modules
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", "..")); string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app"); string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app");
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh"); string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
string currentPid = Process.GetCurrentProcess().Id.ToString(); string currentPid = Environment.ProcessId.ToString();
executablePath = "/bin/bash";
arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid }); arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid });
Process.Start("/bin/bash", arguments);
}
else
{
// Find the process name.
string ryuName = Path.GetFileName(Environment.ProcessPath);
// Some operating systems can see the renamed executable, so strip off the .ryuold if found.
if (ryuName.EndsWith(".ryuold"))
{
ryuName = ryuName[..^7];
}
// Fallback if the executable could not be found.
if (!Path.Exists(Path.Combine(executableDirectory, ryuName)))
{
ryuName = OperatingSystem.IsWindows() ? "Ryujinx.Ava.exe" : "Ryujinx.Ava";
}
ProcessStartInfo processStart = new(ryuName)
{
UseShellExecute = true,
WorkingDirectory = executableDirectory
};
foreach (string argument in CommandLineState.Arguments)
{
processStart.ArgumentList.Add(argument);
}
Process.Start(processStart);
} }
Process.Start(executablePath, arguments);
Environment.Exit(0); Environment.Exit(0);
} }
} }

View File

@@ -129,7 +129,7 @@
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding LastPlayed}" Text="{Binding LastPlayed, Converter={helpers:NullableDateTimeConverter}}"
TextAlignment="Right" TextAlignment="Right"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock

View File

@@ -0,0 +1,38 @@
using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.Common.Locale;
using System;
using System.Globalization;
namespace Ryujinx.Ava.UI.Helpers
{
internal class NullableDateTimeConverter : MarkupExtension, IValueConverter
{
private static readonly NullableDateTimeConverter _instance = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return LocaleManager.Instance[LocaleKeys.Never];
}
if (value is DateTime dateTime)
{
return dateTime.ToLocalTime().ToString(culture);
}
throw new NotSupportedException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _instance;
}
}
}

View File

@@ -1,4 +1,3 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -14,20 +13,20 @@ namespace Ryujinx.Ava.UI.Models.Generic
public int Compare(ApplicationData x, ApplicationData y) public int Compare(ApplicationData x, ApplicationData y)
{ {
string aValue = x.LastPlayed; var aValue = x.LastPlayed;
string bValue = y.LastPlayed; var bValue = y.LastPlayed;
if (aValue == LocaleManager.Instance[LocaleKeys.Never]) if (!aValue.HasValue)
{ {
aValue = DateTime.UnixEpoch.ToString(); aValue = DateTime.UnixEpoch;
} }
if (bValue == LocaleManager.Instance[LocaleKeys.Never]) if (!bValue.HasValue)
{ {
bValue = DateTime.UnixEpoch.ToString(); bValue = DateTime.UnixEpoch;
} }
return (IsAscending ? 1 : -1) * DateTime.Compare(DateTime.Parse(bValue), DateTime.Parse(aValue)); return (IsAscending ? 1 : -1) * DateTime.Compare(bValue.Value, aValue.Value);
} }
} }
} }

View File

@@ -370,7 +370,7 @@ namespace Ryujinx.Ava.UI.ViewModels
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
return response.Content.Headers.LastModified != oldLastModified; return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero);
} }
return false; return false;

View File

@@ -1524,10 +1524,9 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
{ {
if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime)) if (appMetadata.LastPlayed.HasValue)
{ {
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
} }
}); });

View File

@@ -21,35 +21,54 @@
<UserControl.Resources> <UserControl.Resources>
<helpers:KeyValueConverter x:Key="Key" /> <helpers:KeyValueConverter x:Key="Key" />
</UserControl.Resources> </UserControl.Resources>
<UserControl.Styles>
<Style Selector="ToggleButton">
<Setter Property="Width" Value="90" />
<Setter Property="Height" Value="27" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</UserControl.Styles>
<StackPanel <StackPanel
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
Orientation="Vertical"> Orientation="Vertical">
<Grid Margin="2,2,2,5" HorizontalAlignment="Stretch"> <Grid Margin="0,2,0,5" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="0.5*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Player selection -->
<Border <Border
Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Margin="0,0,2,0"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
Padding="2,0"> Padding="2">
<StackPanel <Grid
Margin="2" Margin="2"
HorizontalAlignment="Center" HorizontalAlignment="Stretch"
VerticalAlignment="Center" VerticalAlignment="Center">
Orientation="Vertical"> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="0,0,0,4" Margin="5,0,10,0"
HorizontalAlignment="Center" Width="90"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsPlayer}" /> Text="{locale:Locale ControllerSettingsPlayer}" />
<ComboBox <ComboBox
Grid.Column="1"
Name="PlayerIndexBox" Name="PlayerIndexBox"
Width="150" HorizontalAlignment="Stretch"
VerticalAlignment="Center"
SelectionChanged="PlayerIndexBox_OnSelectionChanged" SelectionChanged="PlayerIndexBox_OnSelectionChanged"
Items="{Binding PlayerIndexes}" Items="{Binding PlayerIndexes}"
SelectedIndex="{Binding PlayerId}"> SelectedIndex="{Binding PlayerId}">
@@ -59,103 +78,44 @@
</DataTemplate> </DataTemplate>
</ComboBox.ItemTemplate> </ComboBox.ItemTemplate>
</ComboBox> </ComboBox>
</StackPanel>
</Border>
<!-- Main Controller Settings -->
<Border
Grid.Column="1"
Margin="0,0,2,0"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
Padding="2,0">
<StackPanel
Margin="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Orientation="Vertical">
<TextBlock
Margin="0,0,0,5"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsInputDevice}" />
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox
Name="DeviceBox"
HorizontalAlignment="Stretch"
Items="{Binding DeviceList}"
SelectedIndex="{Binding Device}" />
<Button
Grid.Column="1"
MinWidth="0"
Margin="5,0,0,0"
VerticalAlignment="Center"
Command="{Binding LoadDevices}">
<ui:SymbolIcon
Symbol="Refresh"
FontSize="15"
Height="20" />
</Button>
</Grid> </Grid>
</StackPanel>
</Border> </Border>
<!-- Profile selection -->
<Border <Border
Grid.Column="2" Grid.Row="1"
Margin="0,0,2,0" Grid.Column="0"
Margin="0,-1,0,0"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
Padding="2,0"> Padding="2">
<Grid <Grid
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center"> VerticalAlignment="Center">
<Grid.RowDefinitions> <Grid.ColumnDefinitions>
<RowDefinition /> <ColumnDefinition Width="Auto"/>
<RowDefinition /> <ColumnDefinition Width="*" />
</Grid.RowDefinitions> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="0,0,0,4" Margin="5,0,10,0"
HorizontalAlignment="Center" Width="90"
Text="{locale:Locale ControllerSettingsControllerType}" /> HorizontalAlignment="Left"
<ComboBox
Grid.Row="1"
HorizontalAlignment="Stretch"
Items="{Binding Controllers}"
SelectedIndex="{Binding Controller}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Border>
<Border
Grid.Column="3"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
Padding="2,0" >
<StackPanel
Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Orientation="Vertical">
<TextBlock
Margin="0,0,0,4"
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsProfile}" /> Text="{locale:Locale ControllerSettingsProfile}" />
<StackPanel Orientation="Horizontal">
<ui:ComboBox <ui:ComboBox
Grid.Column="1"
IsEditable="True" IsEditable="True"
Name="ProfileBox" Name="ProfileBox"
Width="100" HorizontalAlignment="Stretch"
VerticalAlignment="Center"
SelectedIndex="0" SelectedIndex="0"
Items="{Binding ProfilesList}" Items="{Binding ProfilesList}"
Text="{Binding ProfileName}" /> Text="{Binding ProfileName}" />
<Button <Button
Grid.Column="2"
MinWidth="0" MinWidth="0"
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -167,6 +127,7 @@
Height="20" /> Height="20" />
</Button> </Button>
<Button <Button
Grid.Column="3"
MinWidth="0" MinWidth="0"
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -178,6 +139,7 @@
Height="20" /> Height="20" />
</Button> </Button>
<Button <Button
Grid.Column="4"
MinWidth="0" MinWidth="0"
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -188,8 +150,85 @@
FontSize="15" FontSize="15"
Height="20" /> Height="20" />
</Button> </Button>
</StackPanel> </Grid>
</StackPanel> </Border>
<!-- Input device -->
<Border
Grid.Row="0"
Grid.Column="1"
Margin="-1,0,0,0"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
Padding="2">
<Grid Margin="2" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="5,0,10,0"
Width="90"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsInputDevice}" />
<ComboBox
Grid.Column="1"
Name="DeviceBox"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Items="{Binding DeviceList}"
SelectedIndex="{Binding Device}" />
<Button
Grid.Column="2"
MinWidth="0"
Margin="5,0,0,0"
VerticalAlignment="Center"
Command="{Binding LoadDevices}">
<ui:SymbolIcon
Symbol="Refresh"
FontSize="15"
Height="20"/>
</Button>
</Grid>
</Border>
<!-- Controler type -->
<Border
Grid.Row="1"
Grid.Column="1"
Margin="-1,-1,0,0"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
Padding="2">
<Grid
Margin="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Margin="5,0,10,0"
Width="90"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsControllerType}" />
<ComboBox
Grid.Column="1"
HorizontalAlignment="Stretch"
Items="{Binding Controllers}"
SelectedIndex="{Binding Controller}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Border> </Border>
</Grid> </Grid>
@@ -206,7 +245,7 @@
<!-- Left --> <!-- Left -->
<Grid <Grid
Margin="0,0,10,0" Margin="0,0,5,0"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
DockPanel.Dock="Left"> DockPanel.Dock="Left">
@@ -221,7 +260,8 @@
Grid.Row="0" Grid.Row="0"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
IsVisible="{Binding IsLeft}"> IsVisible="{Binding IsLeft}"
MinHeight="90">
<Grid Margin="10" HorizontalAlignment="Stretch"> <Grid Margin="10" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition /> <ColumnDefinition />
@@ -232,7 +272,6 @@
<RowDefinition /> <RowDefinition />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel <StackPanel
Margin="0,0,0,4"
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
Background="{DynamicResource ThemeDarkColor}" Background="{DynamicResource ThemeDarkColor}"
@@ -243,10 +282,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsTriggerZL}" Text="{locale:Locale ControllerSettingsTriggerZL}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.ButtonZl, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.ButtonZl, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -263,19 +299,15 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsTriggerL}" Text="{locale:Locale ControllerSettingsTriggerL}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.ButtonL, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.ButtonL, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Margin="0,0,0,4"
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="1"
Background="{DynamicResource ThemeDarkColor}" Background="{DynamicResource ThemeDarkColor}"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock <TextBlock
@@ -284,10 +316,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtonMinus}" Text="{locale:Locale ControllerSettingsButtonMinus}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.ButtonMinus, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.ButtonMinus, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -301,7 +330,8 @@
Grid.Row="1" Grid.Row="1"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
IsVisible="{Binding IsLeft}"> IsVisible="{Binding IsLeft}"
Margin="0,5,0,0">
<StackPanel Margin="10" Orientation="Vertical"> <StackPanel Margin="10" Orientation="Vertical">
<TextBlock <TextBlock
Margin="0,0,0,10" Margin="0,0,0,10"
@@ -320,10 +350,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLStickButton}" Text="{locale:Locale ControllerSettingsLStickButton}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -339,10 +366,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLStickUp}" Text="{locale:Locale ControllerSettingsLStickUp}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftStickUp, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftStickUp, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -358,10 +382,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLStickDown}" Text="{locale:Locale ControllerSettingsLStickDown}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftStickDown, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftStickDown, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -377,10 +398,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLStickLeft}" Text="{locale:Locale ControllerSettingsLStickLeft}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftStickLeft, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftStickLeft, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -396,10 +414,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLStickRight}" Text="{locale:Locale ControllerSettingsLStickRight}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftStickRight, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftStickRight, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -419,10 +434,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLStickButton}" Text="{locale:Locale ControllerSettingsLStickButton}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -438,16 +450,13 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLStickStick}" Text="{locale:Locale ControllerSettingsLStickStick}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton Tag="stick">
Width="90"
Height="27"
HorizontalAlignment="Stretch"
Tag="stick">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftJoystick, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftJoystick, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<Separator Margin="0,8,0,8" Height="1" />
<CheckBox IsChecked="{Binding Configuration.LeftInvertStickX}"> <CheckBox IsChecked="{Binding Configuration.LeftInvertStickX}">
<TextBlock Text="{locale:Locale ControllerSettingsLStickInvertXAxis}" /> <TextBlock Text="{locale:Locale ControllerSettingsLStickInvertXAxis}" />
</CheckBox> </CheckBox>
@@ -457,9 +466,11 @@
<CheckBox IsChecked="{Binding Configuration.LeftRotate90}"> <CheckBox IsChecked="{Binding Configuration.LeftRotate90}">
<TextBlock Text="{locale:Locale ControllerSettingsRotate90}" /> <TextBlock Text="{locale:Locale ControllerSettingsRotate90}" />
</CheckBox> </CheckBox>
<Separator Margin="0,4,0,4" Height="1" /> <Separator Margin="0,8,0,8" Height="1" />
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock Text="{locale:Locale ControllerSettingsLStickDeadzone}" /> <TextBlock
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsLStickDeadzone}" />
<StackPanel <StackPanel
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -473,9 +484,12 @@
Value="{Binding Configuration.DeadzoneLeft, Mode=TwoWay}" /> Value="{Binding Configuration.DeadzoneLeft, Mode=TwoWay}" />
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Width="25"
Text="{Binding Configuration.DeadzoneLeft, StringFormat=\{0:0.00\}}" /> Text="{Binding Configuration.DeadzoneLeft, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<TextBlock Text="{locale:Locale ControllerSettingsStickRange}" /> <TextBlock
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsStickRange}" />
<StackPanel <StackPanel
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -489,6 +503,7 @@
Value="{Binding Configuration.RangeLeft, Mode=TwoWay}" /> Value="{Binding Configuration.RangeLeft, Mode=TwoWay}" />
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Width="25"
Text="{Binding Configuration.RangeLeft, StringFormat=\{0:0.00\}}" /> Text="{Binding Configuration.RangeLeft, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
@@ -502,7 +517,8 @@
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
VerticalAlignment="Top" VerticalAlignment="Top"
IsVisible="{Binding IsLeft}"> IsVisible="{Binding IsLeft}"
Margin="0,5,0,0">
<StackPanel Margin="10" Orientation="Vertical"> <StackPanel Margin="10" Orientation="Vertical">
<TextBlock <TextBlock
Margin="0,0,0,10" Margin="0,0,0,10"
@@ -519,8 +535,6 @@
Text="{locale:Locale ControllerSettingsDPadUp}" Text="{locale:Locale ControllerSettingsDPadUp}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton
Width="90"
Height="27"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.DpadUp, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.DpadUp, Mode=TwoWay, Converter={StaticResource Key}}"
@@ -538,8 +552,6 @@
Text="{locale:Locale ControllerSettingsDPadDown}" Text="{locale:Locale ControllerSettingsDPadDown}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton
Width="90"
Height="27"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.DpadDown, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.DpadDown, Mode=TwoWay, Converter={StaticResource Key}}"
@@ -557,8 +569,6 @@
Text="{locale:Locale ControllerSettingsDPadLeft}" Text="{locale:Locale ControllerSettingsDPadLeft}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton
Width="90"
Height="27"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.DpadLeft, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.DpadLeft, Mode=TwoWay, Converter={StaticResource Key}}"
@@ -576,8 +586,6 @@
Text="{locale:Locale ControllerSettingsDPadRight}" Text="{locale:Locale ControllerSettingsDPadRight}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton
Width="90"
Height="27"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.DpadRight, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.DpadRight, Mode=TwoWay, Converter={StaticResource Key}}"
@@ -590,11 +598,11 @@
</Grid> </Grid>
<!-- Triggers And Side Buttons--> <!-- Triggers And Side Buttons-->
<StackPanel Grid.Column="1" HorizontalAlignment="Stretch"> <StackPanel Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Border <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"> BorderThickness="1" MinHeight="90">
<StackPanel Margin="10" Orientation="Vertical"> <StackPanel Margin="8" Orientation="Vertical">
<TextBlock HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerThreshold}" /> <TextBlock HorizontalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerThreshold}" />
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal"> <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Slider <Slider
@@ -604,7 +612,9 @@
IsSnapToTickEnabled="True" IsSnapToTickEnabled="True"
Minimum="0" Minimum="0"
Value="{Binding Configuration.TriggerThreshold, Mode=TwoWay}" /> Value="{Binding Configuration.TriggerThreshold, Mode=TwoWay}" />
<TextBlock Text="{Binding Configuration.TriggerThreshold, StringFormat=\{0:0.00\}}" /> <TextBlock
Width="25"
Text="{Binding Configuration.TriggerThreshold, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Margin="0,4,0,0" Margin="0,4,0,0"
@@ -619,10 +629,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLeftSR}" Text="{locale:Locale ControllerSettingsLeftSR}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftButtonSr, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftButtonSr, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -641,10 +648,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsLeftSL}" Text="{locale:Locale ControllerSettingsLeftSL}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.LeftButtonSl, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.LeftButtonSl, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -663,10 +667,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRightSR}" Text="{locale:Locale ControllerSettingsRightSR}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.RightButtonSr, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightButtonSr, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -685,10 +686,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRightSL}" Text="{locale:Locale ControllerSettingsRightSL}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.RightButtonSl, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightButtonSl, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -702,14 +700,15 @@
Margin="0,10,0,0" Margin="0,10,0,0"
MaxHeight="250" MaxHeight="250"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Stretch"
Source="{Binding Image}" /> Source="{Binding Image}" />
<!-- Motion+Rumble --> <!-- Motion+Rumble -->
<StackPanel Margin="0,10,0,0" Orientation="Vertical" > <StackPanel Margin="0,10,0,0" Orientation="Vertical" VerticalAlignment="Bottom">
<Border <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
VerticalAlignment="Bottom"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
IsVisible="{Binding IsController}"> IsVisible="{Binding IsController}">
<Grid> <Grid>
@@ -733,7 +732,8 @@
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
IsVisible="{Binding IsController}"> IsVisible="{Binding IsController}"
Margin="0,-1,0,0">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@@ -756,7 +756,7 @@
<!--Right Controls--> <!--Right Controls-->
<Grid <Grid
Margin="10,0,0,0" Margin="5,0,0,0"
Grid.Column="2" Grid.Column="2"
VerticalAlignment="Top" VerticalAlignment="Top"
HorizontalAlignment="Stretch" > HorizontalAlignment="Stretch" >
@@ -770,9 +770,9 @@
Grid.Row="0" Grid.Row="0"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
IsVisible="{Binding IsRight}"> IsVisible="{Binding IsRight}"
<StackPanel Margin="10" Orientation="Vertical"> MinHeight="90">
<Grid HorizontalAlignment="Stretch"> <Grid Margin="10" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition /> <ColumnDefinition />
<ColumnDefinition /> <ColumnDefinition />
@@ -782,7 +782,6 @@
<RowDefinition /> <RowDefinition />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel <StackPanel
Margin="0,0,0,4"
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"
Background="{DynamicResource ThemeDarkColor}" Background="{DynamicResource ThemeDarkColor}"
@@ -793,10 +792,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsTriggerZR}" Text="{locale:Locale ControllerSettingsTriggerZR}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.ButtonZr, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.ButtonZr, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -815,19 +811,15 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsTriggerR}" Text="{locale:Locale ControllerSettingsTriggerR}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.ButtonR, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.ButtonR, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Margin="0,0,8,4"
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="1"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
Background="{DynamicResource ThemeDarkColor}" Background="{DynamicResource ThemeDarkColor}"
@@ -838,97 +830,39 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtonPlus}" Text="{locale:Locale ControllerSettingsButtonPlus}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.ButtonPlus, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.ButtonPlus, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
</Grid> </Grid>
</StackPanel>
</Border> </Border>
<Border <Border
Grid.Row="1" Grid.Row="1"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
IsVisible="{Binding IsRight}"> IsVisible="{Binding IsRight}"
Margin="0,5,0,0">
<StackPanel Margin="10" Orientation="Vertical"> <StackPanel Margin="10" Orientation="Vertical">
<TextBlock <TextBlock
Margin="0,0,0,10" Margin="0,0,0,10"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtons}" /> Text="{locale:Locale ControllerSettingsButtons}" />
<Grid HorizontalAlignment="Stretch"> <StackPanel Orientation="Vertical">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!-- Right Buttons X -->
<StackPanel
Margin="0,0,0,4"
Grid.Column="0"
Grid.Row="0"
Background="{DynamicResource ThemeDarkColor}"
Orientation="Horizontal">
<TextBlock
Width="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtonX}"
TextAlignment="Center" />
<ToggleButton
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock
Text="{Binding Configuration.ButtonX, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<!-- Right Buttons Y -->
<StackPanel
Grid.Column="0"
Grid.Row="1"
Background="{DynamicResource ThemeDarkColor}"
Orientation="Horizontal">
<TextBlock
Width="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtonY}"
TextAlignment="Center" />
<ToggleButton
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock
Text="{Binding Configuration.ButtonY, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<!-- Right Buttons A --> <!-- Right Buttons A -->
<StackPanel <StackPanel
Margin="0,0,0,4" Margin="0,0,0,4"
Grid.Column="1"
Grid.Row="0"
Background="{DynamicResource ThemeDarkColor}" Background="{DynamicResource ThemeDarkColor}"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock <TextBlock
Width="20" Width="120"
Margin="0,0,10,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtonA}" Text="{locale:Locale ControllerSettingsButtonA}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.ButtonA, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.ButtonA, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -936,26 +870,59 @@
</StackPanel> </StackPanel>
<!-- Right Buttons B --> <!-- Right Buttons B -->
<StackPanel <StackPanel
Grid.Column="1" Margin="0,0,0,4"
Grid.Row="1"
Background="{DynamicResource ThemeDarkColor}" Background="{DynamicResource ThemeDarkColor}"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock <TextBlock
Width="20" Width="120"
Margin="0,0,10,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtonB}" Text="{locale:Locale ControllerSettingsButtonB}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.ButtonB, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.ButtonB, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
</Grid> <!-- Right Buttons X -->
<StackPanel
Margin="0,0,0,4"
Background="{DynamicResource ThemeDarkColor}"
Orientation="Horizontal">
<TextBlock
Width="120"
Margin="0,0,10,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtonX}"
TextAlignment="Center" />
<ToggleButton>
<TextBlock
Text="{Binding Configuration.ButtonX, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<!-- Right Buttons Y -->
<StackPanel
Margin="0,0,0,4"
Background="{DynamicResource ThemeDarkColor}"
Orientation="Horizontal">
<TextBlock
Width="120"
Margin="0,0,10,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsButtonY}"
TextAlignment="Center" />
<ToggleButton>
<TextBlock
Text="{Binding Configuration.ButtonY, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
</StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
<Border <Border
@@ -963,7 +930,8 @@
Padding="10" Padding="10"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
IsVisible="{Binding IsRight}"> IsVisible="{Binding IsRight}"
Margin="0,5,0,0">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock <TextBlock
Margin="0,0,0,10" Margin="0,0,0,10"
@@ -982,10 +950,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRStickButton}" Text="{locale:Locale ControllerSettingsRStickButton}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.RightKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightKeyboardStickButton, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -1001,10 +966,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRStickUp}" Text="{locale:Locale ControllerSettingsRStickUp}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.RightStickUp, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightStickUp, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -1020,10 +982,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRStickDown}" Text="{locale:Locale ControllerSettingsRStickDown}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.RightStickDown, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightStickDown, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -1039,10 +998,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRStickLeft}" Text="{locale:Locale ControllerSettingsRStickLeft}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.RightStickLeft, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightStickLeft, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -1058,10 +1014,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRStickRight}" Text="{locale:Locale ControllerSettingsRStickRight}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.RightStickRight, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightStickRight, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -1081,10 +1034,7 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRStickButton}" Text="{locale:Locale ControllerSettingsRStickButton}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton>
Width="90"
Height="27"
HorizontalAlignment="Stretch">
<TextBlock <TextBlock
Text="{Binding Configuration.RightControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightControllerStickButton, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
@@ -1101,16 +1051,13 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{locale:Locale ControllerSettingsRStickStick}" Text="{locale:Locale ControllerSettingsRStickStick}"
TextAlignment="Center" /> TextAlignment="Center" />
<ToggleButton <ToggleButton Tag="stick">
Width="90"
Height="27"
HorizontalAlignment="Stretch"
Tag="stick">
<TextBlock <TextBlock
Text="{Binding Configuration.RightJoystick, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding Configuration.RightJoystick, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<Separator Margin="0,8,0,8" Height="1" />
<CheckBox IsChecked="{Binding Configuration.RightInvertStickX}"> <CheckBox IsChecked="{Binding Configuration.RightInvertStickX}">
<TextBlock Text="{locale:Locale ControllerSettingsRStickInvertXAxis}" /> <TextBlock Text="{locale:Locale ControllerSettingsRStickInvertXAxis}" />
</CheckBox> </CheckBox>
@@ -1120,9 +1067,11 @@
<CheckBox IsChecked="{Binding Configuration.RightRotate90}"> <CheckBox IsChecked="{Binding Configuration.RightRotate90}">
<TextBlock Text="{locale:Locale ControllerSettingsRotate90}" /> <TextBlock Text="{locale:Locale ControllerSettingsRotate90}" />
</CheckBox> </CheckBox>
<Separator Margin="0,4,0,4" Height="1" /> <Separator Margin="0,8,0,8" Height="1" />
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock Text="{locale:Locale ControllerSettingsRStickDeadzone}" /> <TextBlock
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsRStickDeadzone}" />
<StackPanel <StackPanel
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -1138,9 +1087,12 @@
Value="{Binding Configuration.DeadzoneRight, Mode=TwoWay}" /> Value="{Binding Configuration.DeadzoneRight, Mode=TwoWay}" />
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Width="25"
Text="{Binding Configuration.DeadzoneRight, StringFormat=\{0:0.00\}}" /> Text="{Binding Configuration.DeadzoneRight, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<TextBlock Text="{locale:Locale ControllerSettingsStickRange}" /> <TextBlock
HorizontalAlignment="Center"
Text="{locale:Locale ControllerSettingsStickRange}" />
<StackPanel <StackPanel
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -1154,6 +1106,7 @@
Value="{Binding Configuration.RangeRight, Mode=TwoWay}" /> Value="{Binding Configuration.RangeRight, Mode=TwoWay}" />
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Width="25"
Text="{Binding Configuration.RangeRight, StringFormat=\{0:0.00\}}" /> Text="{Binding Configuration.RangeRight, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

View File

@@ -98,6 +98,9 @@ namespace Ryujinx.Graphics.Gpu
private Thread _gpuThread; private Thread _gpuThread;
private bool _pendingSync; private bool _pendingSync;
private long _modifiedSequence;
private ulong _firstTimestamp;
/// <summary> /// <summary>
/// Creates a new instance of the GPU emulation context. /// Creates a new instance of the GPU emulation context.
/// </summary> /// </summary>
@@ -121,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu
DeferredActions = new Queue<Action>(); DeferredActions = new Queue<Action>();
PhysicalMemoryRegistry = new ConcurrentDictionary<ulong, PhysicalMemory>(); PhysicalMemoryRegistry = new ConcurrentDictionary<ulong, PhysicalMemory>();
_firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
} }
/// <summary> /// <summary>
@@ -200,13 +205,23 @@ namespace Ryujinx.Graphics.Gpu
return divided * NsToTicksFractionNumerator + errorBias; return divided * NsToTicksFractionNumerator + errorBias;
} }
/// <summary>
/// Gets a sequence number for resource modification ordering. This increments on each call.
/// </summary>
/// <returns>A sequence number for resource modification ordering</returns>
public long GetModifiedSequence()
{
return _modifiedSequence++;
}
/// <summary> /// <summary>
/// Gets the value of the GPU timer. /// Gets the value of the GPU timer.
/// </summary> /// </summary>
/// <returns>The current GPU timestamp</returns> /// <returns>The current GPU timestamp</returns>
public ulong GetTimestamp() public ulong GetTimestamp()
{ {
ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); // Guest timestamp will start at 0, instead of host value.
ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds) - _firstTimestamp;
if (GraphicsConfig.FastGpuTime) if (GraphicsConfig.FastGpuTime)
{ {

View File

@@ -1170,6 +1170,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="caps">Host GPU capabilities</param> /// <param name="caps">Host GPU capabilities</param>
/// <param name="firstLayer">Texture view initial layer on this texture</param> /// <param name="firstLayer">Texture view initial layer on this texture</param>
/// <param name="firstLevel">Texture view first mipmap level on this texture</param> /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
/// <param name="flags">Texture search flags</param>
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns> /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
public TextureViewCompatibility IsViewCompatible( public TextureViewCompatibility IsViewCompatible(
TextureInfo info, TextureInfo info,
@@ -1178,11 +1179,12 @@ namespace Ryujinx.Graphics.Gpu.Image
int layerSize, int layerSize,
Capabilities caps, Capabilities caps,
out int firstLayer, out int firstLayer,
out int firstLevel) out int firstLevel,
TextureSearchFlags flags = TextureSearchFlags.None)
{ {
TextureViewCompatibility result = TextureViewCompatibility.Full; TextureViewCompatibility result = TextureViewCompatibility.Full;
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps, flags));
if (result != TextureViewCompatibility.Incompatible) if (result != TextureViewCompatibility.Incompatible)
{ {
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps));

View File

@@ -569,7 +569,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Texture texture = null; Texture texture = null;
TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch; long bestSequence = 0;
for (int index = 0; index < sameAddressOverlapsCount; index++) for (int index = 0; index < sameAddressOverlapsCount; index++)
{ {
@@ -601,17 +601,12 @@ namespace Ryujinx.Graphics.Gpu.Image
continue; continue;
} }
} }
}
if (matchQuality == TextureMatchQuality.Perfect) if (texture == null || overlap.Group.ModifiedSequence - bestSequence > 0)
{ {
texture = overlap; texture = overlap;
break; bestSequence = overlap.Group.ModifiedSequence;
} }
else if (matchQuality > bestQuality)
{
texture = overlap;
bestQuality = matchQuality;
} }
} }
@@ -664,6 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int fullyCompatible = 0; int fullyCompatible = 0;
// Evaluate compatibility of overlaps, add temporary references // Evaluate compatibility of overlaps, add temporary references
int preferredOverlap = -1;
for (int index = 0; index < overlapsCount; index++) for (int index = 0; index < overlapsCount; index++)
{ {
@@ -675,17 +671,26 @@ namespace Ryujinx.Graphics.Gpu.Image
sizeInfo.LayerSize, sizeInfo.LayerSize,
_context.Capabilities, _context.Capabilities,
out int firstLayer, out int firstLayer,
out int firstLevel); out int firstLevel,
flags);
if (overlapCompatibility == TextureViewCompatibility.Full) if (overlapCompatibility >= TextureViewCompatibility.FormatAlias)
{ {
if (overlap.IsView) if (overlap.IsView)
{ {
overlapCompatibility = TextureViewCompatibility.CopyOnly; overlapCompatibility = overlapCompatibility == TextureViewCompatibility.FormatAlias ?
TextureViewCompatibility.Incompatible :
TextureViewCompatibility.CopyOnly;
} }
else else
{ {
fullyCompatible++; fullyCompatible++;
if (preferredOverlap == -1 || overlap.Group.ModifiedSequence - bestSequence > 0)
{
preferredOverlap = index;
bestSequence = overlap.Group.ModifiedSequence;
}
} }
} }
@@ -695,26 +700,38 @@ namespace Ryujinx.Graphics.Gpu.Image
// Search through the overlaps to find a compatible view and establish any copy dependencies. // Search through the overlaps to find a compatible view and establish any copy dependencies.
for (int index = 0; index < overlapsCount; index++) if (preferredOverlap != -1)
{ {
Texture overlap = _textureOverlaps[index]; Texture overlap = _textureOverlaps[preferredOverlap];
OverlapInfo oInfo = _overlapInfo[index]; OverlapInfo oInfo = _overlapInfo[preferredOverlap];
bool aliased = oInfo.Compatibility == TextureViewCompatibility.FormatAlias;
if (oInfo.Compatibility == TextureViewCompatibility.Full)
{
if (!isSamplerTexture) if (!isSamplerTexture)
{ {
// If this is not a sampler texture, the size might be different from the requested size, // If this is not a sampler texture, the size might be different from the requested size,
// so we need to make sure the texture information has the correct size for this base texture, // so we need to make sure the texture information has the correct size for this base texture,
// before creating the view. // before creating the view.
info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel);
info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel, aliased);
}
else if (aliased)
{
// The format must be changed to match the parent.
info = info.CreateInfoWithFormat(overlap.Info.FormatInfo);
} }
texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
texture.SynchronizeMemory(); texture.SynchronizeMemory();
break;
} }
else if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0) else
{
for (int index = 0; index < overlapsCount; index++)
{
Texture overlap = _textureOverlaps[index];
OverlapInfo oInfo = _overlapInfo[index];
if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0)
{ {
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead. // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
@@ -728,6 +745,7 @@ namespace Ryujinx.Graphics.Gpu.Image
break; break;
} }
} }
}
if (texture != null) if (texture != null)
{ {
@@ -740,7 +758,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Texture overlap = _textureOverlaps[index]; Texture overlap = _textureOverlaps[index];
OverlapInfo oInfo = _overlapInfo[index]; OverlapInfo oInfo = _overlapInfo[index];
if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible) if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible || oInfo.Compatibility == TextureViewCompatibility.FormatAlias)
{ {
if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility)) if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility))
{ {

View File

@@ -291,22 +291,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>The minimum compatibility level of two provided view compatibility results</returns> /// <returns>The minimum compatibility level of two provided view compatibility results</returns>
public static TextureViewCompatibility PropagateViewCompatibility(TextureViewCompatibility first, TextureViewCompatibility second) public static TextureViewCompatibility PropagateViewCompatibility(TextureViewCompatibility first, TextureViewCompatibility second)
{ {
if (first == TextureViewCompatibility.Incompatible || second == TextureViewCompatibility.Incompatible) return (TextureViewCompatibility)Math.Min((int)first, (int)second);
{
return TextureViewCompatibility.Incompatible;
}
else if (first == TextureViewCompatibility.LayoutIncompatible || second == TextureViewCompatibility.LayoutIncompatible)
{
return TextureViewCompatibility.LayoutIncompatible;
}
else if (first == TextureViewCompatibility.CopyOnly || second == TextureViewCompatibility.CopyOnly)
{
return TextureViewCompatibility.CopyOnly;
}
else
{
return TextureViewCompatibility.Full;
}
} }
/// <summary> /// <summary>
@@ -628,15 +613,21 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="lhs">Texture information of the texture view</param> /// <param name="lhs">Texture information of the texture view</param>
/// <param name="rhs">Texture information of the texture view</param> /// <param name="rhs">Texture information of the texture view</param>
/// <param name="caps">Host GPU capabilities</param> /// <param name="caps">Host GPU capabilities</param>
/// <param name="flags">Texture search flags</param>
/// <returns>The view compatibility level of the texture formats</returns> /// <returns>The view compatibility level of the texture formats</returns>
public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps) public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps, TextureSearchFlags flags)
{ {
FormatInfo lhsFormat = lhs.FormatInfo; FormatInfo lhsFormat = lhs.FormatInfo;
FormatInfo rhsFormat = rhs.FormatInfo; FormatInfo rhsFormat = rhs.FormatInfo;
if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil()) if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil())
{ {
return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; return FormatMatches(lhs, rhs, flags.HasFlag(TextureSearchFlags.ForSampler), flags.HasFlag(TextureSearchFlags.DepthAlias)) switch
{
TextureMatchQuality.Perfect => TextureViewCompatibility.Full,
TextureMatchQuality.FormatAlias => TextureViewCompatibility.FormatAlias,
_ => TextureViewCompatibility.Incompatible
};
} }
if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps)) if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps))
@@ -754,49 +745,6 @@ namespace Ryujinx.Graphics.Gpu.Image
return result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; return result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
} }
/// <summary>
/// Checks if a swizzle component in two textures functionally match, taking into account if the components are defined.
/// </summary>
/// <param name="lhs">Texture information to compare</param>
/// <param name="rhs">Texture information to compare with</param>
/// <param name="swizzleLhs">Swizzle component for the first texture</param>
/// <param name="swizzleRhs">Swizzle component for the second texture</param>
/// <param name="component">Component index, starting at 0 for red</param>
/// <returns>True if the swizzle components functionally match, false othersize</returns>
private static bool SwizzleComponentMatches(TextureInfo lhs, TextureInfo rhs, SwizzleComponent swizzleLhs, SwizzleComponent swizzleRhs, int component)
{
int lhsComponents = lhs.FormatInfo.Components;
int rhsComponents = rhs.FormatInfo.Components;
if (lhsComponents == 4 && rhsComponents == 4)
{
return swizzleLhs == swizzleRhs;
}
// Swizzles after the number of components a format defines are "undefined".
// We allow these to not be equal under certain circumstances.
// This can only happen when there are less than 4 components in a format.
// It tends to happen when float depth textures are sampled.
bool lhsDefined = (swizzleLhs - SwizzleComponent.Red) < lhsComponents;
bool rhsDefined = (swizzleRhs - SwizzleComponent.Red) < rhsComponents;
if (lhsDefined == rhsDefined)
{
// If both are undefined, return true. Otherwise just check if they're equal.
return lhsDefined ? swizzleLhs == swizzleRhs : true;
}
else
{
SwizzleComponent defined = lhsDefined ? swizzleLhs : swizzleRhs;
SwizzleComponent undefined = lhsDefined ? swizzleRhs : swizzleLhs;
// Undefined swizzle can be matched by a forced value (0, 1), exact equality, or expected value.
// For example, R___ matches R001, RGBA but not RBGA.
return defined == undefined || defined < SwizzleComponent.Red || defined == SwizzleComponent.Red + component;
}
}
/// <summary> /// <summary>
/// Checks if the texture shader sampling parameters of two texture informations match. /// Checks if the texture shader sampling parameters of two texture informations match.
/// </summary> /// </summary>
@@ -806,10 +754,10 @@ namespace Ryujinx.Graphics.Gpu.Image
public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs) public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs)
{ {
return lhs.DepthStencilMode == rhs.DepthStencilMode && return lhs.DepthStencilMode == rhs.DepthStencilMode &&
SwizzleComponentMatches(lhs, rhs, lhs.SwizzleR, rhs.SwizzleR, 0) && lhs.SwizzleR == rhs.SwizzleR &&
SwizzleComponentMatches(lhs, rhs, lhs.SwizzleG, rhs.SwizzleG, 1) && lhs.SwizzleG == rhs.SwizzleG &&
SwizzleComponentMatches(lhs, rhs, lhs.SwizzleB, rhs.SwizzleB, 2) && lhs.SwizzleB == rhs.SwizzleB &&
SwizzleComponentMatches(lhs, rhs, lhs.SwizzleA, rhs.SwizzleA, 3); lhs.SwizzleA == rhs.SwizzleA;
} }
/// <summary> /// <summary>

View File

@@ -68,6 +68,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0; public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0;
/// <summary>
/// Number indicating the order this texture group was modified relative to others.
/// </summary>
public long ModifiedSequence { get; private set; }
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory; private readonly PhysicalMemory _physicalMemory;
@@ -664,6 +669,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture that has been modified</param> /// <param name="texture">The texture that has been modified</param>
public void SignalModified(Texture texture) public void SignalModified(Texture texture)
{ {
ModifiedSequence = _context.GetModifiedSequence();
ClearIncompatibleOverlaps(texture); ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
@@ -684,6 +691,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="bound">True if this texture is being bound, false if unbound</param> /// <param name="bound">True if this texture is being bound, false if unbound</param>
public void SignalModifying(Texture texture, bool bound) public void SignalModifying(Texture texture, bool bound)
{ {
ModifiedSequence = _context.GetModifiedSequence();
ClearIncompatibleOverlaps(texture); ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>

View File

@@ -300,8 +300,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="parent">The parent texture</param> /// <param name="parent">The parent texture</param>
/// <param name="firstLevel">The first level of the texture view</param> /// <param name="firstLevel">The first level of the texture view</param>
/// <param name="parentFormat">True if the parent format should be inherited</param>
/// <returns>The adjusted texture information with the new size</returns> /// <returns>The adjusted texture information with the new size</returns>
public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel) public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel, bool parentFormat)
{ {
// When the texture is used as view of another texture, we must // When the texture is used as view of another texture, we must
// ensure that the sizes are valid, otherwise data uploads would fail // ensure that the sizes are valid, otherwise data uploads would fail
@@ -370,7 +371,36 @@ namespace Ryujinx.Graphics.Gpu.Image
GobBlocksInZ, GobBlocksInZ,
GobBlocksInTileX, GobBlocksInTileX,
target, target,
FormatInfo, parentFormat ? parent.Info.FormatInfo : FormatInfo,
DepthStencilMode,
SwizzleR,
SwizzleG,
SwizzleB,
SwizzleA);
}
/// <summary>
/// Creates texture information for a given format and this information.
/// </summary>
/// <param name="formatInfo">Format for the new texture info</param>
/// <returns>New info with the specified format</returns>
public TextureInfo CreateInfoWithFormat(FormatInfo formatInfo)
{
return new TextureInfo(
GpuAddress,
Width,
Height,
DepthOrLayers,
Levels,
SamplesInX,
SamplesInY,
Stride,
IsLinear,
GobBlocksInY,
GobBlocksInZ,
GobBlocksInTileX,
Target,
formatInfo,
DepthStencilMode, DepthStencilMode,
SwizzleR, SwizzleR,
SwizzleG, SwizzleG,

View File

@@ -9,6 +9,7 @@
Incompatible = 0, Incompatible = 0,
LayoutIncompatible, LayoutIncompatible,
CopyOnly, CopyOnly,
FormatAlias,
Full Full
} }
} }

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4821; private const uint CodeGenVersion = 4892;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

@@ -591,7 +591,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
return true; return true;
} }
ReadOnlySpan<byte> memoryCode = memoryManager.GetSpan(gpuVa, shader.Code.Length); ReadOnlySpan<byte> memoryCode = memoryManager.GetSpanMapped(gpuVa, shader.Code.Length);
return memoryCode.SequenceEqual(shader.Code); return memoryCode.SequenceEqual(shader.Code);
} }

View File

@@ -306,6 +306,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
int offset = WriteToPbo2D(range.Offset, layer, level); int offset = WriteToPbo2D(range.Offset, layer, level);
Debug.Assert(offset == 0); Debug.Assert(offset == 0);
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
} }
public void WriteToPbo(int offset, bool forceBgra) public void WriteToPbo(int offset, bool forceBgra)

View File

@@ -1442,14 +1442,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return GetZeroOperationResult(context, texOp, AggregateType.FP32, colorIsVector); return GetZeroOperationResult(context, texOp, AggregateType.FP32, colorIsVector);
} }
// This combination is valid, but not available on GLSL.
// For now, ignore the LOD level and do a normal sample.
// TODO: How to implement it properly?
if (hasLodLevel && isArray && isShadow)
{
hasLodLevel = false;
}
int srcIndex = isBindless ? 1 : 0; int srcIndex = isBindless ? 1 : 0;
SpvInstruction Src(AggregateType type) SpvInstruction Src(AggregateType type)

View File

@@ -956,7 +956,7 @@ namespace Ryujinx.Graphics.Texture.Astc
{ {
Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition);
int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0)); int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0));
int l1 = (int)Math.Max(l0 + (val[1] & 0x3F), 0xFFU); int l1 = (int)Math.Min(l0 + (val[1] & 0x3F), 0xFFU);
endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0); endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0);
endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1); endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1);

View File

@@ -56,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
private int _writeCount; private int _writeCount;
private int _flushCount; private int _flushCount;
private int _flushTemp; private int _flushTemp;
private int _lastFlushWrite = -1;
private ReaderWriterLock _flushLock; private ReaderWriterLock _flushLock;
private FenceHolder _flushFence; private FenceHolder _flushFence;
@@ -200,14 +201,21 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (_baseType == BufferAllocationType.Auto) if (_baseType == BufferAllocationType.Auto)
{ {
if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold) // When flushed, wait for a bit more info to make a decision.
bool wasFlushed = _flushTemp > 0;
int multiplier = wasFlushed ? 2 : 0;
if (_writeCount >= (WriteCountThreshold << multiplier) || _setCount >= (SetCountThreshold << multiplier) || _flushCount >= (FlushCountThreshold << multiplier))
{ {
if (_flushCount > 0 || _flushTemp-- > 0) if (_flushCount > 0 || _flushTemp-- > 0)
{ {
// Buffers that flush should ideally be mapped in host address space for easy copies. // Buffers that flush should ideally be mapped in host address space for easy copies.
// If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages). // If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages).
// If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached. // If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached.
DesiredType = Size > DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped;
bool hostMappingSensitive = _gd.Vendor == Vendor.Nvidia;
bool deviceLocalMapped = Size > DeviceLocalSizeThreshold || (wasFlushed && _writeCount > _flushCount * 10 && hostMappingSensitive) || _currentType == BufferAllocationType.DeviceLocalMapped;
DesiredType = deviceLocalMapped ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped;
// It's harder for a buffer that is flushed to revert to another type of mapping. // It's harder for a buffer that is flushed to revert to another type of mapping.
if (_flushCount > 0) if (_flushCount > 0)
@@ -215,17 +223,18 @@ namespace Ryujinx.Graphics.Vulkan
_flushTemp = 1000; _flushTemp = 1000;
} }
} }
else if (_writeCount >= WriteCountThreshold) else if (_writeCount >= (WriteCountThreshold << multiplier))
{ {
// Buffers that are written often should ideally be in the device local heap. (Storage buffers) // Buffers that are written often should ideally be in the device local heap. (Storage buffers)
DesiredType = BufferAllocationType.DeviceLocal; DesiredType = BufferAllocationType.DeviceLocal;
} }
else if (_setCount > SetCountThreshold) else if (_setCount > (SetCountThreshold << multiplier))
{ {
// Buffers that have their data set often should ideally be host mapped. (Constant buffers) // Buffers that have their data set often should ideally be host mapped. (Constant buffers)
DesiredType = BufferAllocationType.HostMapped; DesiredType = BufferAllocationType.HostMapped;
} }
_lastFlushWrite = -1;
_flushCount = 0; _flushCount = 0;
_writeCount = 0; _writeCount = 0;
_setCount = 0; _setCount = 0;
@@ -418,7 +427,12 @@ namespace Ryujinx.Graphics.Vulkan
WaitForFlushFence(); WaitForFlushFence();
if (_lastFlushWrite != _writeCount)
{
// If it's on the same page as the last flush, ignore it.
_lastFlushWrite = _writeCount;
_flushCount++; _flushCount++;
}
Span<byte> result; Span<byte> result;

View File

@@ -12,6 +12,28 @@ namespace Ryujinx.Graphics.Vulkan
ShaderStageFlags.FragmentBit | ShaderStageFlags.FragmentBit |
ShaderStageFlags.ComputeBit; ShaderStageFlags.ComputeBit;
private static ShaderStageFlags ActiveStages(uint stages)
{
ShaderStageFlags stageFlags = 0;
while (stages != 0)
{
int stage = BitOperations.TrailingZeroCount(stages);
stages &= ~(1u << stage);
stageFlags |= stage switch
{
1 => ShaderStageFlags.FragmentBit,
2 => ShaderStageFlags.GeometryBit,
3 => ShaderStageFlags.TessellationControlBit,
4 => ShaderStageFlags.TessellationEvaluationBit,
_ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit
};
}
return stageFlags;
}
public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout) public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout)
{ {
int stagesCount = BitOperations.PopCount(stages); int stagesCount = BitOperations.PopCount(stages);
@@ -34,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
}; };
int iter = 0; int iter = 0;
var activeStages = ActiveStages(stages);
while (stages != 0) while (stages != 0)
{ {
@@ -67,12 +90,16 @@ namespace Ryujinx.Graphics.Vulkan
void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0) void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0)
{ {
// There's a bug on MoltenVK where using the same buffer across different stages
// causes invalid resource errors, allow the binding on all active stages as workaround.
var flags = gd.IsMoltenVk ? activeStages : stageFlags;
bindings[start + iter] = new DescriptorSetLayoutBinding bindings[start + iter] = new DescriptorSetLayoutBinding
{ {
Binding = (uint)(start + stage * maxPerStage), Binding = (uint)(start + stage * maxPerStage),
DescriptorType = DescriptorType.StorageBuffer, DescriptorType = DescriptorType.StorageBuffer,
DescriptorCount = (uint)maxPerStage, DescriptorCount = (uint)maxPerStage,
StageFlags = stageFlags StageFlags = flags
}; };
} }

View File

@@ -261,7 +261,7 @@ namespace Ryujinx.HLE.HOS
AudioInputManager = new AudioInputManager(); AudioInputManager = new AudioInputManager();
AudioRendererManager = new AudioRendererManager(tickSource); AudioRendererManager = new AudioRendererManager(tickSource);
AudioRendererManager.SetVolume(Device.Configuration.AudioVolume); AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry(); AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry(Device.AudioDeviceDriver);
IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax]; IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax];

View File

@@ -63,6 +63,7 @@ namespace Ryujinx.SDL2.Common
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
// NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them. // NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them.

View File

@@ -10,7 +10,9 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using System; using System;
using System.Globalization;
using System.IO; using System.IO;
using System.Text.Json.Serialization;
namespace Ryujinx.Ui.App.Common namespace Ryujinx.Ui.App.Common
{ {
@@ -24,13 +26,28 @@ namespace Ryujinx.Ui.App.Common
public string Version { get; set; } public string Version { get; set; }
public string TimePlayed { get; set; } public string TimePlayed { get; set; }
public double TimePlayedNum { get; set; } public double TimePlayedNum { get; set; }
public string LastPlayed { get; set; } public DateTime? LastPlayed { get; set; }
public string FileExtension { get; set; } public string FileExtension { get; set; }
public string FileSize { get; set; } public string FileSize { get; set; }
public double FileSizeBytes { get; set; } public double FileSizeBytes { get; set; }
public string Path { get; set; } public string Path { get; set; }
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; } public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
[JsonIgnore]
public string LastPlayedString
{
get
{
if (!LastPlayed.HasValue)
{
// TODO: maybe put localized string here instead of just "Never"
return "Never";
}
return LastPlayed.Value.ToLocalTime().ToString(CultureInfo.CurrentCulture);
}
}
public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath) public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath)
{ {
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);

View File

@@ -414,21 +414,28 @@ namespace Ryujinx.Ui.App.Common
ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata => ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata =>
{ {
appMetadata.Title = titleName; appMetadata.Title = titleName;
});
if (appMetadata.LastPlayed != "Never") if (appMetadata.LastPlayedOld == default || appMetadata.LastPlayed.HasValue)
{ {
if (!DateTime.TryParse(appMetadata.LastPlayed, out _)) // Don't do the migration if last_played doesn't exist or last_played_utc already has a value.
{ return;
Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); }
appMetadata.LastPlayed = "Never"; // Migrate from string-based last_played to DateTime-based last_played_utc.
if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed))
{
Logger.Info?.Print(LogClass.Application, $"last_played found: \"{appMetadata.LastPlayedOld}\", migrating to last_played_utc");
appMetadata.LastPlayed = lastPlayedOldParsed;
// Migration successful: deleting last_played from the metadata file.
appMetadata.LastPlayedOld = default;
} }
else else
{ {
appMetadata.LastPlayed = appMetadata.LastPlayed[..^3]; // Migration failed: emitting warning but leaving the unparsable value in the metadata file so the user can fix it.
} Logger.Warning?.Print(LogClass.Application, $"Last played string \"{appMetadata.LastPlayedOld}\" is invalid for current system culture, skipping (did current culture change?)");
} }
});
ApplicationData data = new() ApplicationData data = new()
{ {

View File

@@ -1,10 +1,19 @@
namespace Ryujinx.Ui.App.Common using System;
using System.Text.Json.Serialization;
namespace Ryujinx.Ui.App.Common
{ {
public class ApplicationMetadata public class ApplicationMetadata
{ {
public string Title { get; set; } public string Title { get; set; }
public bool Favorite { get; set; } public bool Favorite { get; set; }
public double TimePlayed { get; set; } public double TimePlayed { get; set; }
public string LastPlayed { get; set; } = "Never";
[JsonPropertyName("last_played_utc")]
public DateTime? LastPlayed { get; set; } = null;
[JsonPropertyName("last_played")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string LastPlayedOld { get; set; }
} }
} }

View File

@@ -1,5 +1,6 @@
using Gdk; using Gdk;
using Gtk; using Gtk;
using Ryujinx.Common;
using Ryujinx.Ui; using Ryujinx.Ui;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
@@ -47,9 +48,19 @@ namespace Ryujinx.Modules
if (_restartQuery) if (_restartQuery)
{ {
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
Process.Start(ryuExe, CommandLineState.Arguments); ProcessStartInfo processStart = new(ryuName)
{
UseShellExecute = true,
WorkingDirectory = ReleaseInformation.GetBaseApplicationDirectory()
};
foreach (string argument in CommandLineState.Arguments)
{
processStart.ArgumentList.Add(argument);
}
Process.Start(processStart);
Environment.Exit(0); Environment.Exit(0);
} }

View File

@@ -876,7 +876,7 @@ namespace Ryujinx.Ui
_applicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata => _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata =>
{ {
appMetadata.LastPlayed = DateTime.UtcNow.ToString(); appMetadata.LastPlayed = DateTime.UtcNow;
}); });
} }
} }
@@ -1019,10 +1019,11 @@ namespace Ryujinx.Ui
{ {
_applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => _applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
{ {
DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed); if (appMetadata.LastPlayed.HasValue)
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; {
double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
}
}); });
} }
} }
@@ -1089,7 +1090,7 @@ namespace Ryujinx.Ui
args.AppData.Developer, args.AppData.Developer,
args.AppData.Version, args.AppData.Version,
args.AppData.TimePlayed, args.AppData.TimePlayed,
args.AppData.LastPlayed, args.AppData.LastPlayedString,
args.AppData.FileExtension, args.AppData.FileExtension,
args.AppData.FileSize, args.AppData.FileSize,
args.AppData.Path, args.AppData.Path,

View File

@@ -321,13 +321,12 @@ namespace Ryujinx.Ui
_toggleDockedMode = toggleDockedMode; _toggleDockedMode = toggleDockedMode;
if (ConfigurationState.Instance.Hid.EnableMouse.Value)
{
if (_isMouseInClient) if (_isMouseInClient)
{
if (ConfigurationState.Instance.Hid.EnableMouse.Value)
{ {
Window.Cursor = _invisibleCursor; Window.Cursor = _invisibleCursor;
} }
}
else else
{ {
switch (_hideCursorMode) switch (_hideCursorMode)
@@ -345,6 +344,7 @@ namespace Ryujinx.Ui
} }
} }
} }
}
public void Initialize(Switch device) public void Initialize(Switch device)
{ {

View File

@@ -180,7 +180,7 @@ namespace Ryujinx.Ui.Windows
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
return response.Content.Headers.LastModified != oldLastModified; return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero);
} }
return false; return false;