Compare commits

..

46 Commits

Author SHA1 Message Date
fc26189fe1 Eliminate redundant multiplications by gl_FragCoord.w on the shader (#4578)
* Eliminate redundant multiplications by gl_FragCoord.w on the shader

* Shader cache version bump
2023-05-19 11:52:31 -03:00
a40c90e7dd nuget: bump DynamicData from 7.13.5 to 7.13.8 (#5001)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.5 to 7.13.8.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.5...7.13.8)

---
updated-dependencies:
- dependency-name: DynamicData
  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-19 06:52:44 +02:00
f864a49014 Fix Vulkan blit-like operations swizzle (#5003) 2023-05-18 18:16:03 -03:00
ecbf303266 GPU: Avoid using garbage size for non-cb0 storage buffers (#4999)
* GPU: Avoid using garbage size for non-cb0 storage buffers

In the depths area, Tears of the Kingdom uses a global memory access with address on constant buffer slot 6. This isn't standard and thus doesn't actually have a size 8 bytes after it, so we were reading back a garbage size that ended up very large (at least in version 1.1.0), and would synchronize a lot of data per frame.

This PR makes storage buffers created from addresses outside constant buffer slot 0 get their size as the number of bytes remaining in the GPU mapping starting at the given virtual address. This should bound the buffer to a reasonable size, and ideally stop it crossing into other memory.

* Limit max size

* Add TODO

* Feedback
2023-05-18 08:56:34 +02:00
b3bf05356b ava: Fix crash when extracting sections from NCA with no data section (#5002)
Tested against Omega Strickers.
2023-05-17 19:27:49 +00:00
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
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
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
17ba217940 Vulkan: Device map buffers written more than flushed (#4911) 2023-05-13 15:15:05 +02:00
aae4595bdb Add timeout of 35 minutes to workflow jobs (#4928) 2023-05-13 13:24:43 +02:00
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
f679f25e08 Set OpenGL PixelPackBuffer to 0 when done (#4921) 2023-05-13 00:59:46 +00:00
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
2b6e81deea Ava: Fix wrong MouseButton (#4900) 2023-05-12 21:39:42 +02:00
7271f1b18e Bump shader cache codegen version
That was missing from #4892
2023-05-12 18:53:14 +02:00
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
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
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
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
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
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
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
9205077590 Enable explicit LOD for array textures with depth compare on SPIR-V (#4892) 2023-05-11 21:35:36 +02:00
0ed40c7175 Fix incorrect ASTC endpoint color when using LuminanceDelta mode (#4890) 2023-05-11 20:47:55 +02:00
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
ec0bb74968 Stop SDL from inhibiting sleep. (#4842) 2023-05-11 20:13:01 +02:00
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
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
3d42995822 Attempt to fix release.yml after merge 2023-05-11 17:42:33 +02:00
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
ba71141bdc Ensure background translation threads exited before disposing JIT (#4874) 2023-05-10 21:46:38 -03:00
0a0675a7f6 Fix missing domain service object dispose (#4879) 2023-05-11 00:29:17 +00:00
a7c6e6a8cf fix(mvk): resumeLostDevice (#4800)
Command buffer errors currently trigger an exception "DeviceLost" crashing the process.

Looking at [MKV's code](53a4eb26f2/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm (L392-L408)) we observe that:
- It hard fails if error is:
  ```
   MTLCommandBufferErrorBlacklisted || MTLCommandBufferErrorNotPermitted || MTLCommandBufferErrorDeviceRemoved
   ```
- Otherwise fails conditionally if `config.resumeLostDevice == false` (current default)

For Ryujinx's use-case it's more graceful to resume on those errors rather than crashing the app, the error isn't totally silenced since `mvk` still logs it

Fixes #4704, #4575
2023-05-10 03:31:52 +02:00
0bc8151c7e IPC - Refactor Bcat service to use new ipc - Revisit (#4803)
* bcat ipc

* fix hipc buffer flags

* add buffer fixed size flag on generator
2023-05-09 21:46:23 +00:00
40c17673f5 Replace DelegateHelper with pre-generated delegates (#4867) 2023-05-09 09:02:39 +00:00
a8950d6ac4 vulkan: Pass Vk instance to VulkanRenderer (#4859)
This will allow possible multiple driver selection without any need of
LD preload. (useful when testing custom version of mesa for example)
2023-05-08 13:05:37 +02:00
162798b026 vulkan: Avoid hardcoding features in CreateDevice (#4858)
Those shouldn't have been hardcoded.
2023-05-08 10:48:16 +00:00
1b28ecd63e Vulkan: Simplify MultiFenceHolder and managing them (#4845)
* Vulkan: Simplify waitable add/remove

Removal of unnecessary hashset and dictionary

* Thread safety for GetBufferData in PersistentFlushBuffer

* Fix WaitForFencesImpl thread safety

* Proper methods for risky reference increments

* Wrong type of CB.

* Address feedback
2023-05-08 12:45:12 +02:00
895d9b53bc Vulkan: Batch vertex buffer updates (#4843)
* Vulkan: Batch vertex buffer updates

Some games can bind a large number of vertex buffers for draws. This PR allows for vertex buffers to be updated with one call rather than one per buffer.

This mostly affects the AMD Mesa driver, the testing platform was Steam Deck with Super Mario Odyssey. It was taking about 12% before, should be greatly reduced now.

A small optimization has been added to avoid looking up the same buffer multiple times, as a common pattern is for the same buffer to be bound many times in a row with different ranges.

* Only rebind vertex buffers if they have changed

* Address feedback
2023-05-08 10:59:26 +02:00
0e06aace45 misc: Avoid copy of ApplicationControlProperty (#4849)
Avoid more giant copy when passing it around.
2023-05-08 01:50:07 +02:00
adf4ebcd60 Ava: Fix SystemTimeOffset calculation (#4848)
* Ava: Fix SystemTimeOffset calculation

During testing of #4822, Mary pointed out the way we calculate time offset is wrong in our Avalonia UI. This PR fixed that.
The axaml file is autoformatted too.

* DateTime.Now in local var
2023-05-08 00:31:08 +02:00
470a8031a4 time: Update for 15.0.0 changes and fixes long standing issues (#4822)
* time: Update for 15.0.0 changes

Last time we did an upgrade on the time service was during 9.x era, it was about time to take back that reverse again!

15.0.0 added a new structure on the shared memory to get steady clock raw timepoints with a granularity in nanoseconds.

This commit implements this new part.

I plan to write a follow up with a bit of refactoring of this ancient part of the emulator.

As always, reverse and work done by your truly.

PS: As a reminder, if this change is reused anywhere else, work should be credited as Ryujinx and not my person.

* time: Do not set setup value to posix time

This should fix local and network clock returning 0 under usage with
shared memory.

This probably fix #2430.

* Address gdkchan's comment

* Fix internal offset not working since changes and ensure that user clock have a valid clock id

* time: Report auto correcting clock and hardcode steady clock unique id

Fix Pokemon Sword Pokejobs for real.

* Address gdkchan's comment
2023-05-08 00:15:58 +02:00
5440d4ad5c misc: Switch ProcessResult to a class (#4846)
This avoid giant copies being performed when being returned or passed.
2023-05-07 20:50:45 +00:00
dde208b480 UI: Expose games build ID for cheat management (#4340)
* Ava UI: Expose games build ID for cheat management

* Fix bad merge

* Change integrity check level to error on invalid

* Add support for GDK

* Remove whitespace

* Add BID identifier

* PR Comments fix

* Restore title id in cheats GTK window

* use halign center instead of margin_left

* Merge

* fix after merge

* PR comments fix - design AVA

* PR fix - Move GetApplicationBuildId to ApplicationData class

* PR comment fix - Add empty line before method

* Align with PR #4755

* PR comments fix

* Change BuildId label to support translation

* Comments fix

* Remove unused BuildIdLabel property
2023-05-07 14:36:44 +00:00
4c3d2d5d75 UI: Add progress bar for re-packaging shaders (#4805)
* feat: introduce new shader loading state for progress tracking when writing shaders to disk

* fix: move translation to bottom of locale file

* fix: change back to foreach and add requested spacing between lines

* style: fix formatting

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-05-06 15:35:46 +02:00
fab11ba3f1 AM: Stub some service calls (#4825)
* AM: Stub some service call

Some IPC I have stubbed during private testing and I don't want to deal with them anymore. Nothing more.

* ICommonStateGetter disposable
2023-05-06 03:33:50 +02:00
125 changed files with 2918 additions and 1654 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:
@ -65,4 +66,4 @@ jobs:
} else { } else {
core.info(`Creating a comment`); core.info(`Creating a comment`);
await github.rest.issues.createComment({repo, owner, issue_number, body}); await github.rest.issues.createComment({repo, owner, issue_number, body});
} }

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

@ -13,7 +13,7 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageVersion Include="DynamicData" Version="7.13.5" /> <PackageVersion Include="DynamicData" Version="7.13.8" />
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
@ -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

@ -39,7 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory.Tests", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "src\Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "src\Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}"
EndProject EndProject
@ -256,4 +256,4 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A} SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

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

@ -1,104 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace ARMeilleure.Translation
{
static class DelegateHelper
{
private const string DelegateTypesAssemblyName = "JitDelegateTypes";
private static readonly ModuleBuilder _modBuilder;
private static readonly Dictionary<string, Type> _delegateTypesCache;
static DelegateHelper()
{
AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run);
_modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
_delegateTypesCache = new Dictionary<string, Type>();
}
public static Delegate GetDelegate(MethodInfo info)
{
ArgumentNullException.ThrowIfNull(info);
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
Type returnType = info.ReturnType;
Type delegateType = GetDelegateType(parameters, returnType);
return Delegate.CreateDelegate(delegateType, info);
}
private static Type GetDelegateType(Type[] parameters, Type returnType)
{
string key = GetFunctionSignatureKey(parameters, returnType);
if (!_delegateTypesCache.TryGetValue(key, out Type delegateType))
{
delegateType = MakeDelegateType(parameters, returnType, key);
_delegateTypesCache.TryAdd(key, delegateType);
}
return delegateType;
}
private static string GetFunctionSignatureKey(Type[] parameters, Type returnType)
{
string sig = GetTypeName(returnType);
foreach (Type type in parameters)
{
sig += '_' + GetTypeName(type);
}
return sig;
}
private static string GetTypeName(Type type)
{
return type.FullName.Replace(".", string.Empty);
}
private const MethodAttributes CtorAttributes =
MethodAttributes.RTSpecialName |
MethodAttributes.HideBySig |
MethodAttributes.Public;
private const TypeAttributes DelegateTypeAttributes =
TypeAttributes.Class |
TypeAttributes.Public |
TypeAttributes.Sealed |
TypeAttributes.AnsiClass |
TypeAttributes.AutoClass;
private const MethodImplAttributes ImplAttributes =
MethodImplAttributes.Runtime |
MethodImplAttributes.Managed;
private const MethodAttributes InvokeAttributes =
MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.NewSlot |
MethodAttributes.Virtual;
private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) };
private static Type MakeDelegateType(Type[] parameters, Type returnType, string name)
{
TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate));
builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes);
builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);
return builder.CreateTypeInfo();
}
}
}

View File

@ -1,4 +1,5 @@
using ARMeilleure.Instructions; using ARMeilleure.Instructions;
using ARMeilleure.State;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
@ -63,11 +64,9 @@ namespace ARMeilleure.Translation
return index; return index;
} }
private static void SetDelegateInfo(MethodInfo info) private static void SetDelegateInfo(Delegate dlg)
{ {
string key = GetKey(info); string key = GetKey(dlg.Method);
Delegate dlg = DelegateHelper.GetDelegate(info);
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key). _delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
} }
@ -83,179 +82,354 @@ namespace ARMeilleure.Translation
{ {
_delegates = new SortedList<string, DelegateInfo>(); _delegates = new SortedList<string, DelegateInfo>();
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) })); SetDelegateInfo(new MathAbs(Math.Abs));
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) })); SetDelegateInfo(new MathCeiling(Math.Ceiling));
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) })); SetDelegateInfo(new MathFloor(Math.Floor));
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) })); SetDelegateInfo(new MathRound(Math.Round));
SetDelegateInfo(typeof(Math).GetMethod(nameof(Math.Truncate), new Type[] { typeof(double) })); SetDelegateInfo(new MathTruncate(Math.Truncate));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) })); SetDelegateInfo(new MathFAbs(MathF.Abs));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Ceiling), new Type[] { typeof(float) })); SetDelegateInfo(new MathFCeiling(MathF.Ceiling));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) })); SetDelegateInfo(new MathFFloor(MathF.Floor));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) })); SetDelegateInfo(new MathFRound(MathF.Round));
SetDelegateInfo(typeof(MathF).GetMethod(nameof(MathF.Truncate), new Type[] { typeof(float) })); SetDelegateInfo(new MathFTruncate(MathF.Truncate));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break))); SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization))); SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit))); SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0))); SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0))); SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0))); SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0))); SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0))); SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress))); SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine))); SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte))); SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16))); SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32))); SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64))); SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128))); SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking))); SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall))); SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess))); SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined))); SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte))); SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16))); SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32))); SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64))); SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128))); SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns))); SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros))); SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b))); SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb))); SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch))); SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw))); SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx))); SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h))); SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w))); SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x))); SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt))); SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt))); SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate))); SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose))); SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower))); SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority))); SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity))); SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper))); SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns))); SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns))); SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128))); SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32))); SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64))); SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32))); SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64))); SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32))); SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64))); SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32))); SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64))); SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1))); SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2))); SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1))); SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2))); SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64))); SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1))); SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2))); SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3))); SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4))); SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1))); SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2))); SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3))); SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4))); SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64))); SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64));
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert))); SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert));
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))); SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd))); SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare))); SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ))); SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE))); SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT))); SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE))); SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT))); SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv))); SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax))); SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum))); SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin))); SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum))); SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul))); SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd))); SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub))); SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX))); SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd))); SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub))); SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate))); SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only. SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused))); SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX))); SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate))); SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only. SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only. SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused))); SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt))); SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub))); SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub));
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert))); SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd))); SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare))); SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ))); SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE))); SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT))); SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE))); SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT))); SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv))); SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax))); SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum))); SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin))); SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum))); SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul))); SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd))); SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub))); SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX))); SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd))); SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub))); SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate))); SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only. SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused))); SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX))); SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate))); SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only. SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only. SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused))); SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt))); SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub))); SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub));
SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))); SetDelegateInfo(new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert));
} }
private delegate double MathAbs(double value);
private delegate double MathCeiling(double a);
private delegate double MathFloor(double d);
private delegate double MathRound(double value, MidpointRounding mode);
private delegate double MathTruncate(double d);
private delegate float MathFAbs(float x);
private delegate float MathFCeiling(float x);
private delegate float MathFFloor(float x);
private delegate float MathFRound(float x, MidpointRounding mode);
private delegate float MathFTruncate(float x);
private delegate void NativeInterfaceBreak(ulong address, int imm);
private delegate bool NativeInterfaceCheckSynchronization();
private delegate void NativeInterfaceEnqueueForRejit(ulong address);
private delegate ulong NativeInterfaceGetCntfrqEl0();
private delegate ulong NativeInterfaceGetCntpctEl0();
private delegate ulong NativeInterfaceGetCntvctEl0();
private delegate ulong NativeInterfaceGetCtrEl0();
private delegate ulong NativeInterfaceGetDczidEl0();
private delegate ulong NativeInterfaceGetFunctionAddress(ulong address);
private delegate void NativeInterfaceInvalidateCacheLine(ulong address);
private delegate byte NativeInterfaceReadByte(ulong address);
private delegate ushort NativeInterfaceReadUInt16(ulong address);
private delegate uint NativeInterfaceReadUInt32(ulong address);
private delegate ulong NativeInterfaceReadUInt64(ulong address);
private delegate V128 NativeInterfaceReadVector128(ulong address);
private delegate void NativeInterfaceSignalMemoryTracking(ulong address, ulong size, bool write);
private delegate void NativeInterfaceSupervisorCall(ulong address, int imm);
private delegate void NativeInterfaceThrowInvalidMemoryAccess(ulong address);
private delegate void NativeInterfaceUndefined(ulong address, int opCode);
private delegate void NativeInterfaceWriteByte(ulong address, byte value);
private delegate void NativeInterfaceWriteUInt16(ulong address, ushort value);
private delegate void NativeInterfaceWriteUInt32(ulong address, uint value);
private delegate void NativeInterfaceWriteUInt64(ulong address, ulong value);
private delegate void NativeInterfaceWriteVector128(ulong address, V128 value);
private delegate ulong SoftFallbackCountLeadingSigns(ulong value, int size);
private delegate ulong SoftFallbackCountLeadingZeros(ulong value, int size);
private delegate uint SoftFallbackCrc32b(uint crc, byte value);
private delegate uint SoftFallbackCrc32cb(uint crc, byte value);
private delegate uint SoftFallbackCrc32ch(uint crc, ushort value);
private delegate uint SoftFallbackCrc32cw(uint crc, uint value);
private delegate uint SoftFallbackCrc32cx(uint crc, ulong value);
private delegate uint SoftFallbackCrc32h(uint crc, ushort value);
private delegate uint SoftFallbackCrc32w(uint crc, uint value);
private delegate uint SoftFallbackCrc32x(uint crc, ulong value);
private delegate V128 SoftFallbackDecrypt(V128 value, V128 roundKey);
private delegate V128 SoftFallbackEncrypt(V128 value, V128 roundKey);
private delegate uint SoftFallbackFixedRotate(uint hash_e);
private delegate V128 SoftFallbackHashChoose(V128 hash_abcd, uint hash_e, V128 wk);
private delegate V128 SoftFallbackHashLower(V128 hash_abcd, V128 hash_efgh, V128 wk);
private delegate V128 SoftFallbackHashMajority(V128 hash_abcd, uint hash_e, V128 wk);
private delegate V128 SoftFallbackHashParity(V128 hash_abcd, uint hash_e, V128 wk);
private delegate V128 SoftFallbackHashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk);
private delegate V128 SoftFallbackInverseMixColumns(V128 value);
private delegate V128 SoftFallbackMixColumns(V128 value);
private delegate V128 SoftFallbackPolynomialMult64_128(ulong op1, ulong op2);
private delegate int SoftFallbackSatF32ToS32(float value);
private delegate long SoftFallbackSatF32ToS64(float value);
private delegate uint SoftFallbackSatF32ToU32(float value);
private delegate ulong SoftFallbackSatF32ToU64(float value);
private delegate int SoftFallbackSatF64ToS32(double value);
private delegate long SoftFallbackSatF64ToS64(double value);
private delegate uint SoftFallbackSatF64ToU32(double value);
private delegate ulong SoftFallbackSatF64ToU64(double value);
private delegate V128 SoftFallbackSha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11);
private delegate V128 SoftFallbackSha1SchedulePart2(V128 tw0_3, V128 w12_15);
private delegate V128 SoftFallbackSha256SchedulePart1(V128 w0_3, V128 w4_7);
private delegate V128 SoftFallbackSha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15);
private delegate long SoftFallbackSignedShrImm64(long value, long roundConst, int shift);
private delegate V128 SoftFallbackTbl1(V128 vector, int bytes, V128 tb0);
private delegate V128 SoftFallbackTbl2(V128 vector, int bytes, V128 tb0, V128 tb1);
private delegate V128 SoftFallbackTbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2);
private delegate V128 SoftFallbackTbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3);
private delegate V128 SoftFallbackTbx1(V128 dest, V128 vector, int bytes, V128 tb0);
private delegate V128 SoftFallbackTbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1);
private delegate V128 SoftFallbackTbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2);
private delegate V128 SoftFallbackTbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3);
private delegate ulong SoftFallbackUnsignedShrImm64(ulong value, long roundConst, int shift);
private delegate float SoftFloat16_32FPConvert(ushort valueBits);
private delegate double SoftFloat16_64FPConvert(ushort valueBits);
private delegate float SoftFloat32FPAdd(float value1, float value2);
private delegate float SoftFloat32FPAddFpscr(float value1, float value2, bool standardFpscr);
private delegate int SoftFloat32FPCompare(float value1, float value2, bool signalNaNs);
private delegate float SoftFloat32FPCompareEQ(float value1, float value2);
private delegate float SoftFloat32FPCompareEQFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPCompareGE(float value1, float value2);
private delegate float SoftFloat32FPCompareGEFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPCompareGT(float value1, float value2);
private delegate float SoftFloat32FPCompareGTFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPCompareLE(float value1, float value2);
private delegate float SoftFloat32FPCompareLEFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPCompareLT(float value1, float value2);
private delegate float SoftFloat32FPCompareLTFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPDiv(float value1, float value2);
private delegate float SoftFloat32FPMax(float value1, float value2);
private delegate float SoftFloat32FPMaxFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMaxNum(float value1, float value2);
private delegate float SoftFloat32FPMaxNumFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMin(float value1, float value2);
private delegate float SoftFloat32FPMinFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMinNum(float value1, float value2);
private delegate float SoftFloat32FPMinNumFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMul(float value1, float value2);
private delegate float SoftFloat32FPMulFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMulAdd(float valueA, float value1, float value2);
private delegate float SoftFloat32FPMulAddFpscr(float valueA, float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMulSub(float valueA, float value1, float value2);
private delegate float SoftFloat32FPMulSubFpscr(float valueA, float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMulX(float value1, float value2);
private delegate float SoftFloat32FPNegMulAdd(float valueA, float value1, float value2);
private delegate float SoftFloat32FPNegMulSub(float valueA, float value1, float value2);
private delegate float SoftFloat32FPRecipEstimate(float value);
private delegate float SoftFloat32FPRecipEstimateFpscr(float value, bool standardFpscr);
private delegate float SoftFloat32FPRecipStep(float value1, float value2);
private delegate float SoftFloat32FPRecipStepFused(float value1, float value2);
private delegate float SoftFloat32FPRecpX(float value);
private delegate float SoftFloat32FPRSqrtEstimate(float value);
private delegate float SoftFloat32FPRSqrtEstimateFpscr(float value, bool standardFpscr);
private delegate float SoftFloat32FPRSqrtStep(float value1, float value2);
private delegate float SoftFloat32FPRSqrtStepFused(float value1, float value2);
private delegate float SoftFloat32FPSqrt(float value);
private delegate float SoftFloat32FPSub(float value1, float value2);
private delegate ushort SoftFloat32_16FPConvert(float value);
private delegate double SoftFloat64FPAdd(double value1, double value2);
private delegate double SoftFloat64FPAddFpscr(double value1, double value2, bool standardFpscr);
private delegate int SoftFloat64FPCompare(double value1, double value2, bool signalNaNs);
private delegate double SoftFloat64FPCompareEQ(double value1, double value2);
private delegate double SoftFloat64FPCompareEQFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPCompareGE(double value1, double value2);
private delegate double SoftFloat64FPCompareGEFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPCompareGT(double value1, double value2);
private delegate double SoftFloat64FPCompareGTFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPCompareLE(double value1, double value2);
private delegate double SoftFloat64FPCompareLEFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPCompareLT(double value1, double value2);
private delegate double SoftFloat64FPCompareLTFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPDiv(double value1, double value2);
private delegate double SoftFloat64FPMax(double value1, double value2);
private delegate double SoftFloat64FPMaxFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMaxNum(double value1, double value2);
private delegate double SoftFloat64FPMaxNumFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMin(double value1, double value2);
private delegate double SoftFloat64FPMinFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMinNum(double value1, double value2);
private delegate double SoftFloat64FPMinNumFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMul(double value1, double value2);
private delegate double SoftFloat64FPMulFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMulAdd(double valueA, double value1, double value2);
private delegate double SoftFloat64FPMulAddFpscr(double valueA, double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMulSub(double valueA, double value1, double value2);
private delegate double SoftFloat64FPMulSubFpscr(double valueA, double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMulX(double value1, double value2);
private delegate double SoftFloat64FPNegMulAdd(double valueA, double value1, double value2);
private delegate double SoftFloat64FPNegMulSub(double valueA, double value1, double value2);
private delegate double SoftFloat64FPRecipEstimate(double value);
private delegate double SoftFloat64FPRecipEstimateFpscr(double value, bool standardFpscr);
private delegate double SoftFloat64FPRecipStep(double value1, double value2);
private delegate double SoftFloat64FPRecipStepFused(double value1, double value2);
private delegate double SoftFloat64FPRecpX(double value);
private delegate double SoftFloat64FPRSqrtEstimate(double value);
private delegate double SoftFloat64FPRSqrtEstimateFpscr(double value, bool standardFpscr);
private delegate double SoftFloat64FPRSqrtStep(double value1, double value2);
private delegate double SoftFloat64FPRSqrtStepFused(double value1, double value2);
private delegate double SoftFloat64FPSqrt(double value);
private delegate double SoftFloat64FPSub(double value1, double value2);
private delegate ushort SoftFloat64_16FPConvert(double value);
} }
} }

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

@ -35,6 +35,7 @@ using Ryujinx.Input.HLE;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using Silk.NET.Vulkan;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -85,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;
@ -159,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())
{ {
@ -182,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)
{ {
@ -200,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);
@ -445,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();
@ -670,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;
@ -701,6 +712,7 @@ namespace Ryujinx.Ava
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
{ {
renderer = new VulkanRenderer( renderer = new VulkanRenderer(
Vk.GetApi(),
(_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, (_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface,
VulkanHelper.GetRequiredInstanceExtensions, VulkanHelper.GetRequiredInstanceExtensions,
ConfigurationState.Instance.Graphics.PreferredGpu.Value); ConfigurationState.Instance.Graphics.PreferredGpu.Value);

View File

@ -590,6 +590,7 @@
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})", "DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
"UpdateWindowTitle": "Title Update Manager", "UpdateWindowTitle": "Title Update Manager",
"CheatWindowHeading": "Cheats Available for {0} [{1}]", "CheatWindowHeading": "Cheats Available for {0} [{1}]",
"BuildId": "BuildId:",
"DlcWindowHeading": "{0} Downloadable Content(s)", "DlcWindowHeading": "{0} Downloadable Content(s)",
"UserProfilesEditProfile": "Edit Selected", "UserProfilesEditProfile": "Edit Selected",
"Cancel": "Cancel", "Cancel": "Cancel",
@ -644,5 +645,6 @@
"UserEditorTitleCreate" : "Create User", "UserEditorTitleCreate" : "Create User",
"SettingsTabNetworkInterface": "Network Interface:", "SettingsTabNetworkInterface": "Network Interface:",
"NetworkInterfaceTooltip": "The network interface used for LAN features", "NetworkInterfaceTooltip": "The network interface used for LAN features",
"NetworkInterfaceDefault": "Default" "NetworkInterfaceDefault": "Default",
"PackagingShaders": "Packaging Shaders"
} }

View File

@ -193,7 +193,7 @@ namespace Ryujinx.Ava.Common
if (nca.Header.ContentType == NcaContentType.Program) if (nca.Header.ContentType == NcaContentType.Program)
{ {
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{ {
patchNca = nca; patchNca = nca;
} }

View File

@ -70,11 +70,14 @@ 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)
if (PressedButtons.Count() >= button)
{ {
PressedButtons[button] = false; int button = (int)args.InitialPressMouseButton;
if (PressedButtons.Count() >= button)
{
PressedButtons[button] = false;
}
} }
} }

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

@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Ui.App.Common;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using System; using System;
@ -118,7 +119,11 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null) if (viewModel?.SelectedApplication != null)
{ {
await new CheatWindow(viewModel.VirtualFileSystem, viewModel.SelectedApplication.TitleId, viewModel.SelectedApplication.TitleName).ShowDialog(viewModel.TopLevel as Window); await new CheatWindow(
viewModel.VirtualFileSystem,
viewModel.SelectedApplication.TitleId,
viewModel.SelectedApplication.TitleName,
viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window);
} }
} }

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

@ -1099,6 +1099,10 @@ namespace Ryujinx.Ava.UI.ViewModels
LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders]; LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders];
IsLoadingIndeterminate = false; IsLoadingIndeterminate = false;
break; break;
case ShaderCacheLoadingState.Packaging:
LoadHeading = LocaleManager.Instance[LocaleKeys.PackagingShaders];
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Loaded: case ShaderCacheLoadingState.Loaded:
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName); LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
IsLoadingIndeterminate = true; IsLoadingIndeterminate = true;
@ -1520,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

@ -25,6 +25,7 @@ using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
using Silk.NET.Vulkan;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
@ -238,8 +239,9 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public DateTimeOffset DateOffset { get; set; } public DateTimeOffset CurrentDate { get; set; }
public TimeSpan TimeOffset { get; set; } public TimeSpan CurrentTime { get; set; }
internal AvaloniaList<TimeZone> TimeZones { get; set; } internal AvaloniaList<TimeZone> TimeZones { get; set; }
public AvaloniaList<string> GameDirectories { get; set; } public AvaloniaList<string> GameDirectories { get; set; }
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; } public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
@ -309,7 +311,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_gpuIds = new List<string>(); _gpuIds = new List<string>();
List<string> names = new(); List<string> names = new();
var devices = VulkanRenderer.GetPhysicalDevices(); var devices = VulkanRenderer.GetPhysicalDevices(Vk.GetApi());
if (devices.Length == 0) if (devices.Length == 0)
{ {
@ -397,10 +399,11 @@ namespace Ryujinx.Ava.UI.ViewModels
Language = (int)config.System.Language.Value; Language = (int)config.System.Language.Value;
TimeZone = config.System.TimeZone; TimeZone = config.System.TimeZone;
DateTime dateTimeOffset = DateTime.Now.AddSeconds(config.System.SystemTimeOffset); DateTime currentDateTime = DateTime.Now;
CurrentDate = currentDateTime.Date;
CurrentTime = currentDateTime.TimeOfDay.Add(TimeSpan.FromSeconds(config.System.SystemTimeOffset));
DateOffset = dateTimeOffset.Date;
TimeOffset = dateTimeOffset.TimeOfDay;
EnableVsync = config.Graphics.EnableVsync; EnableVsync = config.Graphics.EnableVsync;
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks; EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
ExpandDramSize = config.System.ExpandRam; ExpandDramSize = config.System.ExpandRam;
@ -487,9 +490,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.TimeZone.Value = TimeZone; config.System.TimeZone.Value = TimeZone;
} }
TimeSpan systemTimeOffset = DateOffset - DateTime.Now; config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds());
config.System.SystemTimeOffset.Value = systemTimeOffset.Seconds;
config.Graphics.EnableVsync.Value = EnableVsync; config.Graphics.EnableVsync.Value = EnableVsync;
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
config.System.ExpandRam.Value = ExpandDramSize; config.System.ExpandRam.Value = ExpandDramSize;

View File

@ -11,6 +11,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
@ -176,7 +177,11 @@ namespace Ryujinx.Ava.UI.Views.Main
string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString(); string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
await new CheatWindow(Window.VirtualFileSystem, ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, name).ShowDialog(Window); await new CheatWindow(
Window.VirtualFileSystem,
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
name,
Window.ViewModel.SelectedApplication.Path).ShowDialog(Window);
ViewModel.AppHost.Device.EnableCheats(); ViewModel.AppHost.Device.EnableCheats();
} }

View File

@ -3,12 +3,12 @@
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True" x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel"> x:DataType="viewModels:SettingsViewModel"
mc:Ignorable="d">
<Design.DataContext> <Design.DataContext>
<viewModels:SettingsViewModel /> <viewModels:SettingsViewModel />
</Design.DataContext> </Design.DataContext>
@ -27,13 +27,15 @@
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical"> <StackPanel Margin="10,0,0,0" Orientation="Vertical">
<StackPanel Margin="0,0,0,10" Orientation="Horizontal"> <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock
Text="{locale:Locale SettingsTabSystemSystemRegion}" Width="250"
Width="250" /> VerticalAlignment="Center"
<ComboBox SelectedIndex="{Binding Region}" Text="{locale:Locale SettingsTabSystemSystemRegion}" />
ToolTip.Tip="{locale:Locale RegionTooltip}" <ComboBox
HorizontalContentAlignment="Left" Width="350"
Width="350"> HorizontalContentAlignment="Left"
SelectedIndex="{Binding Region}"
ToolTip.Tip="{locale:Locale RegionTooltip}">
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" /> <TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
</ComboBoxItem> </ComboBoxItem>
@ -58,20 +60,21 @@
</ComboBox> </ComboBox>
</StackPanel> </StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal"> <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguage}" Width="250"
ToolTip.Tip="{locale:Locale LanguageTooltip}" VerticalAlignment="Center"
Width="250" /> Text="{locale:Locale SettingsTabSystemSystemLanguage}"
<ComboBox SelectedIndex="{Binding Language}" ToolTip.Tip="{locale:Locale LanguageTooltip}" />
ToolTip.Tip="{locale:Locale LanguageTooltip}" <ComboBox
HorizontalContentAlignment="Left" Width="350"
Width="350"> HorizontalContentAlignment="Left"
SelectedIndex="{Binding Language}"
ToolTip.Tip="{locale:Locale LanguageTooltip}">
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" /> <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" /> <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" />
@ -104,71 +107,67 @@
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" /> <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock <TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
</ComboBoxItem> </ComboBoxItem>
</ComboBox> </ComboBox>
</StackPanel> </StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal"> <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock
Text="{locale:Locale SettingsTabSystemSystemTimeZone}" Width="250"
ToolTip.Tip="{locale:Locale TimezoneTooltip}" VerticalAlignment="Center"
Width="250" /> Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
<AutoCompleteBox <AutoCompleteBox
Name="TimeZoneBox" Name="TimeZoneBox"
Width="350" Width="350"
MaxDropDownHeight="500"
FilterMode="Contains" FilterMode="Contains"
Items="{Binding TimeZones}" Items="{Binding TimeZones}"
MaxDropDownHeight="500"
SelectionChanged="TimeZoneBox_OnSelectionChanged" SelectionChanged="TimeZoneBox_OnSelectionChanged"
Text="{Binding Path=TimeZone, Mode=OneWay}" Text="{Binding Path=TimeZone, Mode=OneWay}"
TextChanged="TimeZoneBox_OnTextChanged" TextChanged="TimeZoneBox_OnTextChanged"
ToolTip.Tip="{locale:Locale TimezoneTooltip}" /> ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
</StackPanel> </StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal"> <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock
Text="{locale:Locale SettingsTabSystemSystemTime}" Width="250"
ToolTip.Tip="{locale:Locale TimeTooltip}" VerticalAlignment="Center"
Width="250"/> Text="{locale:Locale SettingsTabSystemSystemTime}"
<DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}" ToolTip.Tip="{locale:Locale TimeTooltip}" />
ToolTip.Tip="{locale:Locale TimeTooltip}" <DatePicker
Width="350" /> Width="350"
VerticalAlignment="Center"
SelectedDate="{Binding CurrentDate}"
ToolTip.Tip="{locale:Locale TimeTooltip}" />
</StackPanel> </StackPanel>
<StackPanel Margin="250,0,0,10" Orientation="Horizontal"> <StackPanel Margin="250,0,0,10" Orientation="Horizontal">
<TimePicker <TimePicker
Width="350"
VerticalAlignment="Center" VerticalAlignment="Center"
ClockIdentifier="24HourClock" ClockIdentifier="24HourClock"
SelectedTime="{Binding TimeOffset}" SelectedTime="{Binding CurrentTime}"
Width="350"
ToolTip.Tip="{locale:Locale TimeTooltip}" /> ToolTip.Tip="{locale:Locale TimeTooltip}" />
</StackPanel> </StackPanel>
<CheckBox IsChecked="{Binding EnableVsync}"> <CheckBox IsChecked="{Binding EnableVsync}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}" <TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}" ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}"> <CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}" <TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}" ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
@ -180,12 +179,10 @@
Margin="10,0,0,0" Margin="10,0,0,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical"> Orientation="Vertical">
<CheckBox IsChecked="{Binding ExpandDramSize}" <CheckBox IsChecked="{Binding ExpandDramSize}" ToolTip.Tip="{locale:Locale DRamTooltip}">
ToolTip.Tip="{locale:Locale DRamTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" /> <TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding IgnoreMissingServices}" <CheckBox IsChecked="{Binding IgnoreMissingServices}" ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" /> <TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>

View File

@ -21,23 +21,52 @@
</Window.Styles> </Window.Styles>
<Grid Name="CheatGrid" Margin="15"> <Grid Name="CheatGrid" Margin="15">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
MaxWidth="500" MaxWidth="500"
Margin="20,15,20,20" Margin="20,15,20,5"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
LineHeight="18" LineHeight="18"
Text="{Binding Heading}" Text="{Binding Heading}"
TextAlignment="Center" TextAlignment="Center"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<Border <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0"
MaxWidth="500"
Margin="140,15,20,5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
LineHeight="30"
Text="{locale:Locale BuildId}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Margin="0,5,110,5"
MinWidth="160"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding BuildId}"
IsReadOnly="True" />
<Border
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="5" Margin="5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
@ -81,7 +110,9 @@
</TreeView> </TreeView>
</Border> </Border>
<DockPanel <DockPanel
Grid.Row="3" Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0" Margin="0"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<DockPanel Margin="0" HorizontalAlignment="Right"> <DockPanel Margin="0" HorizontalAlignment="Right">

View File

@ -1,8 +1,10 @@
using Avalonia.Collections; using Avalonia;
using Avalonia.Collections;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -17,6 +19,7 @@ namespace Ryujinx.Ava.UI.Windows
private AvaloniaList<CheatsList> LoadedCheats { get; } private AvaloniaList<CheatsList> LoadedCheats { get; }
public string Heading { get; } public string Heading { get; }
public string BuildId { get; }
public CheatWindow() public CheatWindow()
{ {
@ -27,12 +30,13 @@ namespace Ryujinx.Ava.UI.Windows
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle]; Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle];
} }
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
{ {
LoadedCheats = new AvaloniaList<CheatsList>(); LoadedCheats = new AvaloniaList<CheatsList>();
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper()); Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
InitializeComponent(); InitializeComponent();
string modsBasePath = ModLoader.GetModsBasePath(); string modsBasePath = ModLoader.GetModsBasePath();

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public const int PrimitiveRestartStateIndex = 12; public const int PrimitiveRestartStateIndex = 12;
public const int RenderTargetStateIndex = 27; public const int RenderTargetStateIndex = 27;
private const ulong MaxUnknownStorageSize = 0x100000;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
private readonly DeviceStateWithShadow<ThreedClassState> _state; private readonly DeviceStateWithShadow<ThreedClassState> _state;
@ -356,7 +358,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress); SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
_channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); uint size;
if (sb.SbCbSlot == 0)
{
// Only trust the SbDescriptor size if it comes from slot 0.
size = (uint)sbDescriptor.Size;
}
else
{
// TODO: Use full mapped size and somehow speed up buffer sync.
size = (uint)_channel.MemoryManager.GetMappedSize(sbDescriptor.PackAddress(), MaxUnknownStorageSize);
}
_channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags);
} }
} }
} }

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,37 +700,50 @@ 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];
if (oInfo.Compatibility == TextureViewCompatibility.Full) bool aliased = oInfo.Compatibility == TextureViewCompatibility.FormatAlias;
if (!isSamplerTexture)
{ {
if (!isSamplerTexture) // 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,
// If this is not a sampler texture, the size might be different from the requested size, // before creating the view.
// so we need to make sure the texture information has the correct size for this base texture,
// before creating the view.
info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel);
}
texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel, aliased);
texture.SynchronizeMemory();
break;
} }
else if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0) else if (aliased)
{ {
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead. // The format must be changed to match the parent.
info = info.CreateInfoWithFormat(overlap.Info.FormatInfo);
}
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode); texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
texture.SynchronizeMemory();
}
else
{
for (int index = 0; index < overlapsCount; index++)
{
Texture overlap = _textureOverlaps[index];
OverlapInfo oInfo = _overlapInfo[index];
texture.InitializeGroup(true, true, new List<TextureIncompatibleOverlap>()); if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0)
texture.InitializeData(false, false); {
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
overlap.SynchronizeMemory(); texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
break; texture.InitializeGroup(true, true, new List<TextureIncompatibleOverlap>());
texture.InitializeData(false, false);
overlap.SynchronizeMemory();
overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true);
break;
}
} }
} }
@ -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

@ -637,6 +637,33 @@ namespace Ryujinx.Graphics.Gpu.Memory
return UnpackPaFromPte(pte) + (va & PageMask); return UnpackPaFromPte(pte) + (va & PageMask);
} }
/// <summary>
/// Translates a GPU virtual address and returns the number of bytes that are mapped after it.
/// </summary>
/// <param name="va">GPU virtual address to be translated</param>
/// <param name="maxSize">Maximum size in bytes to scan</param>
/// <returns>Number of bytes, 0 if unmapped</returns>
public ulong GetMappedSize(ulong va, ulong maxSize)
{
if (!ValidateAddress(va))
{
return 0;
}
ulong startVa = va;
ulong endVa = va + maxSize;
ulong pte = GetPte(va);
while (pte != PteUnmapped && va < endVa)
{
va += PageSize - (va & PageMask);
pte = GetPte(va);
}
return Math.Min(maxSize, va - startVa);
}
/// <summary> /// <summary>
/// Gets the kind of a given memory page. /// Gets the kind of a given memory page.
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling. /// This might indicate the type of resource that can be allocated on the page, and also texture tiling.

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 = 4578;
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

@ -299,10 +299,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
if (_programList.Count != 0) if (_programList.Count != 0)
{ {
_stateChangeCallback(ShaderCacheState.Packaging, 0, _programList.Count);
Logger.Info?.Print(LogClass.Gpu, $"Rebuilding {_programList.Count} shaders..."); Logger.Info?.Print(LogClass.Gpu, $"Rebuilding {_programList.Count} shaders...");
using var streams = _hostStorage.GetOutputStreams(_context); using var streams = _hostStorage.GetOutputStreams(_context);
int packagedShaders = 0;
foreach (var kv in _programList) foreach (var kv in _programList)
{ {
if (!Active) if (!Active)
@ -311,7 +314,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
} }
(CachedShaderProgram program, byte[] binaryCode) = kv.Value; (CachedShaderProgram program, byte[] binaryCode) = kv.Value;
_hostStorage.AddShader(_context, program, binaryCode, streams); _hostStorage.AddShader(_context, program, binaryCode, streams);
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
} }
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully."); Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");

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

@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
Start, Start,
/// <summary>Shader cache is loading</summary> /// <summary>Shader cache is loading</summary>
Loading, Loading,
/// <summary>Shader cache is written to disk</summary>
Packaging,
/// <summary>Shader cache finished loading</summary> /// <summary>Shader cache finished loading</summary>
Loaded Loaded
} }

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

@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.OpenGL
oldView.Dispose(); oldView.Dispose();
} }
} }
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer);
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer);

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

@ -20,6 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask); GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask);
BindlessToIndexed.RunPass(blocks[blkIndex], config); BindlessToIndexed.RunPass(blocks[blkIndex], config);
BindlessElimination.RunPass(blocks[blkIndex], config); BindlessElimination.RunPass(blocks[blkIndex], config);
// FragmentCoord only exists on fragment shaders, so we don't need to check other stages.
if (config.Stage == ShaderStage.Fragment)
{
EliminateMultiplyByFragmentCoordW(blocks[blkIndex]);
}
} }
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask); config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
@ -281,6 +287,75 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return modified; return modified;
} }
private static void EliminateMultiplyByFragmentCoordW(BasicBlock block)
{
foreach (INode node in block.Operations)
{
if (node is Operation operation)
{
EliminateMultiplyByFragmentCoordW(operation);
}
}
}
private static void EliminateMultiplyByFragmentCoordW(Operation operation)
{
// We're looking for the pattern:
// y = x * gl_FragCoord.w
// v = y * (1.0 / gl_FragCoord.w)
// Then we transform it into:
// v = x
// This pattern is common on fragment shaders due to the way how perspective correction is done.
// We are expecting a multiplication by the reciprocal of gl_FragCoord.w.
if (operation.Inst != (Instruction.FP32 | Instruction.Multiply))
{
return;
}
Operand lhs = operation.GetSource(0);
Operand rhs = operation.GetSource(1);
// Check LHS of the the main multiplication operation. We expect an input being multiplied by gl_FragCoord.w.
if (!(lhs.AsgOp is Operation attrMulOp) || attrMulOp.Inst != (Instruction.FP32 | Instruction.Multiply))
{
return;
}
Operand attrMulLhs = attrMulOp.GetSource(0);
Operand attrMulRhs = attrMulOp.GetSource(1);
// LHS should be any input, RHS should be exactly gl_FragCoord.w.
if (!Utils.IsInputLoad(attrMulLhs.AsgOp) || !Utils.IsInputLoad(attrMulRhs.AsgOp, IoVariable.FragmentCoord, 3))
{
return;
}
// RHS of the main multiplication should be a reciprocal operation (1.0 / x).
if (!(rhs.AsgOp is Operation reciprocalOp) || reciprocalOp.Inst != (Instruction.FP32 | Instruction.Divide))
{
return;
}
Operand reciprocalLhs = reciprocalOp.GetSource(0);
Operand reciprocalRhs = reciprocalOp.GetSource(1);
// Check if the divisor is a constant equal to 1.0.
if (reciprocalLhs.Type != OperandType.Constant || reciprocalLhs.AsFloat() != 1.0f)
{
return;
}
// Check if the dividend is gl_FragCoord.w.
if (!Utils.IsInputLoad(reciprocalRhs.AsgOp, IoVariable.FragmentCoord, 3))
{
return;
}
// If everything matches, we can replace the operation with the input load result.
operation.TurnIntoCopy(attrMulLhs);
}
private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode) private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
{ {
// Remove a node from the nodes list, and also remove itself // Remove a node from the nodes list, and also remove itself

View File

@ -4,6 +4,35 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{ {
static class Utils static class Utils
{ {
public static bool IsInputLoad(INode node)
{
return (node is Operation operation) &&
operation.Inst == Instruction.Load &&
operation.StorageKind == StorageKind.Input;
}
public static bool IsInputLoad(INode node, IoVariable ioVariable, int elemIndex)
{
if (!(node is Operation operation) ||
operation.Inst != Instruction.Load ||
operation.StorageKind != StorageKind.Input ||
operation.SourcesCount != 2)
{
return false;
}
Operand ioVariableSrc = operation.GetSource(0);
if (ioVariableSrc.Type != OperandType.Constant || (IoVariable)ioVariableSrc.Value != ioVariable)
{
return false;
}
Operand elemIndexSrc = operation.GetSource(1);
return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex;
}
private static Operation FindBranchSource(BasicBlock block) private static Operation FindBranchSource(BasicBlock block)
{ {
foreach (BasicBlock sourceBlock in block.Predecessors) foreach (BasicBlock sourceBlock in block.Predecessors)

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

@ -105,6 +105,23 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
public bool TryIncrementReferenceCount()
{
int lastValue;
do
{
lastValue = _referenceCount;
if (lastValue == 0)
{
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
return true;
}
public void IncrementReferenceCount() public void IncrementReferenceCount()
{ {
if (Interlocked.Increment(ref _referenceCount) == 1) if (Interlocked.Increment(ref _referenceCount) == 1)

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();
_flushCount++; if (_lastFlushWrite != _writeCount)
{
// If it's on the same page as the last flush, ignore it.
_lastFlushWrite = _writeCount;
_flushCount++;
}
Span<byte> result; Span<byte> result;
@ -599,9 +613,10 @@ namespace Ryujinx.Graphics.Vulkan
Auto<DisposableBuffer> dst, Auto<DisposableBuffer> dst,
int srcOffset, int srcOffset,
int dstOffset, int dstOffset,
int size) int size,
bool registerSrcUsage = true)
{ {
var srcBuffer = src.Get(cbs, srcOffset, size).Value; var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
var dstBuffer = dst.Get(cbs, dstOffset, size).Value; var dstBuffer = dst.Get(cbs, dstOffset, size).Value;
InsertBufferBarrier( InsertBufferBarrier(

View File

@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Vulkan
public SemaphoreHolder Semaphore; public SemaphoreHolder Semaphore;
public List<IAuto> Dependants; public List<IAuto> Dependants;
public HashSet<MultiFenceHolder> Waitables; public List<MultiFenceHolder> Waitables;
public HashSet<SemaphoreHolder> Dependencies; public HashSet<SemaphoreHolder> Dependencies;
public void Initialize(Vk api, Device device, CommandPool pool) public void Initialize(Vk api, Device device, CommandPool pool)
@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Vulkan
api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer); api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);
Dependants = new List<IAuto>(); Dependants = new List<IAuto>();
Waitables = new HashSet<MultiFenceHolder>(); Waitables = new List<MultiFenceHolder>();
Dependencies = new HashSet<SemaphoreHolder>(); Dependencies = new HashSet<SemaphoreHolder>();
} }
} }
@ -143,8 +143,10 @@ namespace Ryujinx.Graphics.Vulkan
public void AddWaitable(int cbIndex, MultiFenceHolder waitable) public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
{ {
ref var entry = ref _commandBuffers[cbIndex]; ref var entry = ref _commandBuffers[cbIndex];
waitable.AddFence(cbIndex, entry.Fence); if (waitable.AddFence(cbIndex, entry.Fence))
entry.Waitables.Add(waitable); {
entry.Waitables.Add(waitable);
}
} }
public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offset, int size) public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offset, int size)
@ -156,7 +158,7 @@ namespace Ryujinx.Graphics.Vulkan
ref var entry = ref _commandBuffers[i]; ref var entry = ref _commandBuffers[i];
if (entry.InUse && if (entry.InUse &&
entry.Waitables.Contains(waitable) && waitable.HasFence(i) &&
waitable.IsBufferRangeInUse(i, offset, size)) waitable.IsBufferRangeInUse(i, offset, size))
{ {
return true; return true;
@ -331,7 +333,7 @@ namespace Ryujinx.Graphics.Vulkan
foreach (var waitable in entry.Waitables) foreach (var waitable in entry.Waitables)
{ {
waitable.RemoveFence(cbIndex, entry.Fence); waitable.RemoveFence(cbIndex);
waitable.RemoveBufferUses(cbIndex); waitable.RemoveBufferUses(cbIndex);
} }

View File

@ -228,7 +228,12 @@ namespace Ryujinx.Graphics.Vulkan
SignalDirty(DirtyFlags.Storage); SignalDirty(DirtyFlags.Storage);
} }
public void SetTextureAndSampler(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture texture, ISampler sampler) public void SetTextureAndSampler(
CommandBufferScoped cbs,
ShaderStage stage,
int binding,
ITexture texture,
ISampler sampler)
{ {
if (texture is TextureBuffer textureBuffer) if (texture is TextureBuffer textureBuffer)
{ {
@ -251,6 +256,28 @@ namespace Ryujinx.Graphics.Vulkan
SignalDirty(DirtyFlags.Texture); SignalDirty(DirtyFlags.Texture);
} }
public void SetTextureAndSamplerIdentitySwizzle(
CommandBufferScoped cbs,
ShaderStage stage,
int binding,
ITexture texture,
ISampler sampler)
{
if (texture is TextureView view)
{
view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
_textureRefs[binding] = view.GetIdentityImageView();
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
SignalDirty(DirtyFlags.Texture);
}
else
{
SetTextureAndSampler(cbs, stage, binding, texture, sampler);
}
}
public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers) public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
{ {
for (int i = 0; i < buffers.Length; i++) for (int i = 0; i < buffers.Length; i++)

View File

@ -32,6 +32,25 @@ namespace Ryujinx.Graphics.Vulkan
return _fence; return _fence;
} }
public bool TryGet(out Fence fence)
{
int lastValue;
do
{
lastValue = _referenceCount;
if (lastValue == 0)
{
fence = default;
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
fence = _fence;
return true;
}
public Fence Get() public Fence Get()
{ {
Interlocked.Increment(ref _referenceCount); Interlocked.Increment(ref _referenceCount);

View File

@ -415,7 +415,7 @@ namespace Ryujinx.Graphics.Vulkan
var sampler = linearFilter ? _samplerLinear : _samplerNearest; var sampler = linearFilter ? _samplerLinear : _samplerNearest;
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, sampler);
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)]; Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
@ -625,7 +625,7 @@ namespace Ryujinx.Graphics.Vulkan
private void BlitDepthStencilDraw(TextureView src, bool isDepth) private void BlitDepthStencilDraw(TextureView src, bool isDepth)
{ {
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, _samplerNearest);
if (isDepth) if (isDepth)
{ {
@ -1037,7 +1037,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, srcLevel + l, srcFormat); var srcView = Create2DLayerView(src, srcLayer + z, srcLevel + l, srcFormat);
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l); var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, dstFormat); _pipeline.SetImage(0, dstView, dstFormat);
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
@ -1177,7 +1177,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, 0, format); var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
var dstView = Create2DLayerView(dst, dstLayer + z, 0); var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, format); _pipeline.SetImage(0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
@ -1313,7 +1313,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, 0, format); var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
var dstView = Create2DLayerView(dst, dstLayer + z, 0); var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, srcView, null);
_pipeline.SetRenderTarget( _pipeline.SetRenderTarget(
((TextureView)dstView).GetView(format).GetImageViewForAttachment(), ((TextureView)dstView).GetView(format).GetImageViewForAttachment(),
(uint)dst.Width, (uint)dst.Width,
@ -1384,7 +1384,7 @@ namespace Ryujinx.Graphics.Vulkan
private void CopyMSAspectDraw(TextureView src, bool fromMS, bool isDepth) private void CopyMSAspectDraw(TextureView src, bool fromMS, bool isDepth)
{ {
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, _samplerNearest);
if (isDepth) if (isDepth)
{ {

View File

@ -25,6 +25,8 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE; config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE;
config.SynchronousQueueSubmits = false; config.SynchronousQueueSubmits = false;
config.ResumeLostDevice = true;
vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize); vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize);
} }
} }

View File

@ -1,6 +1,5 @@
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System.Collections.Generic; using System;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
private static int BufferUsageTrackingGranularity = 4096; private static int BufferUsageTrackingGranularity = 4096;
private readonly Dictionary<FenceHolder, int> _fences; private readonly FenceHolder[] _fences;
private BufferUsageBitmap _bufferUsageBitmap; private BufferUsageBitmap _bufferUsageBitmap;
/// <summary> /// <summary>
@ -19,7 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
/// </summary> /// </summary>
public MultiFenceHolder() public MultiFenceHolder()
{ {
_fences = new Dictionary<FenceHolder, int>(); _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
} }
/// <summary> /// <summary>
@ -28,7 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <param name="size">Size of the buffer</param> /// <param name="size">Size of the buffer</param>
public MultiFenceHolder(int size) public MultiFenceHolder(int size)
{ {
_fences = new Dictionary<FenceHolder, int>(); _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
} }
@ -80,25 +79,37 @@ namespace Ryujinx.Graphics.Vulkan
/// </summary> /// </summary>
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param> /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
/// <param name="fence">Fence to be added</param> /// <param name="fence">Fence to be added</param>
public void AddFence(int cbIndex, FenceHolder fence) /// <returns>True if the command buffer's previous fence value was null</returns>
public bool AddFence(int cbIndex, FenceHolder fence)
{ {
lock (_fences) ref FenceHolder fenceRef = ref _fences[cbIndex];
if (fenceRef == null)
{ {
_fences.TryAdd(fence, cbIndex); fenceRef = fence;
return true;
} }
return false;
} }
/// <summary> /// <summary>
/// Removes a fence from the holder. /// Removes a fence from the holder.
/// </summary> /// </summary>
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param> /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
/// <param name="fence">Fence to be removed</param> public void RemoveFence(int cbIndex)
public void RemoveFence(int cbIndex, FenceHolder fence)
{ {
lock (_fences) _fences[cbIndex] = null;
{ }
_fences.Remove(fence);
} /// <summary>
/// Determines if a fence referenced on the given command buffer.
/// </summary>
/// <param name="cbIndex">Index of the command buffer to check if it's used</param>
/// <returns>True if referenced, false otherwise</returns>
public bool HasFence(int cbIndex)
{
return _fences[cbIndex] != null;
} }
/// <summary> /// <summary>
@ -147,21 +158,29 @@ namespace Ryujinx.Graphics.Vulkan
/// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns> /// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout) private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout)
{ {
FenceHolder[] fenceHolders; Span<FenceHolder> fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
Fence[] fences;
lock (_fences) int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
Span<Fence> fences = stackalloc Fence[count];
int fenceCount = 0;
for (int i = 0; i < count; i++)
{ {
fenceHolders = size != 0 ? GetOverlappingFences(offset, size) : _fences.Keys.ToArray(); if (fenceHolders[i].TryGet(out Fence fence))
fences = new Fence[fenceHolders.Length];
for (int i = 0; i < fenceHolders.Length; i++)
{ {
fences[i] = fenceHolders[i].Get(); fences[fenceCount] = fence;
if (fenceCount < i)
{
fenceHolders[fenceCount] = fenceHolders[i];
}
fenceCount++;
} }
} }
if (fences.Length == 0) if (fenceCount == 0)
{ {
return true; return true;
} }
@ -170,14 +189,14 @@ namespace Ryujinx.Graphics.Vulkan
if (hasTimeout) if (hasTimeout)
{ {
signaled = FenceHelper.AllSignaled(api, device, fences, timeout); signaled = FenceHelper.AllSignaled(api, device, fences.Slice(0, fenceCount), timeout);
} }
else else
{ {
FenceHelper.WaitAllIndefinitely(api, device, fences); FenceHelper.WaitAllIndefinitely(api, device, fences.Slice(0, fenceCount));
} }
for (int i = 0; i < fenceHolders.Length; i++) for (int i = 0; i < fenceCount; i++)
{ {
fenceHolders[i].Put(); fenceHolders[i].Put();
} }
@ -186,27 +205,49 @@ namespace Ryujinx.Graphics.Vulkan
} }
/// <summary> /// <summary>
/// Gets fences to wait for use of a given buffer region. /// Gets fences to wait for.
/// </summary> /// </summary>
/// <param name="offset">Offset of the range</param> /// <param name="storage">Span to store fences in</param>
/// <param name="size">Size of the range in bytes</param> /// <returns>Number of fences placed in storage</returns>
/// <returns>Fences for the specified region</returns> private int GetFences(Span<FenceHolder> storage)
private FenceHolder[] GetOverlappingFences(int offset, int size)
{ {
List<FenceHolder> overlapping = new List<FenceHolder>(); int count = 0;
foreach (var kv in _fences) for (int i = 0; i < _fences.Length; i++)
{ {
var fence = kv.Key; var fence = _fences[i];
var ownerCbIndex = kv.Value;
if (_bufferUsageBitmap.OverlapsWith(ownerCbIndex, offset, size)) if (fence != null)
{ {
overlapping.Add(fence); storage[count++] = fence;
} }
} }
return overlapping.ToArray(); return count;
}
/// <summary>
/// Gets fences to wait for use of a given buffer region.
/// </summary>
/// <param name="storage">Span to store overlapping fences in</param>
/// <param name="offset">Offset of the range</param>
/// <param name="size">Size of the range in bytes</param>
/// <returns>Number of fences for the specified region placed in storage</returns>
private int GetOverlappingFences(Span<FenceHolder> storage, int offset, int size)
{
int count = 0;
for (int i = 0; i < _fences.Length; i++)
{
var fence = _fences[i];
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
{
storage[count++] = fence;
}
}
return count;
} }
} }
} }

View File

@ -34,16 +34,26 @@ namespace Ryujinx.Graphics.Vulkan
public Span<byte> GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size) public Span<byte> GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size)
{ {
var flushStorage = ResizeIfNeeded(size); var flushStorage = ResizeIfNeeded(size);
Auto<DisposableBuffer> srcBuffer;
using (var cbs = cbp.Rent()) using (var cbs = cbp.Rent())
{ {
var srcBuffer = buffer.GetBuffer(cbs.CommandBuffer); srcBuffer = buffer.GetBuffer(cbs.CommandBuffer);
var dstBuffer = flushStorage.GetBuffer(cbs.CommandBuffer); var dstBuffer = flushStorage.GetBuffer(cbs.CommandBuffer);
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, 0, size); if (srcBuffer.TryIncrementReferenceCount())
{
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false);
}
else
{
// Source buffer is no longer alive, don't copy anything to flush storage.
srcBuffer = null;
}
} }
flushStorage.WaitForFences(); flushStorage.WaitForFences();
srcBuffer?.DecrementReferenceCount();
return flushStorage.GetDataStorage(0, size); return flushStorage.GetDataStorage(0, size);
} }

View File

@ -66,6 +66,8 @@ namespace Ryujinx.Graphics.Vulkan
private ulong _vertexBuffersDirty; private ulong _vertexBuffersDirty;
protected Rectangle<int> ClearScissor; protected Rectangle<int> ClearScissor;
private readonly VertexBufferUpdater _vertexBufferUpdater;
public SupportBufferUpdater SupportBufferUpdater; public SupportBufferUpdater SupportBufferUpdater;
public IndexBufferPattern QuadsToTrisPattern; public IndexBufferPattern QuadsToTrisPattern;
public IndexBufferPattern TriFanToTrisPattern; public IndexBufferPattern TriFanToTrisPattern;
@ -96,6 +98,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
_descriptorSetUpdater = new DescriptorSetUpdater(gd, this); _descriptorSetUpdater = new DescriptorSetUpdater(gd, this);
_vertexBufferUpdater = new VertexBufferUpdater(gd);
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers]; _transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
_vertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers + 1]; _vertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers + 1];
@ -1095,6 +1098,11 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater.SetTextureAndSampler(Cbs, stage, binding, texture, sampler); _descriptorSetUpdater.SetTextureAndSampler(Cbs, stage, binding, texture, sampler);
} }
public void SetTextureAndSamplerIdentitySwizzle(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
{
_descriptorSetUpdater.SetTextureAndSamplerIdentitySwizzle(Cbs, stage, binding, texture, sampler);
}
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{ {
PauseTransformFeedbackInternal(); PauseTransformFeedbackInternal();
@ -1185,6 +1193,9 @@ namespace Ryujinx.Graphics.Vulkan
int validCount = 1; int validCount = 1;
BufferHandle lastHandle = default;
Auto<DisposableBuffer> lastBuffer = default;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
var vertexBuffer = vertexBuffers[i]; var vertexBuffer = vertexBuffers[i];
@ -1194,7 +1205,12 @@ namespace Ryujinx.Graphics.Vulkan
if (vertexBuffer.Buffer.Handle != BufferHandle.Null) if (vertexBuffer.Buffer.Handle != BufferHandle.Null)
{ {
var vb = Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false); Auto<DisposableBuffer> vb = (vertexBuffer.Buffer.Handle == lastHandle) ? lastBuffer :
Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false);
lastHandle = vertexBuffer.Buffer.Handle;
lastBuffer = vb;
if (vb != null) if (vb != null)
{ {
int binding = i + 1; int binding = i + 1;
@ -1222,24 +1238,29 @@ namespace Ryujinx.Graphics.Vulkan
ref var buffer = ref _vertexBuffers[binding]; ref var buffer = ref _vertexBuffers[binding];
int oldScalarAlign = buffer.AttributeScalarAlignment; int oldScalarAlign = buffer.AttributeScalarAlignment;
buffer.Dispose();
if (Gd.Capabilities.VertexBufferAlignment < 2 && if (Gd.Capabilities.VertexBufferAlignment < 2 &&
(vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{ {
buffer = new VertexBufferState( if (!buffer.Matches(vb, descriptorIndex, vertexBuffer.Buffer.Offset, vbSize, vertexBuffer.Stride))
vb, {
descriptorIndex, buffer.Dispose();
vertexBuffer.Buffer.Offset,
vbSize,
vertexBuffer.Stride);
buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState); buffer = new VertexBufferState(
vb,
descriptorIndex,
vertexBuffer.Buffer.Offset,
vbSize,
vertexBuffer.Stride);
buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState, _vertexBufferUpdater);
}
} }
else else
{ {
// May need to be rewritten. Bind this buffer before draw. // May need to be rewritten. Bind this buffer before draw.
buffer.Dispose();
buffer = new VertexBufferState( buffer = new VertexBufferState(
vertexBuffer.Buffer.Handle, vertexBuffer.Buffer.Handle,
descriptorIndex, descriptorIndex,
@ -1255,6 +1276,8 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
_vertexBufferUpdater.Commit(Cbs);
_newState.VertexBindingDescriptionsCount = (uint)validCount; _newState.VertexBindingDescriptionsCount = (uint)validCount;
SignalStateChange(); SignalStateChange();
} }
@ -1596,10 +1619,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty); int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty);
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState); _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater);
_vertexBuffersDirty &= ~(1UL << i); _vertexBuffersDirty &= ~(1UL << i);
} }
_vertexBufferUpdater.Commit(Cbs);
} }
if (_stateDirty || Pbp != pbp) if (_stateDirty || Pbp != pbp)
@ -1712,6 +1737,7 @@ namespace Ryujinx.Graphics.Vulkan
_framebuffer?.Dispose(); _framebuffer?.Dispose();
_newState.Dispose(); _newState.Dispose();
_descriptorSetUpdater.Dispose(); _descriptorSetUpdater.Dispose();
_vertexBufferUpdater.Dispose();
for (int i = 0; i < _vertexBuffers.Length; i++) for (int i = 0; i < _vertexBuffers.Length; i++)
{ {

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

@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan
AttributeScalarAlignment = 1; AttributeScalarAlignment = 1;
} }
public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state) public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state, VertexBufferUpdater updater)
{ {
var autoBuffer = _buffer; var autoBuffer = _buffer;
@ -65,21 +65,7 @@ namespace Ryujinx.Graphics.Vulkan
var buffer = autoBuffer.Get(cbs, 0, newSize).Value; var buffer = autoBuffer.Get(cbs, 0, newSize).Value;
if (gd.Capabilities.SupportsExtendedDynamicState) updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride);
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
0,
(ulong)newSize,
(ulong)stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
}
_buffer = autoBuffer; _buffer = autoBuffer;
@ -106,21 +92,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
var buffer = autoBuffer.Get(cbs, _offset, _size).Value; var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
if (gd.Capabilities.SupportsExtendedDynamicState) updater.BindVertexBuffer(cbs, binding, buffer, (ulong)_offset, (ulong)_size, (ulong)_stride);
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
(ulong)_offset,
(ulong)_size,
(ulong)_stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset);
}
} }
} }
@ -129,6 +101,11 @@ namespace Ryujinx.Graphics.Vulkan
return _buffer == buffer; return _buffer == buffer;
} }
public bool Matches(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0)
{
return _buffer == buffer && DescriptorIndex == descriptorIndex && _offset == offset && _size == size && _stride == stride;
}
public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
{ {
if (_buffer == from) if (_buffer == from)

View File

@ -0,0 +1,84 @@
using Silk.NET.Vulkan;
using System;
using VkBuffer = Silk.NET.Vulkan.Buffer;
namespace Ryujinx.Graphics.Vulkan
{
internal class VertexBufferUpdater : IDisposable
{
private VulkanRenderer _gd;
private uint _baseBinding;
private uint _count;
private NativeArray<VkBuffer> _buffers;
private NativeArray<ulong> _offsets;
private NativeArray<ulong> _sizes;
private NativeArray<ulong> _strides;
public VertexBufferUpdater(VulkanRenderer gd)
{
_gd = gd;
_buffers = new NativeArray<VkBuffer>(Constants.MaxVertexBuffers);
_offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_strides = new NativeArray<ulong>(Constants.MaxVertexBuffers);
}
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, VkBuffer buffer, ulong offset, ulong size, ulong stride)
{
if (_count == 0)
{
_baseBinding = binding;
}
else if (_baseBinding + _count != binding)
{
Commit(cbs);
_baseBinding = binding;
}
int index = (int)_count;
_buffers[index] = buffer;
_offsets[index] = offset;
_sizes[index] = size;
_strides[index] = stride;
_count++;
}
public unsafe void Commit(CommandBufferScoped cbs)
{
if (_count != 0)
{
if (_gd.Capabilities.SupportsExtendedDynamicState)
{
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
_baseBinding,
_count,
_buffers.Pointer,
_offsets.Pointer,
_sizes.Pointer,
_strides.Pointer);
}
else
{
_gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, _baseBinding, _count, _buffers.Pointer, _offsets.Pointer);
}
_count = 0;
}
}
public void Dispose()
{
_buffers.Dispose();
_offsets.Dispose();
_sizes.Dispose();
_strides.Dispose();
}
}
}

View File

@ -351,26 +351,26 @@ namespace Ryujinx.Graphics.Vulkan
var features = new PhysicalDeviceFeatures() var features = new PhysicalDeviceFeatures()
{ {
DepthBiasClamp = true, DepthBiasClamp = supportedFeatures.DepthBiasClamp,
DepthClamp = supportedFeatures.DepthClamp, DepthClamp = supportedFeatures.DepthClamp,
DualSrcBlend = supportedFeatures.DualSrcBlend, DualSrcBlend = supportedFeatures.DualSrcBlend,
FragmentStoresAndAtomics = true, FragmentStoresAndAtomics = supportedFeatures.FragmentStoresAndAtomics,
GeometryShader = supportedFeatures.GeometryShader, GeometryShader = supportedFeatures.GeometryShader,
ImageCubeArray = true, ImageCubeArray = supportedFeatures.ImageCubeArray,
IndependentBlend = true, IndependentBlend = supportedFeatures.IndependentBlend,
LogicOp = supportedFeatures.LogicOp, LogicOp = supportedFeatures.LogicOp,
OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise, OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise,
MultiViewport = supportedFeatures.MultiViewport, MultiViewport = supportedFeatures.MultiViewport,
PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery,
SamplerAnisotropy = true, SamplerAnisotropy = supportedFeatures.SamplerAnisotropy,
ShaderClipDistance = true, ShaderClipDistance = supportedFeatures.ShaderClipDistance,
ShaderFloat64 = supportedFeatures.ShaderFloat64, ShaderFloat64 = supportedFeatures.ShaderFloat64,
ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended, ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended,
ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample,
// ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageReadWithoutFormat = true,
// ShaderStorageImageWriteWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true,
TessellationShader = supportedFeatures.TessellationShader, TessellationShader = supportedFeatures.TessellationShader,
VertexPipelineStoresAndAtomics = true, VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
RobustBufferAccess = useRobustBufferAccess RobustBufferAccess = useRobustBufferAccess
}; };

View File

@ -89,11 +89,12 @@ namespace Ryujinx.Graphics.Vulkan
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
public VulkanRenderer(Func<Instance, Vk, SurfaceKHR> surfaceFunc, Func<string[]> requiredExtensionsFunc, string preferredGpuId) public VulkanRenderer(Vk api, Func<Instance, Vk, SurfaceKHR> surfaceFunc, Func<string[]> requiredExtensionsFunc, string preferredGpuId)
{ {
_getSurface = surfaceFunc; _getSurface = surfaceFunc;
_getRequiredExtensions = requiredExtensionsFunc; _getRequiredExtensions = requiredExtensionsFunc;
_preferredGpuId = preferredGpuId; _preferredGpuId = preferredGpuId;
Api = api;
Shaders = new HashSet<ShaderCollection>(); Shaders = new HashSet<ShaderCollection>();
Textures = new HashSet<ITexture>(); Textures = new HashSet<ITexture>();
Samplers = new HashSet<SamplerHolder>(); Samplers = new HashSet<SamplerHolder>();
@ -345,31 +346,27 @@ namespace Ryujinx.Graphics.Vulkan
private unsafe void SetupContext(GraphicsDebugLevel logLevel) private unsafe void SetupContext(GraphicsDebugLevel logLevel)
{ {
var api = Vk.GetApi(); _instance = VulkanInitialization.CreateInstance(Api, logLevel, _getRequiredExtensions());
_debugMessenger = new VulkanDebugMessenger(Api, _instance.Instance, logLevel);
Api = api; if (Api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi))
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions());
_debugMessenger = new VulkanDebugMessenger(api, _instance.Instance, logLevel);
if (api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi))
{ {
SurfaceApi = surfaceApi; SurfaceApi = surfaceApi;
} }
_surface = _getSurface(_instance.Instance, api); _surface = _getSurface(_instance.Instance, Api);
_physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId); _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(Api, _instance, _surface, _preferredGpuId);
var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount); var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(Api, _physicalDevice, _surface, out uint maxQueueCount);
_device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, maxQueueCount); _device = VulkanInitialization.CreateDevice(Api, _physicalDevice, queueFamilyIndex, maxQueueCount);
if (api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi))
{ {
SwapchainApi = swapchainApi; SwapchainApi = swapchainApi;
} }
api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue); Api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue);
Queue = queue; Queue = queue;
QueueLock = new object(); QueueLock = new object();
@ -603,11 +600,11 @@ namespace Ryujinx.Graphics.Vulkan
return new HardwareInfo(GpuVendor, GpuRenderer); return new HardwareInfo(GpuVendor, GpuRenderer);
} }
public static DeviceInfo[] GetPhysicalDevices() public static DeviceInfo[] GetPhysicalDevices(Vk api)
{ {
try try
{ {
return VulkanInitialization.GetSuitablePhysicalDevices(Vk.GetApi()); return VulkanInitialization.GetSuitablePhysicalDevices(api);
} }
catch (Exception) catch (Exception)
{ {

View File

@ -200,9 +200,10 @@ namespace Ryujinx.HLE.HOS
LibHacHorizonManager = device.Configuration.LibHacHorizonManager; LibHacHorizonManager = device.Configuration.LibHacHorizonManager;
// We hardcode a clock source id to avoid it changing between each start.
// TODO: use set:sys (and get external clock source id from settings) // TODO: use set:sys (and get external clock source id from settings)
// TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate.
UInt128 clockSourceId = UInt128Utils.CreateRandom(); UInt128 clockSourceId = new UInt128(0x36a0328702ce8bc1, 0x1608eaba02333284);
IRtcManager.GetExternalRtcValue(out ulong rtcValue); IRtcManager.GetExternalRtcValue(out ulong rtcValue);
// We assume the rtc is system time. // We assume the rtc is system time.
@ -222,22 +223,22 @@ namespace Ryujinx.HLE.HOS
internalOffset = internalOffset.AddSeconds(-3600L); internalOffset = internalOffset.AddSeconds(-3600L);
} }
internalOffset = new TimeSpanType(-internalOffset.NanoSeconds); systemTime = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds);
// First init the standard steady clock // First init the standard steady clock
TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false); TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, TimeSpanType.Zero, TimeSpanType.Zero, TimeSpanType.Zero, false);
TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds()); TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds());
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes)) if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
{ {
TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000); TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
// The network system clock needs a valid system clock, as such we setup this system clock using the local system clock. // The network system clock needs a valid system clock, as such we setup this system clock using the local system clock.
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy); TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy);
} }
TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, false, SteadyClockTimePoint.GetRandom()); TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, true, localSytemClockContext.SteadyTimePoint);
// FIXME: TimeZone should be init here but it's actually done in ContentManager // FIXME: TimeZone should be init here but it's actually done in ContentManager
@ -260,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];
@ -327,7 +328,7 @@ namespace Ryujinx.HLE.HOS
private void StartNewServices() private void StartNewServices()
{ {
ServiceTable = new ServiceTable(); ServiceTable = new ServiceTable();
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices)); var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient));
foreach (var service in services) foreach (var service in services)
{ {

View File

@ -92,6 +92,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(23)]
// GetAppletCommonFunctions() -> object<nn::am::service::IAppletCommonFunctions>
public ResultCode GetAppletCommonFunctions(ServiceCtx context)
{
MakeObject(context, new IAppletCommonFunctions());
return ResultCode.Success;
}
[CommandCmif(1000)] [CommandCmif(1000)]
// GetDebugFunctions() -> object<nn::am::service::IDebugFunctions> // GetDebugFunctions() -> object<nn::am::service::IDebugFunctions>
public ResultCode GetDebugFunctions(ServiceCtx context) public ResultCode GetDebugFunctions(ServiceCtx context)

View File

@ -9,8 +9,10 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{ {
class ICommonStateGetter : IpcService class ICommonStateGetter : DisposableIpcService
{ {
private readonly ServiceCtx _context;
private Apm.ManagerServer _apmManagerServer; private Apm.ManagerServer _apmManagerServer;
private Apm.SystemManagerServer _apmSystemManagerServer; private Apm.SystemManagerServer _apmSystemManagerServer;
private Lbl.LblControllerServer _lblControllerServer; private Lbl.LblControllerServer _lblControllerServer;
@ -23,11 +25,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private int _messageEventHandle; private int _messageEventHandle;
private int _displayResolutionChangedEventHandle; private int _displayResolutionChangedEventHandle;
private KEvent _acquiredSleepLockEvent;
private int _acquiredSleepLockEventHandle;
public ICommonStateGetter(ServiceCtx context) public ICommonStateGetter(ServiceCtx context)
{ {
_context = context;
_apmManagerServer = new Apm.ManagerServer(context); _apmManagerServer = new Apm.ManagerServer(context);
_apmSystemManagerServer = new Apm.SystemManagerServer(context); _apmSystemManagerServer = new Apm.SystemManagerServer(context);
_lblControllerServer = new Lbl.LblControllerServer(context); _lblControllerServer = new Lbl.LblControllerServer(context);
_acquiredSleepLockEvent = new KEvent(context.Device.System.KernelContext);
} }
[CommandCmif(0)] [CommandCmif(0)]
@ -117,6 +126,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(10)]
// RequestToAcquireSleepLock()
public ResultCode RequestToAcquireSleepLock(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(13)]
// GetAcquiredSleepLockEvent() -> handle<copy>
public ResultCode GetAcquiredSleepLockEvent(ServiceCtx context)
{
if (_acquiredSleepLockEventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(_acquiredSleepLockEvent.ReadableEvent, out _acquiredSleepLockEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_acquiredSleepLockEventHandle);
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(50)] // 3.0.0+ [CommandCmif(50)] // 3.0.0+
// IsVrModeEnabled() -> b8 // IsVrModeEnabled() -> b8
public ResultCode IsVrModeEnabled(ServiceCtx context) public ResultCode IsVrModeEnabled(ServiceCtx context)
@ -281,5 +318,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success; return ResultCode.Success;
} }
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
if (_acquiredSleepLockEventHandle != 0)
{
_context.Process.HandleTable.CloseHandle(_acquiredSleepLockEventHandle);
_acquiredSleepLockEventHandle = 0;
}
}
}
} }
} }

View File

@ -1,85 +0,0 @@
using LibHac;
using LibHac.Common;
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Services.Arp;
using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator;
namespace Ryujinx.HLE.HOS.Services.Bcat
{
[Service("bcat:a", "bcat:a")]
[Service("bcat:m", "bcat:m")]
[Service("bcat:u", "bcat:u")]
[Service("bcat:s", "bcat:s")]
class IServiceCreator : DisposableIpcService
{
private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _base;
public IServiceCreator(ServiceCtx context, string serviceName)
{
var applicationClient = context.Device.System.LibHacHorizonManager.ApplicationClient;
applicationClient.Sm.GetService(ref _base, serviceName).ThrowIfFailure();
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_base.Destroy();
}
}
[CommandCmif(0)]
// CreateBcatService(pid) -> object<nn::bcat::detail::ipc::IBcatService>
public ResultCode CreateBcatService(ServiceCtx context)
{
// TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId.
// Add an instance of nn::bcat::detail::service::core::PassphraseManager.
// Add an instance of nn::bcat::detail::service::ServiceMemoryManager.
// Add an instance of nn::bcat::detail::service::core::TaskManager who load "bcat-sys:/" system save data and open "dc/task.bin".
// If the file don't exist, create a new one (size of 0x800) and write 2 empty struct with a size of 0x400.
MakeObject(context, new IBcatService(ApplicationLaunchProperty.GetByPid(context)));
// NOTE: If the IBcatService is null this error is returned, Doesn't occur in our case.
// return ResultCode.NullObject;
return ResultCode.Success;
}
[CommandCmif(1)]
// CreateDeliveryCacheStorageService(pid) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService>
public ResultCode CreateDeliveryCacheStorageService(ServiceCtx context)
{
ulong pid = context.RequestData.ReadUInt64();
using var serv = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>();
Result rc = _base.Get.CreateDeliveryCacheStorageService(ref serv.Ref, pid);
if (rc.IsSuccess())
{
MakeObject(context, new IDeliveryCacheStorageService(context, ref serv.Ref));
}
return (ResultCode)rc.Value;
}
[CommandCmif(2)]
// CreateDeliveryCacheStorageServiceWithApplicationId(nn::ApplicationId) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService>
public ResultCode CreateDeliveryCacheStorageServiceWithApplicationId(ServiceCtx context)
{
ApplicationId applicationId = context.RequestData.ReadStruct<ApplicationId>();
using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>();
Result rc = _base.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref service.Ref, applicationId);
if (rc.IsSuccess())
{
MakeObject(context, new IDeliveryCacheStorageService(context, ref service.Ref));
}
return (ResultCode)rc.Value;
}
}
}

View File

@ -1,29 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Bcat
{
enum ResultCode
{
ModuleId = 122,
ErrorCodeShift = 9,
Success = 0,
InvalidArgument = (1 << ErrorCodeShift) | ModuleId,
NotFound = (2 << ErrorCodeShift) | ModuleId,
TargetLocked = (3 << ErrorCodeShift) | ModuleId,
TargetAlreadyMounted = (4 << ErrorCodeShift) | ModuleId,
TargetNotMounted = (5 << ErrorCodeShift) | ModuleId,
AlreadyOpen = (6 << ErrorCodeShift) | ModuleId,
NotOpen = (7 << ErrorCodeShift) | ModuleId,
InternetRequestDenied = (8 << ErrorCodeShift) | ModuleId,
ServiceOpenLimitReached = (9 << ErrorCodeShift) | ModuleId,
SaveDataNotFound = (10 << ErrorCodeShift) | ModuleId,
NetworkServiceAccountNotAvailable = (31 << ErrorCodeShift) | ModuleId,
PassphrasePathNotFound = (80 << ErrorCodeShift) | ModuleId,
DataVerificationFailed = (81 << ErrorCodeShift) | ModuleId,
PermissionDenied = (90 << ErrorCodeShift) | ModuleId,
AllocationFailed = (91 << ErrorCodeShift) | ModuleId,
InvalidOperation = (98 << ErrorCodeShift) | ModuleId,
InvalidDeliveryCacheStorageFile = (204 << ErrorCodeShift) | ModuleId,
StorageOpenLimitReached = (205 << ErrorCodeShift) | ModuleId
}
}

View File

@ -1,18 +0,0 @@
using Ryujinx.HLE.HOS.Services.Arp;
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
class IBcatService : IpcService
{
public IBcatService(ApplicationLaunchProperty applicationLaunchProperty) { }
[CommandCmif(10100)]
// RequestSyncDeliveryCache() -> object<nn::bcat::detail::ipc::IDeliveryCacheProgressService>
public ResultCode RequestSyncDeliveryCache(ServiceCtx context)
{
MakeObject(context, new IDeliveryCacheProgressService(context));
return ResultCode.Success;
}
}
}

View File

@ -1,65 +0,0 @@
using LibHac;
using LibHac.Bcat;
using LibHac.Common;
using Ryujinx.Common;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
class IDeliveryCacheDirectoryService : DisposableIpcService
{
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _base;
public IDeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> baseService)
{
_base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref baseService);
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_base.Destroy();
}
}
[CommandCmif(0)]
// Open(nn::bcat::DirectoryName)
public ResultCode Open(ServiceCtx context)
{
DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>();
Result result = _base.Get.Open(ref directoryName);
return (ResultCode)result.Value;
}
[CommandCmif(1)]
// Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>)
public ResultCode Read(ServiceCtx context)
{
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(region.Memory.Span));
context.ResponseData.Write(entriesRead);
return (ResultCode)result.Value;
}
}
[CommandCmif(2)]
// GetCount() -> u32
public ResultCode GetCount(ServiceCtx context)
{
Result result = _base.Get.GetCount(out int count);
context.ResponseData.Write(count);
return (ResultCode)result.Value;
}
}
}

View File

@ -1,78 +0,0 @@
using LibHac;
using LibHac.Bcat;
using LibHac.Common;
using Ryujinx.Common;
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
class IDeliveryCacheFileService : DisposableIpcService
{
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _base;
public IDeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> baseService)
{
_base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref baseService);
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_base.Destroy();
}
}
[CommandCmif(0)]
// Open(nn::bcat::DirectoryName, nn::bcat::FileName)
public ResultCode Open(ServiceCtx context)
{
DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>();
FileName fileName = context.RequestData.ReadStruct<FileName>();
Result result = _base.Get.Open(ref directoryName, ref fileName);
return (ResultCode)result.Value;
}
[CommandCmif(1)]
// Read(u64) -> (u64, buffer<bytes, 6>)
public ResultCode Read(ServiceCtx context)
{
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
long offset = context.RequestData.ReadInt64();
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _base.Get.Read(out long bytesRead, offset, region.Memory.Span);
context.ResponseData.Write(bytesRead);
return (ResultCode)result.Value;
}
}
[CommandCmif(2)]
// GetSize() -> u64
public ResultCode GetSize(ServiceCtx context)
{
Result result = _base.Get.GetSize(out long size);
context.ResponseData.Write(size);
return (ResultCode)result.Value;
}
[CommandCmif(3)]
// GetDigest() -> nn::bcat::Digest
public ResultCode GetDigest(ServiceCtx context)
{
Result result = _base.Get.GetDigest(out Digest digest);
context.ResponseData.WriteStruct(digest);
return (ResultCode)result.Value;
}
}
}

View File

@ -1,63 +0,0 @@
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types;
using Ryujinx.Horizon.Common;
using System;
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
class IDeliveryCacheProgressService : IpcService
{
private KEvent _event;
private int _eventHandle;
public IDeliveryCacheProgressService(ServiceCtx context)
{
_event = new KEvent(context.Device.System.KernelContext);
}
[CommandCmif(0)]
// GetEvent() -> handle<copy>
public ResultCode GetEvent(ServiceCtx context)
{
if (_eventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle);
Logger.Stub?.PrintStub(LogClass.ServiceBcat);
return ResultCode.Success;
}
[CommandCmif(1)]
// GetImpl() -> buffer<nn::bcat::detail::DeliveryCacheProgressImpl, 0x1a>
public ResultCode GetImpl(ServiceCtx context)
{
DeliveryCacheProgressImpl deliveryCacheProgress = new DeliveryCacheProgressImpl
{
State = DeliveryCacheProgressImpl.Status.Done,
Result = 0
};
ulong dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress);
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(dcpSize);
Logger.Stub?.PrintStub(LogClass.ServiceBcat);
return ResultCode.Success;
}
private ulong WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress)
{
return MemoryHelper.Write(context.Memory, ipcDesc.Position, deliveryCacheProgress);
}
}
}

View File

@ -1,74 +0,0 @@
using LibHac;
using LibHac.Bcat;
using LibHac.Common;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
class IDeliveryCacheStorageService : DisposableIpcService
{
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _base;
public IDeliveryCacheStorageService(ServiceCtx context, ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> baseService)
{
_base = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref baseService);
}
[CommandCmif(0)]
// CreateFileService() -> object<nn::bcat::detail::ipc::IDeliveryCacheFileService>
public ResultCode CreateFileService(ServiceCtx context)
{
using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>();
Result result = _base.Get.CreateFileService(ref service.Ref);
if (result.IsSuccess())
{
MakeObject(context, new IDeliveryCacheFileService(ref service.Ref));
}
return (ResultCode)result.Value;
}
[CommandCmif(1)]
// CreateDirectoryService() -> object<nn::bcat::detail::ipc::IDeliveryCacheDirectoryService>
public ResultCode CreateDirectoryService(ServiceCtx context)
{
using var service = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>();
Result result = _base.Get.CreateDirectoryService(ref service.Ref);
if (result.IsSuccess())
{
MakeObject(context, new IDeliveryCacheDirectoryService(ref service.Ref));
}
return (ResultCode)result.Value;
}
[CommandCmif(10)]
// EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>)
public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context)
{
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(region.Memory.Span));
context.ResponseData.Write(count);
return (ResultCode)result.Value;
}
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_base.Destroy();
}
}
}
}

View File

@ -0,0 +1,13 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.Clock.Types
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ContinuousAdjustmentTimePoint
{
public ulong ClockOffset;
public long Multiplier;
public long DivisorLog2;
public SystemClockContext Context;
}
}

View File

@ -1,8 +1,8 @@
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.Clock.Types;
using Ryujinx.HLE.HOS.Services.Time.Types; using Ryujinx.HLE.HOS.Services.Time.Types;
using Ryujinx.HLE.Utilities;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
@ -16,10 +16,11 @@ namespace Ryujinx.HLE.HOS.Services.Time
private SharedMemoryStorage _timeSharedMemoryStorage; private SharedMemoryStorage _timeSharedMemoryStorage;
private int _timeSharedMemorySize; private int _timeSharedMemorySize;
private const uint SteadyClockContextOffset = 0x00; private const uint SteadyClockContextOffset = 0x00;
private const uint LocalSystemClockContextOffset = 0x38; private const uint LocalSystemClockContextOffset = 0x38;
private const uint NetworkSystemClockContextOffset = 0x80; private const uint NetworkSystemClockContextOffset = 0x80;
private const uint AutomaticCorrectionEnabledOffset = 0xC8; private const uint AutomaticCorrectionEnabledOffset = 0xC8;
private const uint ContinuousAdjustmentTimePointOffset = 0xD0;
public void Initialize(Switch device, KSharedMemory sharedMemory, SharedMemoryStorage timeSharedMemoryStorage, int timeSharedMemorySize) public void Initialize(Switch device, KSharedMemory sharedMemory, SharedMemoryStorage timeSharedMemoryStorage, int timeSharedMemorySize)
{ {
@ -39,15 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
public void SetupStandardSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint) public void SetupStandardSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint)
{ {
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); UpdateSteadyClock(tickSource, clockSourceId, currentTimePoint);
SteadyClockContext context = new SteadyClockContext
{
InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds),
ClockSourceId = clockSourceId
};
WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context);
} }
public void SetAutomaticCorrectionEnabled(bool isAutomaticCorrectionEnabled) public void SetAutomaticCorrectionEnabled(bool isAutomaticCorrectionEnabled)
@ -58,10 +51,38 @@ namespace Ryujinx.HLE.HOS.Services.Time
public void SetSteadyClockRawTimePoint(ITickSource tickSource, TimeSpanType currentTimePoint) public void SetSteadyClockRawTimePoint(ITickSource tickSource, TimeSpanType currentTimePoint)
{ {
SteadyClockContext context = ReadObjectFromSharedMemory<SteadyClockContext>(SteadyClockContextOffset, 4); SteadyClockContext context = ReadObjectFromSharedMemory<SteadyClockContext>(SteadyClockContextOffset, 4);
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
context.InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds); UpdateSteadyClock(tickSource, context.ClockSourceId, currentTimePoint);
}
private void UpdateSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint)
{
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
ContinuousAdjustmentTimePoint adjustmentTimePoint = new ContinuousAdjustmentTimePoint
{
ClockOffset = (ulong)ticksTimeSpan.NanoSeconds,
Multiplier = 1,
DivisorLog2 = 0,
Context = new SystemClockContext
{
Offset = 0,
SteadyTimePoint = new SteadyClockTimePoint
{
ClockSourceId = clockSourceId,
TimePoint = 0
}
}
};
WriteObjectToSharedMemory(ContinuousAdjustmentTimePointOffset, 4, adjustmentTimePoint);
SteadyClockContext context = new SteadyClockContext
{
InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds),
ClockSourceId = clockSourceId
};
WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context); WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context);
} }

View File

@ -116,7 +116,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
device, device,
device.System.KernelContext, device.System.KernelContext,
metaLoader, metaLoader,
nacpData.Value, nacpData,
enablePtc, enablePtc,
allowCodeMemoryForJit, allowCodeMemoryForJit,
programName, programName,

View File

@ -209,7 +209,7 @@ namespace Ryujinx.HLE.Loaders.Processes
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device, ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
_device.System.KernelContext, _device.System.KernelContext,
dummyExeFs.GetNpdm(), dummyExeFs.GetNpdm(),
nacpData.Value, nacpData,
diskCacheEnabled: false, diskCacheEnabled: false,
allowCodeMemoryForJit: true, allowCodeMemoryForJit: true,
programName, programName,

View File

@ -219,7 +219,7 @@ namespace Ryujinx.HLE.Loaders.Processes
Switch device, Switch device,
KernelContext context, KernelContext context,
MetaLoader metaLoader, MetaLoader metaLoader,
ApplicationControlProperty applicationControlProperties, BlitStruct<ApplicationControlProperty> applicationControlProperties,
bool diskCacheEnabled, bool diskCacheEnabled,
bool allowCodeMemoryForJit, bool allowCodeMemoryForJit,
string name, string name,
@ -355,7 +355,7 @@ namespace Ryujinx.HLE.Loaders.Processes
context.Device.System.TickSource, context.Device.System.TickSource,
context.Device.Gpu, context.Device.Gpu,
$"{programId:x16}", $"{programId:x16}",
applicationControlProperties.DisplayVersionString.ToString(), applicationControlProperties.Value.DisplayVersionString.ToString(),
diskCacheEnabled, diskCacheEnabled,
codeStart, codeStart,
codeSize); codeSize);

View File

@ -1,4 +1,5 @@
using LibHac.Loader; using LibHac.Common;
using LibHac.Loader;
using LibHac.Ns; using LibHac.Ns;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Cpu; using Ryujinx.Cpu;
@ -9,9 +10,9 @@ using System.Linq;
namespace Ryujinx.HLE.Loaders.Processes namespace Ryujinx.HLE.Loaders.Processes
{ {
public struct ProcessResult public class ProcessResult
{ {
public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish); public static ProcessResult Failed => new(null, new BlitStruct<ApplicationControlProperty>(1), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish);
private readonly byte _mainThreadPriority; private readonly byte _mainThreadPriority;
private readonly uint _mainThreadStackSize; private readonly uint _mainThreadStackSize;
@ -31,15 +32,15 @@ namespace Ryujinx.HLE.Loaders.Processes
public readonly bool AllowCodeMemoryForJit; public readonly bool AllowCodeMemoryForJit;
public ProcessResult( public ProcessResult(
MetaLoader metaLoader, MetaLoader metaLoader,
ApplicationControlProperty applicationControlProperties, BlitStruct<ApplicationControlProperty> applicationControlProperties,
bool diskCacheEnabled, bool diskCacheEnabled,
bool allowCodeMemoryForJit, bool allowCodeMemoryForJit,
IDiskCacheLoadState diskCacheLoadState, IDiskCacheLoadState diskCacheLoadState,
ulong pid, ulong pid,
byte mainThreadPriority, byte mainThreadPriority,
uint mainThreadStackSize, uint mainThreadStackSize,
TitleLanguage titleLanguage) TitleLanguage titleLanguage)
{ {
_mainThreadPriority = mainThreadPriority; _mainThreadPriority = mainThreadPriority;
_mainThreadStackSize = mainThreadStackSize; _mainThreadStackSize = mainThreadStackSize;
@ -48,7 +49,7 @@ namespace Ryujinx.HLE.Loaders.Processes
ProcessId = pid; ProcessId = pid;
MetaLoader = metaLoader; MetaLoader = metaLoader;
ApplicationControlProperties = applicationControlProperties; ApplicationControlProperties = applicationControlProperties.Value;
if (metaLoader is not null) if (metaLoader is not null)
{ {

View File

@ -487,11 +487,12 @@ namespace Ryujinx.Headless.SDL2
if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow)
{ {
string preferredGpuId = string.Empty; string preferredGpuId = string.Empty;
Vk api = Vk.GetApi();
if (!string.IsNullOrEmpty(options.PreferredGpuVendor)) if (!string.IsNullOrEmpty(options.PreferredGpuVendor))
{ {
string preferredGpuVendor = options.PreferredGpuVendor.ToLowerInvariant(); string preferredGpuVendor = options.PreferredGpuVendor.ToLowerInvariant();
var devices = VulkanRenderer.GetPhysicalDevices(); var devices = VulkanRenderer.GetPhysicalDevices(api);
foreach (var device in devices) foreach (var device in devices)
{ {
@ -504,6 +505,7 @@ namespace Ryujinx.Headless.SDL2
} }
return new VulkanRenderer( return new VulkanRenderer(
api,
(instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))), (instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))),
vulkanWindow.GetRequiredInstanceExtensions, vulkanWindow.GetRequiredInstanceExtensions,
preferredGpuId); preferredGpuId);

View File

@ -145,7 +145,7 @@ namespace Ryujinx.Horizon.Generators.Hipc
if (bufferFixedSize != null) if (bufferFixedSize != null)
{ {
arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})"; arg = $"new CommandArg({bufferFlags} | HipcBufferFlags.FixedSize, {bufferFixedSize})";
} }
else else
{ {

View File

@ -0,0 +1,48 @@
using Ryujinx.Horizon.Bcat.Ipc;
using Ryujinx.Horizon.Bcat.Types;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
namespace Ryujinx.Horizon.Bcat
{
internal class BcatIpcServer
{
private const int BcatMaxSessionsCount = 8;
private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4;
private const int PointerBufferSize = 0x400;
private const int MaxDomains = 64;
private const int MaxDomainObjects = 64;
private const int MaxPortsCount = 4;
private SmApi _sm;
private BcatServerManager _serverManager;
private static readonly ManagerOptions _bcatManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
internal void Initialize()
{
HeapAllocator allocator = new();
_sm = new SmApi();
_sm.Initialize().AbortOnFailure();
_serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _bcatManagerOptions, BcatTotalMaxSessionsCount);
_serverManager.RegisterServer((int)BcatPortIndex.Admin, ServiceName.Encode("bcat:a"), BcatMaxSessionsCount);
_serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), BcatMaxSessionsCount);
_serverManager.RegisterServer((int)BcatPortIndex.User, ServiceName.Encode("bcat:u"), BcatMaxSessionsCount);
_serverManager.RegisterServer((int)BcatPortIndex.System, ServiceName.Encode("bcat:s"), BcatMaxSessionsCount);
}
public void ServiceRequests()
{
_serverManager.ServiceRequests();
}
public void Shutdown()
{
_serverManager.Dispose();
}
}
}

View File

@ -0,0 +1,24 @@
using Ryujinx.Horizon.LogManager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ryujinx.Horizon.Bcat
{
internal class BcatMain : IService
{
public static void Main(ServiceTable serviceTable)
{
BcatIpcServer ipcServer = new();
ipcServer.Initialize();
serviceTable.SignalServiceReady();
ipcServer.ServiceRequests();
ipcServer.Shutdown();
}
}
}

View File

@ -0,0 +1,29 @@
using Ryujinx.Horizon.Common;
namespace Ryujinx.Horizon.Bcat
{
class BcatResult
{
private const int ModuleId = 122;
public static Result Success => new(ModuleId, 0);
public static Result InvalidArgument => new(ModuleId, 1);
public static Result NotFound => new(ModuleId, 2);
public static Result TargetLocked => new(ModuleId, 3);
public static Result TargetAlreadyMounted => new(ModuleId, 4);
public static Result TargetNotMounted => new(ModuleId, 5);
public static Result AlreadyOpen => new(ModuleId, 6);
public static Result NotOpen => new(ModuleId, 7);
public static Result InternetRequestDenied => new(ModuleId, 8);
public static Result ServiceOpenLimitReached => new(ModuleId, 9);
public static Result SaveDataNotFound => new(ModuleId, 10);
public static Result NetworkServiceAccountNotAvailable => new(ModuleId, 31);
public static Result PassphrasePathNotFound => new(ModuleId, 80);
public static Result DataVerificationFailed => new(ModuleId, 81);
public static Result PermissionDenied => new(ModuleId, 90);
public static Result AllocationFailed => new(ModuleId, 91);
public static Result InvalidOperation => new(ModuleId, 98);
public static Result InvalidDeliveryCacheStorageFile => new(ModuleId, 204);
public static Result StorageOpenLimitReached => new(ModuleId, 205);
}
}

View File

@ -0,0 +1,28 @@
using Ryujinx.Horizon.Bcat.Ipc;
using Ryujinx.Horizon.Bcat.Types;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
using System;
namespace Ryujinx.Horizon.Bcat
{
class BcatServerManager : ServerManager
{
public BcatServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions)
{
}
protected override Result OnNeedsToAccept(int portIndex, Server server)
{
return (BcatPortIndex)portIndex switch
{
BcatPortIndex.Admin => AcceptImpl(server, new ServiceCreator("bcat:a", BcatServicePermissionLevel.Admin)),
BcatPortIndex.Manager => AcceptImpl(server, new ServiceCreator("bcat:m", BcatServicePermissionLevel.Manager)),
BcatPortIndex.User => AcceptImpl(server, new ServiceCreator("bcat:u", BcatServicePermissionLevel.User)),
BcatPortIndex.System => AcceptImpl(server, new ServiceCreator("bcat:s", BcatServicePermissionLevel.System)),
_ => throw new ArgumentOutOfRangeException(nameof(portIndex)),
};
}
}
}

View File

@ -0,0 +1,85 @@
using LibHac.Common;
using Ryujinx.Horizon.Bcat.Types;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Bcat;
using Ryujinx.Horizon.Sdk.Sf;
using System;
using System.Threading;
using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId;
namespace Ryujinx.Horizon.Bcat.Ipc
{
partial class ServiceCreator : IServiceCreator, IDisposable
{
private readonly BcatServicePermissionLevel _permissionLevel;
private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _libHacService;
private int _disposalState;
public ServiceCreator(string serviceName, BcatServicePermissionLevel permissionLevel)
{
HorizonStatic.Options.BcatClient.Sm.GetService(ref _libHacService, serviceName).ThrowIfFailure();
_permissionLevel = permissionLevel;
}
[CmifCommand(0)]
public Result CreateBcatService(out IBcatService bcatService, [ClientProcessId] ulong pid)
{
// TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId.
// Add an instance of nn::bcat::detail::service::core::PassphraseManager.
// Add an instance of nn::bcat::detail::service::ServiceMemoryManager.
// Add an instance of nn::bcat::detail::service::core::TaskManager who loads "bcat-sys:/" system save data and opens "dc/task.bin".
// If the file don't exist, create a new one (with a size of 0x800 bytes) and write 2 empty structs with a size of 0x400 bytes.
bcatService = new BcatService(_permissionLevel);
return Result.Success;
}
[CmifCommand(1)]
public Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, [ClientProcessId] ulong pid)
{
using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>();
var resultCode = _libHacService.Get.CreateDeliveryCacheStorageService(ref libHacService.Ref, pid);
if (resultCode.IsSuccess())
{
service = new DeliveryCacheStorageService(ref libHacService.Ref);
}
else
{
service = null;
}
return resultCode.ToHorizonResult();
}
[CmifCommand(2)]
public Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, ApplicationId applicationId)
{
using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>();
var resultCode = _libHacService.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref libHacService.Ref, new LibHac.ApplicationId(applicationId.Id));
if (resultCode.IsSuccess())
{
service = new DeliveryCacheStorageService(ref libHacService.Ref);
}
else
{
service = null;
}
return resultCode.ToHorizonResult();
}
public void Dispose()
{
if (Interlocked.Exchange(ref _disposalState, 1) == 0)
{
_libHacService.Destroy();
}
}
}
}

View File

@ -0,0 +1,25 @@
using Ryujinx.Horizon.Bcat.Types;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Bcat;
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Bcat.Ipc
{
partial class BcatService : IBcatService
{
private readonly BcatServicePermissionLevel _permissionLevel;
public BcatService(BcatServicePermissionLevel permissionLevel)
{
_permissionLevel = permissionLevel;
}
[CmifCommand(10100)]
public Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService)
{
deliveryCacheProgressService = new DeliveryCacheProgressService();
return Result.Success;
}
}
}

View File

@ -0,0 +1,48 @@
using LibHac.Bcat;
using LibHac.Common;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Bcat;
using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;
using System.Threading;
namespace Ryujinx.Horizon.Bcat.Ipc
{
partial class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService, IDisposable
{
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _libHacService;
private int _disposalState;
public DeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> libHacService)
{
_libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref libHacService);
}
[CmifCommand(0)]
public Result Open(DirectoryName directoryName)
{
return _libHacService.Get.Open(ref directoryName).ToHorizonResult();
}
[CmifCommand(1)]
public Result Read(out int entriesRead, [Buffer(Sdk.Sf.Hipc.HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeliveryCacheDirectoryEntry> entriesBuffer)
{
return _libHacService.Get.Read(out entriesRead, entriesBuffer).ToHorizonResult();
}
[CmifCommand(2)]
public Result GetCount(out int count)
{
return _libHacService.Get.GetCount(out count).ToHorizonResult();
}
public void Dispose()
{
if (Interlocked.Exchange(ref _disposalState, 1) == 0)
{
_libHacService.Destroy();
}
}
}
}

View File

@ -0,0 +1,54 @@
using LibHac.Bcat;
using LibHac.Common;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Bcat;
using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;
using System.Threading;
namespace Ryujinx.Horizon.Bcat.Ipc
{
partial class DeliveryCacheFileService : IDeliveryCacheFileService, IDisposable
{
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _libHacService;
private int _disposalState;
public DeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> libHacService)
{
_libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref libHacService);
}
[CmifCommand(0)]
public Result Open(DirectoryName directoryName, FileName fileName)
{
return _libHacService.Get.Open(ref directoryName, ref fileName).ToHorizonResult();
}
[CmifCommand(1)]
public Result Read(long offset, out long bytesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> data)
{
return _libHacService.Get.Read(out bytesRead, offset, data).ToHorizonResult();
}
[CmifCommand(2)]
public Result GetSize(out long size)
{
return _libHacService.Get.GetSize(out size).ToHorizonResult();
}
[CmifCommand(3)]
public Result GetDigest(out Digest digest)
{
return _libHacService.Get.GetDigest(out digest).ToHorizonResult();
}
public void Dispose()
{
if (Interlocked.Exchange(ref _disposalState, 1) == 0)
{
_libHacService.Destroy();
}
}
}
}

View File

@ -0,0 +1,58 @@
using Ryujinx.Common.Logging;
using Ryujinx.Horizon.Bcat.Ipc.Types;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Bcat;
using Ryujinx.Horizon.Sdk.OsTypes;
using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;
using System.Threading;
namespace Ryujinx.Horizon.Bcat.Ipc
{
partial class DeliveryCacheProgressService : IDeliveryCacheProgressService, IDisposable
{
private int _handle;
private SystemEventType _systemEvent;
private int _disposalState;
[CmifCommand(0)]
public Result GetEvent([CopyHandle] out int handle)
{
if (_handle == 0)
{
Os.CreateSystemEvent(out _systemEvent, EventClearMode.ManualClear, true).AbortOnFailure();
_handle = Os.GetReadableHandleOfSystemEvent(ref _systemEvent);
}
handle = _handle;
Logger.Stub?.PrintStub(LogClass.ServiceBcat);
return Result.Success;
}
[CmifCommand(1)]
public Result GetImpl([Buffer(HipcBufferFlags.Out | HipcBufferFlags.Pointer, 0x200)] out DeliveryCacheProgressImpl deliveryCacheProgressImpl)
{
deliveryCacheProgressImpl = new DeliveryCacheProgressImpl
{
State = DeliveryCacheProgressImpl.Status.Done,
Result = 0
};
Logger.Stub?.PrintStub(LogClass.ServiceBcat);
return Result.Success;
}
public void Dispose()
{
if (_handle != 0 && Interlocked.Exchange(ref _disposalState, 1) == 0)
{
Os.DestroySystemEvent(ref _systemEvent);
}
}
}
}

View File

@ -0,0 +1,74 @@
using LibHac.Bcat;
using LibHac.Common;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Bcat;
using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using System;
using System.Threading;
namespace Ryujinx.Horizon.Bcat.Ipc
{
partial class DeliveryCacheStorageService : IDeliveryCacheStorageService, IDisposable
{
private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _libHacService;
private int _disposalState;
public DeliveryCacheStorageService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> libHacService)
{
_libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref libHacService);
}
[CmifCommand(0)]
public Result CreateFileService(out IDeliveryCacheFileService service)
{
using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>();
var resultCode = _libHacService.Get.CreateFileService(ref libHacService.Ref);
if (resultCode.IsSuccess())
{
service = new DeliveryCacheFileService(ref libHacService.Ref);
}
else
{
service = null;
}
return resultCode.ToHorizonResult();
}
[CmifCommand(1)]
public Result CreateDirectoryService(out IDeliveryCacheDirectoryService service)
{
using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>();
var resultCode = _libHacService.Get.CreateDirectoryService(ref libHacService.Ref);
if (resultCode.IsSuccess())
{
service = new DeliveryCacheDirectoryService(ref libHacService.Ref);
}
else
{
service = null;
}
return resultCode.ToHorizonResult();
}
[CmifCommand(10)]
public Result EnumerateDeliveryCacheDirectory(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DirectoryName> directoryNames)
{
return _libHacService.Get.EnumerateDeliveryCacheDirectory(out count, directoryNames).ToHorizonResult();
}
public void Dispose()
{
if (Interlocked.Exchange(ref _disposalState, 1) == 0)
{
_libHacService.Destroy();
}
}
}
}

View File

@ -1,6 +1,6 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator.Types namespace Ryujinx.Horizon.Bcat.Ipc.Types
{ {
[StructLayout(LayoutKind.Sequential, Size = 0x200)] [StructLayout(LayoutKind.Sequential, Size = 0x200)]
public struct DeliveryCacheProgressImpl public struct DeliveryCacheProgressImpl

Some files were not shown because too many files have changed in this diff Show More