Compare commits

..

33 Commits

Author SHA1 Message Date
gdkchan
cdccf89e10 Revert "Disable keyboard controller input while swkbd is open (foreground) (#…" (#6805)
This reverts commit a3dc295c5f.
2024-05-15 02:20:24 -03:00
Ac_K
2ca0b17339 New Crowdin updates (#6590)
* New translations en_us.json (Arabic)

* New translations en_us.json (Russian)

* New translations en_us.json (Chinese Traditional)

* New translations en_us.json (French)

* New translations en_us.json (Russian)

* New translations en_us.json (Spanish)

* New translations en_us.json (Arabic)

* New translations en_us.json (German)

* New translations en_us.json (Greek)

* New translations en_us.json (Hebrew)

* New translations en_us.json (Italian)

* New translations en_us.json (Japanese)

* New translations en_us.json (Korean)

* New translations en_us.json (Polish)

* New translations en_us.json (Russian)

* New translations en_us.json (Turkish)

* New translations en_us.json (Ukrainian)

* New translations en_us.json (Chinese Simplified)

* New translations en_us.json (Chinese Traditional)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Thai)

* New translations en_us.json (French)

* New translations en_us.json (Arabic)

* New translations en_us.json (Italian)

* New translations en_us.json (Korean)

* New translations en_us.json (Russian)

* New translations en_us.json (Ukrainian)

* New translations en_us.json (Chinese Simplified)

* New translations en_us.json (Arabic)

* New translations en_us.json (Polish)

* New translations en_us.json (Turkish)

* New translations en_us.json (Arabic)

* New translations en_us.json (Chinese Traditional)

* New translations en_us.json (Russian)

* New translations en_us.json (French)

* New translations en_us.json (Thai)

* New translations en_us.json (Spanish)

* New translations en_us.json (Arabic)

* New translations en_us.json (German)

* New translations en_us.json (Greek)

* New translations en_us.json (Hebrew)

* New translations en_us.json (Italian)

* New translations en_us.json (Japanese)

* New translations en_us.json (Korean)

* New translations en_us.json (Polish)

* New translations en_us.json (Russian)

* New translations en_us.json (Turkish)

* New translations en_us.json (Ukrainian)

* New translations en_us.json (Chinese Simplified)

* New translations en_us.json (Chinese Traditional)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Thai)

* New translations en_us.json (French)

* New translations en_us.json (Arabic)

* New translations en_us.json (Chinese Traditional)

* New translations en_us.json (Arabic)

* New translations en_us.json (Italian)

* New translations en_us.json (Arabic)

* New translations en_us.json (Italian)

* New translations en_us.json (Spanish)

* New translations en_us.json (Russian)

* New translations en_us.json (Russian)

* New translations en_us.json (Thai)

* New translations en_us.json (Spanish)

* New translations en_us.json (Arabic)

* New translations en_us.json (German)

* New translations en_us.json (Greek)

* New translations en_us.json (Hebrew)

* New translations en_us.json (Italian)

* New translations en_us.json (Japanese)

* New translations en_us.json (Korean)

* New translations en_us.json (Polish)

* New translations en_us.json (Russian)

* New translations en_us.json (Turkish)

* New translations en_us.json (Ukrainian)

* New translations en_us.json (Chinese Simplified)

* New translations en_us.json (Chinese Traditional)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (Thai)

* New translations en_us.json (French)

* New translations en_us.json (Chinese Traditional)

* New translations en_us.json (Arabic)

* New translations en_us.json (Russian)

* New translations en_us.json (Turkish)

* New translations en_us.json (Chinese Simplified)

* New translations en_us.json (Russian)

* New translations en_us.json (Chinese Simplified)

* New translations en_us.json (Russian)

* New translations en_us.json (Portuguese, Brazilian)

* New translations en_us.json (German)
2024-05-14 17:47:03 +02:00
Isaac Marovitz
47639e6eeb Bump Avalonia.Svg (#6603)
* Bump Avalonia.Svg

* Remove using

* Bump Version

* Remove other reload
2024-05-14 17:36:11 +02:00
gdkchan
cd78adf07f Add missing lock on texture cache UpdateMapping method (#6657) 2024-05-14 17:23:13 +02:00
TSRBerry
a3dc295c5f Disable keyboard controller input while swkbd is open (foreground) (#6646)
* Block input updates while swkbd is open in foreground mode

* Flush internal driver state before unblocking input updates

* Rename Flush to Clear and remove unnecessary attribute
2024-05-14 17:14:39 +02:00
gdkchan
2ef4f92b07 Make TextureGroup.ClearModified thread safe (#6686) 2024-05-14 17:06:36 +02:00
yell0wsuit
2b6cc4b353 Add the "Auto" theme option in setting (#6611)
* Add "Follow OS theme" option

* Update App.axaml.cs

* Add "Follow OS theme" option

* Update App.axaml.cs

* Remove `this`

* Remove annotation for nullable reference

* Change into switch expression to make it concise

* Change comments to XML docs

* Update en_US.json

* Fix icons in About dialog do not response to "auto" theme

The theme icons seemingly use Dark variant event when the OS theme is light. In addition, I added ThemeManager common to make it accessible for both App and AboutWindow

* Newline at the end

* newline moment

* Update ThemeManager.cs

* bait to switch to lf

* change to lf

* temp. revert

* Add back ThemeManager.cs common, pls pass the format check

* I found the mistake: should have put `ThemeManager.OnThemeChanged();` in try block

Finally solve the formatting check

* test formatting

* Update App.axaml.cs

* Ok i seem to forget to add version lol

* Fix info CA1816
2024-05-14 17:00:03 +02:00
Gavin Zyonse
075575200d Update compatibility information in README.md (#6801) 2024-05-14 16:58:48 +02:00
gdkchan
3a3b51893e Add support for bindless textures from storage buffer on Vulkan (#6721)
* Halve primitive ID when converting quads to triangles

* Shader cache version bump

* Add support for bindless textures from storage buffer on Vulkan
2024-05-14 16:47:16 +02:00
MutantAura
44dbab3848 discordRPC: Truncate game title and details if they exceed discord byte limit. (#6581)
* Truncate game title and details if they exceed DiscordRPC limit.

* Update implementation to a byte total check.

* Track if the string has actually been modified correctly.

* Allow an early function return and simplify logic.

* Better handling of large input strings and minimise loop opportunities.

* Remove unused using.

* Update to `applicationName` over `titleName`.
2024-05-14 16:36:44 +02:00
Tsubasa0504
cada4d04ef HID: Stub IHidServer: 134 (SetNpadAnalogStickUseCenterClamp) (#6664)
* Add files via upload

* Update IHidServer.cs

mistakes...

* format

how do i do it

* Update src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs

Co-authored-by: Agatem <agaatem@outlook.com>

* Update src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs

Co-authored-by: Agatem <agaatem@outlook.com>

* bruh

* Apply suggestions from code review

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

* use readuint32 instead

* second thought

* i hope it works

thanks someone higher up with the same thing

* pid

* Apply suggestions from code review

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

* styles i think

* Apply suggestions from code review

Co-authored-by: makigumo <makigumo@users.noreply.github.com>

---------

Co-authored-by: Agatem <agaatem@outlook.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: makigumo <makigumo@users.noreply.github.com>
2024-05-14 16:26:49 +02:00
Luke Warner
e9edf0ab7f Update outdated Windows version warning (#6481)
* Change text

* clarify version number

* update gtk
2024-05-14 16:19:43 +02:00
TSRBerry
6e40b64554 Add linux specific files to local builds (#6762) 2024-05-14 16:06:40 +02:00
Isaac Marovitz
1a676ee913 Update DotSettings (#6535) 2024-05-14 15:59:28 +02:00
Marco Carvalho
a23d8cb92f Replace "List.ForEach" for "foreach" (#6783)
* Replace "List.ForEach" for "foreach"

* dotnet format

* Update Ptc.cs

* Update GpuContext.cs
2024-05-08 13:53:25 +02:00
Nicolas Abram
ab12fbe963 Fix system dateTime loading in avalonia LoadCurrentConfiguration (#6676)
* Fix system dateTime loading in avalonia LoadCurrentConfiguration

* Rename local var to not use upper camel case
2024-05-02 13:33:28 +02:00
MutantAura
d0cc13ce0b UI: Fix some MainWindow bugs and implement menubar items to change window size. (#6750)
* Do not save window dimensions when maximized.

* Implement option to disable window size/position memory.

* Remove logging statements

* Implement menubar items for common window sizes.

* formatting fixes

* Set 720p window as a composite value.

* Remove unused using

* Fix exception paramter.

* Force restore window when a size change is requested

* Fix some resizing bugs.
2024-05-01 18:21:24 +02:00
Andrew Glaze
65c035cdf8 Fix alt key appearing as control in settings menus (#6742) 2024-04-29 18:18:27 +01:00
Exhigh
56c5dbe557 Fix Cursor States On Windows (#6725)
* [Ava]: Fix Cursor States On Windows

It's been sometime since the last PR #5415 was made and last time i was waiting for Ava 11 to be merged before re-writing the code and along the way forgot about the PR.

Anyway this PR supersedes both #5288 and #5415, and fixes issue: #5136

* Now, the bounds for which the cursor should be detected in renderer should be accurate to any scaling / resolution, taking into account the status and the menu bar. ( This issue was partially resolved by #6450 )

* Reduced the number of times the cursor updates from per frame update to updating only when the cursor state needs to be changed.

* Fixed the issue wherein you weren't able to resize the window, because of the cursor passthrough which caused the cursor to reset from the reset icon or flicker.

* Fixed the issue caused by #6450 which caused the cursor to disappear over the submenus while cursor was set to always hide.

* Changed the cursor state to not disappear while the game is being loaded. ( Needs Feedback ).

* Removed an unused library import.

* PR feedback

* Fix excessive line breaks and whitespaces and other feedback
* Add a check before calculating cursor idle time, such that it calculates only while the cursor mode is OnIdle.

* PR Feedback

* Rework the cursor state check code block

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* PR Feedback

* A simpler version of the previous implementation.

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* PR Feedback

* PR Feedback

---------

Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
2024-04-28 20:21:08 -03:00
MaxLastBreath
5976a5161b Fix direct keyboard not working when using a Controller. (#6716)
* Fix direct keyboard not working when connected with a controller

- Pass KeyboardDriver to NpadController.GetHLEKeyboardInput().
- Always fetch all keyboards if Direct Keyboard is turned on.
- Remove unnecessary return null.

* Get Keyboard Inputs outside of the controller loop.

- Moved GetHLEKeyboardInput outside of the controller loop.
- Made GetHLEKeyboardInput public static from public

* Removed extra newline

* Update src/Ryujinx.Input/HLE/NpadManager.cs

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

* Update src/Ryujinx.Input/HLE/NpadController.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2024-04-28 19:02:29 +02:00
riperiperi
3d4dea624d HID: Correct direct mouse deltas (#6736)
The delta position of the mouse should be the difference between the current and last position. Subtracting the last deltas doesn't really make sense.

Won't implement pointer lock for first person games, but might stop some super weird behaviour with the mouse values appearing totally random.
2024-04-28 18:55:37 +02:00
TSRBerry
89a274c6a6 ci: Replace macos-latest label with macos-13 (#6729)
Due to a change to the GitHub runner labels a few days ago (see: actions/runner#3256) our build workflows for macOS x64 didn't work anymore.
According to https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories the macos-13 label is not using arm64 yet.

Until a better solution is offered in the linked issue above, we'll keep using the macos-13 label which hopefully doesn't switch to arm64 soon.
2024-04-26 20:36:35 -03:00
gdkchan
c6f8bfed90 Add support for bindless textures from shader input (vertex buffer) on Vulkan (#6577)
* Add support for bindless textures from shader input (vertex buffer)

* Shader cache version bump

* Format whitespace

* Remove cache entries on pool removal, disable for OpenGL

* PR feedback
2024-04-22 15:05:55 -03:00
jhorv
9b94662b4b implement MemoryManagerHostTracked.GetReadOnlySequence() (#6695)
* implement `MemoryManagerHostTracked.GetReadOnlySequence()`, fixes crashes on game starts on MacOS

* whitespace fixes

* whitespace fixes

* add missing call to `SignalMemoryTracking()`

* adjust call to `SignalMemoryTracking()``

* don't use GetPhysicalAddressMemory()

* add newline for consistency
2024-04-21 16:34:04 -03:00
jhorv
216026c096 Use pooled memory and avoid memory copies (#6691)
* perf: use ByteMemoryPool

* feat: KPageTableBase/KPageTable new methods to read and write `ReadOnlySequence<byte>`

* new: add IWritableBlock.Write(ulong, ReadOnlySequence<byte>) with default impl

* perf: use GetReadOnlySequence() instead of GetSpan()

* perf: make `Parcel` IDisposable, use `ByteMemoryPool` for internal allocation, and make Parcel consumers dispose of it

* remove comment about copySize

* remove unnecessary Clear()
2024-04-21 12:57:35 +02:00
gdkchan
7070cf6ae5 End render target lifetime on syncpoint increment (#6687) 2024-04-20 22:35:20 -03:00
toofooboo
9839cd56fb chore: remove repetitive words (#6690)
Signed-off-by: toofooboo <cmaker@foxmail.com>
2024-04-19 09:45:51 -03:00
Marco Carvalho
99f46e22e2 Do not compare Span<T> to 'null' or 'default' (#6683) 2024-04-19 09:21:21 -03:00
Marco Carvalho
22fb8c9d4f Update to new standard for volatility operations (#6682) 2024-04-19 09:03:52 -03:00
gdkchan
2f93ae9a19 Fix unmapped address check when reading texture handles (#6679) 2024-04-18 19:28:16 -03:00
Marco Carvalho
3224ddeeb4 Update "SixLabors.ImageSharp" (#6680) 2024-04-18 19:52:57 +02:00
Isaac Marovitz
446f2854a5 Ava UI: Input Menu Refactor (#5826)
* Refactor

* Apply suggestions from code review

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

* Update src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs

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

* Update src/Ryujinx.Input/ButtonValueType.cs

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

* Add empty line

* Requested renames

* Update src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs

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

* Make parent models private readonly

* Fix ControllerInputView

* Make line shorter

* Mac keys in locale

* Double line break

* Fix build

* Get rid of _isValid

* Fix potential race condition

* Rename HasAnyButtonPressed to IsAnyButtonPressed

* Use switches

* Simplify enumeration

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2024-04-17 18:52:12 -03:00
Luke
8884d1fd73 Fix crash when changing controller config (#6654)
* fix needsMotionInputUpdate conditions

* Fix formatting

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

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2024-04-15 18:02:09 -03:00
151 changed files with 7597 additions and 2263 deletions

View File

@@ -20,7 +20,7 @@ jobs:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 } - { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 } - { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 } - { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
- { name: osx-x64, os: macOS-latest, zip_os_name: osx_x64 } - { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }
fail-fast: false fail-fast: false
steps: steps:
@@ -41,12 +41,12 @@ jobs:
- name: Change config filename - name: Change config filename
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash shell: bash
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest' if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Change config filename for macOS - name: Change config filename for macOS
run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash shell: bash
if: github.event_name == 'pull_request' && matrix.platform.os == 'macOS-latest' if: github.event_name == 'pull_request' && matrix.platform.os == 'macos-13'
- 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
@@ -61,15 +61,15 @@ jobs:
- name: Publish Ryujinx - name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -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.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest' if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Publish Ryujinx.Headless.SDL2 - name: Publish Ryujinx.Headless.SDL2
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -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.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest' if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Publish Ryujinx.Gtk3 - name: Publish Ryujinx.Gtk3
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_gtk -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Gtk3 --self-contained true run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_gtk -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Gtk3 --self-contained true
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest' if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Set executable bit - name: Set executable bit
run: | run: |
@@ -83,21 +83,21 @@ jobs:
with: with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }} name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
path: publish path: publish
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest' if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Upload Ryujinx.Headless.SDL2 artifact - name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }} name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
path: publish_sdl2_headless path: publish_sdl2_headless
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest' if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Upload Ryujinx.Gtk3 artifact - name: Upload Ryujinx.Gtk3 artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }} name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
path: publish_gtk path: publish_gtk
if: github.event_name == 'pull_request' && matrix.platform.os != 'macOS-latest' if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
build_macos: build_macos:
name: macOS Universal (${{ matrix.configuration }}) name: macOS Universal (${{ matrix.configuration }})

View File

@@ -8,8 +8,8 @@
<PackageVersion Include="Avalonia.Desktop" Version="11.0.10" /> <PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.10" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.0.10" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" /> <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.16" /> <PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.16" /> <PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" /> <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
@@ -42,7 +42,7 @@
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.7" /> <PackageVersion Include="SixLabors.ImageSharp" Version="2.1.8" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
<PackageVersion Include="SPB" Version="0.0.4-build32" /> <PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" /> <PackageVersion Include="System.IO.Hashing" Version="8.0.0" />

View File

@@ -36,8 +36,8 @@
## Compatibility ## Compatibility
As of October 2023, Ryujinx has been tested on approximately 4,200 titles; As of May 2024, Ryujinx has been tested on approximately 4,300 titles;
over 4,150 boot past menus and into gameplay, with roughly 3,500 of those being considered playable. over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable.
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).

View File

@@ -4,6 +4,8 @@
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String> <s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String> <s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=ASET/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Astc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Luma/@EntryIndexedValue">True</s:Boolean>

View File

@@ -857,8 +857,14 @@ namespace ARMeilleure.Translation.PTC
Stopwatch sw = Stopwatch.StartNew(); Stopwatch sw = Stopwatch.StartNew();
threads.ForEach((thread) => thread.Start()); foreach (var thread in threads)
threads.ForEach((thread) => thread.Join()); {
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}
threads.Clear(); threads.Clear();

View File

@@ -1,8 +1,10 @@
using Ryujinx.Audio.Backends.Common; using Ryujinx.Audio.Backends.Common;
using Ryujinx.Audio.Common; using Ryujinx.Audio.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading; using System.Threading;
@@ -87,7 +89,9 @@ namespace Ryujinx.Audio.Backends.SDL2
return; return;
} }
byte[] samples = new byte[frameCount * _bytesPerFrame]; using IMemoryOwner<byte> samplesOwner = ByteMemoryPool.Rent(frameCount * _bytesPerFrame);
Span<byte> samples = samplesOwner.Memory.Span;
_ringBuffer.Read(samples, 0, samples.Length); _ringBuffer.Read(samples, 0, samples.Length);

View File

@@ -1,8 +1,10 @@
using Ryujinx.Audio.Backends.Common; using Ryujinx.Audio.Backends.Common;
using Ryujinx.Audio.Backends.SoundIo.Native; using Ryujinx.Audio.Backends.SoundIo.Native;
using Ryujinx.Audio.Common; using Ryujinx.Audio.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
@@ -37,7 +39,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
_outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount); _outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount);
_outputStream.WriteCallback += Update; _outputStream.WriteCallback += Update;
_outputStream.Volume = requestedVolume; _outputStream.Volume = requestedVolume;
// TODO: Setup other callbacks (errors, ect). // TODO: Setup other callbacks (errors, etc.)
_outputStream.Open(); _outputStream.Open();
} }
@@ -120,7 +122,9 @@ namespace Ryujinx.Audio.Backends.SoundIo
int channelCount = areas.Length; int channelCount = areas.Length;
byte[] samples = new byte[frameCount * bytesPerFrame]; using IMemoryOwner<byte> samplesOwner = ByteMemoryPool.Rent(frameCount * bytesPerFrame);
Span<byte> samples = samplesOwner.Memory.Span;
_ringBuffer.Read(samples, 0, samples.Length); _ringBuffer.Read(samples, 0, samples.Length);

View File

@@ -212,7 +212,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// Check if the audio renderer should fix the GC-ADPCM context not being provided to the DSP. /// Check if the audio renderer should fix the GC-ADPCM context not being provided to the DSP.
/// </summary> /// </summary>
/// <returns>True if if the audio renderer should fix it.</returns> /// <returns>True if the audio renderer should fix it.</returns>
public bool IsAdpcmLoopContextBugFixed() public bool IsAdpcmLoopContextBugFixed()
{ {
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision2); return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision2);

View File

@@ -1,7 +1,5 @@
namespace Ryujinx.Common.Configuration.Hid namespace Ryujinx.Common.Configuration.Hid
{ {
// NOTE: Please don't change this to struct.
// This breaks Avalonia's TwoWay binding, which makes us unable to save new KeyboardHotkeys.
public class KeyboardHotkeys public class KeyboardHotkeys
{ {
public Key ToggleVsync { get; set; } public Key ToggleVsync { get; set; }

View File

@@ -44,7 +44,7 @@ namespace Ryujinx.Common.Extensions
/// <remarks> /// <remarks>
/// DO NOT use <paramref name="copyDestinationIfRequiredDoNotUse"/> after calling this method, as it will only /// DO NOT use <paramref name="copyDestinationIfRequiredDoNotUse"/> after calling this method, as it will only
/// contain a value if the value couldn't be referenced directly because it spans multiple <see cref="ReadOnlyMemory{Byte}"/> segments. /// contain a value if the value couldn't be referenced directly because it spans multiple <see cref="ReadOnlyMemory{Byte}"/> segments.
/// To discourage use, it is recommended to to call this method like the following: /// To discourage use, it is recommended to call this method like the following:
/// <c> /// <c>
/// ref readonly MyStruct value = ref sequenceReader.GetRefOrRefToCopy{MyStruct}(out _); /// ref readonly MyStruct value = ref sequenceReader.GetRefOrRefToCopy{MyStruct}(out _);
/// </c> /// </c>

View File

@@ -85,6 +85,70 @@ namespace Ryujinx.Cpu.Jit
_addressSpace = new(Tracking, backingMemory, _nativePageTable, useProtectionMirrors); _addressSpace = new(Tracking, backingMemory, _nativePageTable, useProtectionMirrors);
} }
public override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
{
if (size == 0)
{
return ReadOnlySequence<byte>.Empty;
}
try
{
if (tracked)
{
SignalMemoryTracking(va, (ulong)size, false);
}
else
{
AssertValidAddressAndSize(va, (ulong)size);
}
ulong endVa = va + (ulong)size;
int offset = 0;
BytesReadOnlySequenceSegment first = null, last = null;
while (va < endVa)
{
(MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(size - offset));
Memory<byte> physicalMemory = memory.GetMemory(rangeOffset, (int)copySize);
if (first is null)
{
first = last = new BytesReadOnlySequenceSegment(physicalMemory);
}
else
{
if (last.IsContiguousWith(physicalMemory, out nuint contiguousStart, out int contiguousSize))
{
Memory<byte> contiguousPhysicalMemory = new NativeMemoryManager<byte>(contiguousStart, contiguousSize).Memory;
last.Replace(contiguousPhysicalMemory);
}
else
{
last = last.Append(physicalMemory);
}
}
va += copySize;
offset += (int)copySize;
}
return new ReadOnlySequence<byte>(first, 0, last, (int)(size - last.RunningIndex));
}
catch (InvalidMemoryRegionException)
{
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
{
throw;
}
return ReadOnlySequence<byte>.Empty;
}
}
/// <inheritdoc/> /// <inheritdoc/>
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
{ {

View File

@@ -36,6 +36,8 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsMismatchingViewFormat; public readonly bool SupportsMismatchingViewFormat;
public readonly bool SupportsCubemapView; public readonly bool SupportsCubemapView;
public readonly bool SupportsNonConstantTextureOffset; public readonly bool SupportsNonConstantTextureOffset;
public readonly bool SupportsQuads;
public readonly bool SupportsSeparateSampler;
public readonly bool SupportsShaderBallot; public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence; public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64; public readonly bool SupportsShaderFloat64;
@@ -92,6 +94,8 @@ namespace Ryujinx.Graphics.GAL
bool supportsMismatchingViewFormat, bool supportsMismatchingViewFormat,
bool supportsCubemapView, bool supportsCubemapView,
bool supportsNonConstantTextureOffset, bool supportsNonConstantTextureOffset,
bool supportsQuads,
bool supportsSeparateSampler,
bool supportsShaderBallot, bool supportsShaderBallot,
bool supportsShaderBarrierDivergence, bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64, bool supportsShaderFloat64,
@@ -144,6 +148,8 @@ namespace Ryujinx.Graphics.GAL
SupportsMismatchingViewFormat = supportsMismatchingViewFormat; SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
SupportsCubemapView = supportsCubemapView; SupportsCubemapView = supportsCubemapView;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
SupportsQuads = supportsQuads;
SupportsSeparateSampler = supportsSeparateSampler;
SupportsShaderBallot = supportsShaderBallot; SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64; SupportsShaderFloat64 = supportsShaderFloat64;

View File

@@ -126,6 +126,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
ulong samplerPoolGpuVa = ((ulong)_state.State.SetTexSamplerPoolAOffsetUpper << 32) | _state.State.SetTexSamplerPoolB; ulong samplerPoolGpuVa = ((ulong)_state.State.SetTexSamplerPoolAOffsetUpper << 32) | _state.State.SetTexSamplerPoolB;
ulong texturePoolGpuVa = ((ulong)_state.State.SetTexHeaderPoolAOffsetUpper << 32) | _state.State.SetTexHeaderPoolB; ulong texturePoolGpuVa = ((ulong)_state.State.SetTexHeaderPoolAOffsetUpper << 32) | _state.State.SetTexHeaderPoolB;
int samplerPoolMaximumId = _state.State.SetTexSamplerPoolCMaximumIndex;
GpuChannelPoolState poolState = new( GpuChannelPoolState poolState = new(
texturePoolGpuVa, texturePoolGpuVa,
_state.State.SetTexHeaderPoolCMaximumIndex, _state.State.SetTexHeaderPoolCMaximumIndex,
@@ -139,7 +141,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
sharedMemorySize, sharedMemorySize,
_channel.BufferManager.HasUnalignedStorageBuffers); _channel.BufferManager.HasUnalignedStorageBuffers);
CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
_context.Renderer.Pipeline.SetProgram(cs.HostProgram); _context.Renderer.Pipeline.SetProgram(cs.HostProgram);
@@ -184,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
sharedMemorySize, sharedMemorySize,
_channel.BufferManager.HasUnalignedStorageBuffers); _channel.BufferManager.HasUnalignedStorageBuffers);
cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
_context.Renderer.Pipeline.SetProgram(cs.HostProgram); _context.Renderer.Pipeline.SetProgram(cs.HostProgram);
} }

View File

@@ -157,6 +157,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
} }
else if (operation == SyncpointbOperation.Incr) else if (operation == SyncpointbOperation.Incr)
{ {
// "Unbind" render targets since a syncpoint increment might indicate future CPU access for the textures.
_parent.TextureManager.RefreshModifiedTextures();
_context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint); _context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint);
_context.Synchronization.IncrementSyncpoint(syncpointId); _context.Synchronization.IncrementSyncpoint(syncpointId);
} }

View File

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Dma;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory; using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Twod; using Ryujinx.Graphics.Gpu.Engine.Twod;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -28,6 +29,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// </summary> /// </summary>
public MemoryManager MemoryManager => _channel.MemoryManager; public MemoryManager MemoryManager => _channel.MemoryManager;
/// <summary>
/// Channel texture manager.
/// </summary>
public TextureManager TextureManager => _channel.TextureManager;
/// <summary> /// <summary>
/// 3D Engine. /// 3D Engine.
/// </summary> /// </summary>

View File

@@ -1429,7 +1429,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
addressesSpan[index] = baseAddress + shader.Offset; addressesSpan[index] = baseAddress + shader.Offset;
} }
CachedShaderProgram gs = shaderCache.GetGraphicsShader(ref _state.State, ref _pipeline, _channel, ref _currentSpecState.GetPoolState(), ref _currentSpecState.GetGraphicsState(), addresses); int samplerPoolMaximumId = _state.State.SamplerIndex == SamplerIndex.ViaHeaderIndex
? _state.State.TexturePoolState.MaximumId
: _state.State.SamplerPoolState.MaximumId;
CachedShaderProgram gs = shaderCache.GetGraphicsShader(
ref _state.State,
ref _pipeline,
_channel,
samplerPoolMaximumId,
ref _currentSpecState.GetPoolState(),
ref _currentSpecState.GetGraphicsState(),
addresses);
// Consume the modified flag for spec state so that it isn't checked again. // Consume the modified flag for spec state so that it isn't checked again.
_currentSpecState.SetShader(gs); _currentSpecState.SetShader(gs);

View File

@@ -395,8 +395,14 @@ namespace Ryujinx.Graphics.Gpu
{ {
Renderer.CreateSync(SyncNumber, strict); Renderer.CreateSync(SyncNumber, strict);
SyncActions.ForEach(action => action.SyncPreAction(syncpoint)); foreach (var action in SyncActions)
SyncpointActions.ForEach(action => action.SyncPreAction(syncpoint)); {
action.SyncPreAction(syncpoint);
}
foreach (var action in SyncpointActions)
{
action.SyncPreAction(syncpoint);
}
SyncNumber++; SyncNumber++;

View File

@@ -62,8 +62,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="channel">GPU channel that the texture pool cache belongs to</param> /// <param name="channel">GPU channel that the texture pool cache belongs to</param>
/// <param name="address">Start address of the texture pool</param> /// <param name="address">Start address of the texture pool</param>
/// <param name="maximumId">Maximum ID of the texture pool</param> /// <param name="maximumId">Maximum ID of the texture pool</param>
/// <param name="bindingsArrayCache">Cache of texture array bindings</param>
/// <returns>The found or newly created texture pool</returns> /// <returns>The found or newly created texture pool</returns>
public T FindOrCreate(GpuChannel channel, ulong address, int maximumId) public T FindOrCreate(GpuChannel channel, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
{ {
// Remove old entries from the cache, if possible. // Remove old entries from the cache, if possible.
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval) while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
@@ -73,6 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_pools.RemoveFirst(); _pools.RemoveFirst();
oldestPool.Dispose(); oldestPool.Dispose();
oldestPool.CacheNode = null; oldestPool.CacheNode = null;
bindingsArrayCache.RemoveAllWithPool(oldestPool);
} }
T pool; T pool;
@@ -87,8 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (pool.CacheNode != _pools.Last) if (pool.CacheNode != _pools.Last)
{ {
_pools.Remove(pool.CacheNode); _pools.Remove(pool.CacheNode);
_pools.AddLast(pool.CacheNode);
pool.CacheNode = _pools.AddLast(pool);
} }
pool.CacheTimestamp = _currentTimestamp; pool.CacheTimestamp = _currentTimestamp;

View File

@@ -390,7 +390,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
_views.Remove(texture); _views.Remove(texture);
Group.RemoveView(texture); Group.RemoveView(_views, texture);
texture._viewStorage = texture; texture._viewStorage = texture;

View File

@@ -44,6 +44,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public TextureUsageFlags Flags { get; } public TextureUsageFlags Flags { get; }
/// <summary>
/// Indicates that the binding is for a sampler.
/// </summary>
public bool IsSamplerOnly { get; }
/// <summary> /// <summary>
/// Constructs the texture binding information structure. /// Constructs the texture binding information structure.
/// </summary> /// </summary>
@@ -74,8 +79,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param> /// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
/// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param> /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
/// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param> /// <param name="flags">The texture's usage flags, indicating how it is used in the shader</param>
public TextureBindingInfo(Target target, int binding, int arrayLength, int cbufSlot, int handle, TextureUsageFlags flags) : this(target, (Format)0, binding, arrayLength, cbufSlot, handle, flags) /// <param name="isSamplerOnly">Indicates that the binding is for a sampler</param>
public TextureBindingInfo(
Target target,
int binding,
int arrayLength,
int cbufSlot,
int handle,
TextureUsageFlags flags,
bool isSamplerOnly) : this(target, 0, binding, arrayLength, cbufSlot, handle, flags)
{ {
IsSamplerOnly = isSamplerOnly;
} }
} }
} }

View File

@@ -21,12 +21,98 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
private readonly bool _isCompute;
/// <summary> /// <summary>
/// Array cache entry key. /// Array cache entry key.
/// </summary> /// </summary>
private readonly struct CacheEntryKey : IEquatable<CacheEntryKey> private readonly struct CacheEntryFromPoolKey : IEquatable<CacheEntryFromPoolKey>
{
/// <summary>
/// Whether the entry is for an image.
/// </summary>
public readonly bool IsImage;
/// <summary>
/// Whether the entry is for a sampler.
/// </summary>
public readonly bool IsSampler;
/// <summary>
/// Texture or image target type.
/// </summary>
public readonly Target Target;
/// <summary>
/// Number of entries of the array.
/// </summary>
public readonly int ArrayLength;
private readonly TexturePool _texturePool;
private readonly SamplerPool _samplerPool;
/// <summary>
/// Creates a new array cache entry.
/// </summary>
/// <param name="isImage">Whether the entry is for an image</param>
/// <param name="bindingInfo">Binding information for the array</param>
/// <param name="texturePool">Texture pool where the array textures are located</param>
/// <param name="samplerPool">Sampler pool where the array samplers are located</param>
public CacheEntryFromPoolKey(bool isImage, TextureBindingInfo bindingInfo, TexturePool texturePool, SamplerPool samplerPool)
{
IsImage = isImage;
IsSampler = bindingInfo.IsSamplerOnly;
Target = bindingInfo.Target;
ArrayLength = bindingInfo.ArrayLength;
_texturePool = texturePool;
_samplerPool = samplerPool;
}
/// <summary>
/// Checks if the pool matches the cached pool.
/// </summary>
/// <param name="texturePool">Texture or sampler pool instance</param>
/// <returns>True if the pool matches, false otherwise</returns>
public bool MatchesPool<T>(IPool<T> pool)
{
return _texturePool == pool || _samplerPool == pool;
}
/// <summary>
/// Checks if the texture and sampler pools matches the cached pools.
/// </summary>
/// <param name="texturePool">Texture pool instance</param>
/// <param name="samplerPool">Sampler pool instance</param>
/// <returns>True if the pools match, false otherwise</returns>
private bool MatchesPools(TexturePool texturePool, SamplerPool samplerPool)
{
return _texturePool == texturePool && _samplerPool == samplerPool;
}
public bool Equals(CacheEntryFromPoolKey other)
{
return IsImage == other.IsImage &&
IsSampler == other.IsSampler &&
Target == other.Target &&
ArrayLength == other.ArrayLength &&
MatchesPools(other._texturePool, other._samplerPool);
}
public override bool Equals(object obj)
{
return obj is CacheEntryFromBufferKey other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(_texturePool, _samplerPool, IsSampler);
}
}
/// <summary>
/// Array cache entry key.
/// </summary>
private readonly struct CacheEntryFromBufferKey : IEquatable<CacheEntryFromBufferKey>
{ {
/// <summary> /// <summary>
/// Whether the entry is for an image. /// Whether the entry is for an image.
@@ -61,7 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texturePool">Texture pool where the array textures are located</param> /// <param name="texturePool">Texture pool where the array textures are located</param>
/// <param name="samplerPool">Sampler pool where the array samplers are located</param> /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
/// <param name="textureBufferBounds">Constant buffer bounds with the texture handles</param> /// <param name="textureBufferBounds">Constant buffer bounds with the texture handles</param>
public CacheEntryKey( public CacheEntryFromBufferKey(
bool isImage, bool isImage,
TextureBindingInfo bindingInfo, TextureBindingInfo bindingInfo,
TexturePool texturePool, TexturePool texturePool,
@@ -100,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return _textureBufferBounds.Equals(textureBufferBounds); return _textureBufferBounds.Equals(textureBufferBounds);
} }
public bool Equals(CacheEntryKey other) public bool Equals(CacheEntryFromBufferKey other)
{ {
return IsImage == other.IsImage && return IsImage == other.IsImage &&
Target == other.Target && Target == other.Target &&
@@ -112,7 +198,7 @@ namespace Ryujinx.Graphics.Gpu.Image
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return obj is CacheEntryKey other && Equals(other); return obj is CacheEntryFromBufferKey other && Equals(other);
} }
public override int GetHashCode() public override int GetHashCode()
@@ -122,40 +208,15 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
/// <summary> /// <summary>
/// Array cache entry. /// Array cache entry from pool.
/// </summary> /// </summary>
private class CacheEntry private class CacheEntry
{ {
/// <summary>
/// Key for this entry on the cache.
/// </summary>
public readonly CacheEntryKey Key;
/// <summary>
/// Linked list node used on the texture bindings array cache.
/// </summary>
public LinkedListNode<CacheEntry> CacheNode;
/// <summary>
/// Timestamp set on the last use of the array by the cache.
/// </summary>
public int CacheTimestamp;
/// <summary> /// <summary>
/// All cached textures, along with their invalidated sequence number as value. /// All cached textures, along with their invalidated sequence number as value.
/// </summary> /// </summary>
public readonly Dictionary<Texture, int> Textures; public readonly Dictionary<Texture, int> Textures;
/// <summary>
/// All pool texture IDs along with their textures.
/// </summary>
public readonly Dictionary<int, Texture> TextureIds;
/// <summary>
/// All pool sampler IDs along with their samplers.
/// </summary>
public readonly Dictionary<int, Sampler> SamplerIds;
/// <summary> /// <summary>
/// Backend texture array if the entry is for a texture, otherwise null. /// Backend texture array if the entry is for a texture, otherwise null.
/// </summary> /// </summary>
@@ -166,44 +227,39 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public readonly IImageArray ImageArray; public readonly IImageArray ImageArray;
private readonly TexturePool _texturePool; /// <summary>
private readonly SamplerPool _samplerPool; /// Texture pool where the array textures are located.
/// </summary>
protected readonly TexturePool TexturePool;
/// <summary>
/// Sampler pool where the array samplers are located.
/// </summary>
protected readonly SamplerPool SamplerPool;
private int _texturePoolSequence; private int _texturePoolSequence;
private int _samplerPoolSequence; private int _samplerPoolSequence;
private int[] _cachedTextureBuffer;
private int[] _cachedSamplerBuffer;
private int _lastSequenceNumber;
/// <summary> /// <summary>
/// Creates a new array cache entry. /// Creates a new array cache entry.
/// </summary> /// </summary>
/// <param name="key">Key for this entry on the cache</param>
/// <param name="texturePool">Texture pool where the array textures are located</param> /// <param name="texturePool">Texture pool where the array textures are located</param>
/// <param name="samplerPool">Sampler pool where the array samplers are located</param> /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
private CacheEntry(ref CacheEntryKey key, TexturePool texturePool, SamplerPool samplerPool) private CacheEntry(TexturePool texturePool, SamplerPool samplerPool)
{ {
Key = key;
Textures = new Dictionary<Texture, int>(); Textures = new Dictionary<Texture, int>();
TextureIds = new Dictionary<int, Texture>();
SamplerIds = new Dictionary<int, Sampler>();
_texturePool = texturePool; TexturePool = texturePool;
_samplerPool = samplerPool; SamplerPool = samplerPool;
_lastSequenceNumber = -1;
} }
/// <summary> /// <summary>
/// Creates a new array cache entry. /// Creates a new array cache entry.
/// </summary> /// </summary>
/// <param name="key">Key for this entry on the cache</param>
/// <param name="array">Backend texture array</param> /// <param name="array">Backend texture array</param>
/// <param name="texturePool">Texture pool where the array textures are located</param> /// <param name="texturePool">Texture pool where the array textures are located</param>
/// <param name="samplerPool">Sampler pool where the array samplers are located</param> /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
public CacheEntry(ref CacheEntryKey key, ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : this(ref key, texturePool, samplerPool) public CacheEntry(ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : this(texturePool, samplerPool)
{ {
TextureArray = array; TextureArray = array;
} }
@@ -211,11 +267,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Creates a new array cache entry. /// Creates a new array cache entry.
/// </summary> /// </summary>
/// <param name="key">Key for this entry on the cache</param>
/// <param name="array">Backend image array</param> /// <param name="array">Backend image array</param>
/// <param name="texturePool">Texture pool where the array textures are located</param> /// <param name="texturePool">Texture pool where the array textures are located</param>
/// <param name="samplerPool">Sampler pool where the array samplers are located</param> /// <param name="samplerPool">Sampler pool where the array samplers are located</param>
public CacheEntry(ref CacheEntryKey key, IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : this(ref key, texturePool, samplerPool) public CacheEntry(IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : this(texturePool, samplerPool)
{ {
ImageArray = array; ImageArray = array;
} }
@@ -248,23 +303,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Clears all cached texture instances. /// Clears all cached texture instances.
/// </summary> /// </summary>
public void Reset() public virtual void Reset()
{ {
Textures.Clear(); Textures.Clear();
TextureIds.Clear();
SamplerIds.Clear();
}
/// <summary>
/// Updates the cached constant buffer data.
/// </summary>
/// <param name="cachedTextureBuffer">Constant buffer data with the texture handles (and sampler handles, if they are combined)</param>
/// <param name="cachedSamplerBuffer">Constant buffer data with the sampler handles</param>
/// <param name="separateSamplerBuffer">Whether <paramref name="cachedTextureBuffer"/> and <paramref name="cachedSamplerBuffer"/> comes from different buffers</param>
public void UpdateData(ReadOnlySpan<int> cachedTextureBuffer, ReadOnlySpan<int> cachedSamplerBuffer, bool separateSamplerBuffer)
{
_cachedTextureBuffer = cachedTextureBuffer.ToArray();
_cachedSamplerBuffer = separateSamplerBuffer ? cachedSamplerBuffer.ToArray() : _cachedTextureBuffer;
} }
/// <summary> /// <summary>
@@ -287,39 +328,105 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Checks if the cached texture or sampler pool has been modified since the last call to this method. /// Checks if the cached texture or sampler pool has been modified since the last call to this method.
/// </summary> /// </summary>
/// <returns>True if any used entries of the pools might have been modified, false otherwise</returns> /// <returns>True if any used entries of the pool might have been modified, false otherwise</returns>
public bool PoolsModified() public bool TexturePoolModified()
{ {
bool texturePoolModified = _texturePool.WasModified(ref _texturePoolSequence); return TexturePool.WasModified(ref _texturePoolSequence);
bool samplerPoolModified = _samplerPool.WasModified(ref _samplerPoolSequence);
// If both pools were not modified since the last check, we have nothing else to check.
if (!texturePoolModified && !samplerPoolModified)
{
return false;
} }
// If the pools were modified, let's check if any of the entries we care about changed. /// <summary>
/// Checks if the cached texture or sampler pool has been modified since the last call to this method.
// Check if any of our cached textures changed on the pool. /// </summary>
foreach ((int textureId, Texture texture) in TextureIds) /// <returns>True if any used entries of the pool might have been modified, false otherwise</returns>
public bool SamplerPoolModified()
{ {
if (_texturePool.GetCachedItem(textureId) != texture) return SamplerPool.WasModified(ref _samplerPoolSequence);
{
return true;
} }
} }
// Check if any of our cached samplers changed on the pool. /// <summary>
foreach ((int samplerId, Sampler sampler) in SamplerIds) /// Array cache entry from constant buffer.
/// </summary>
private class CacheEntryFromBuffer : CacheEntry
{ {
if (_samplerPool.GetCachedItem(samplerId) != sampler) /// <summary>
/// Key for this entry on the cache.
/// </summary>
public readonly CacheEntryFromBufferKey Key;
/// <summary>
/// Linked list node used on the texture bindings array cache.
/// </summary>
public LinkedListNode<CacheEntryFromBuffer> CacheNode;
/// <summary>
/// Timestamp set on the last use of the array by the cache.
/// </summary>
public int CacheTimestamp;
/// <summary>
/// All pool texture IDs along with their textures.
/// </summary>
public readonly Dictionary<int, (Texture, TextureDescriptor)> TextureIds;
/// <summary>
/// All pool sampler IDs along with their samplers.
/// </summary>
public readonly Dictionary<int, (Sampler, SamplerDescriptor)> SamplerIds;
private int[] _cachedTextureBuffer;
private int[] _cachedSamplerBuffer;
private int _lastSequenceNumber;
/// <summary>
/// Creates a new array cache entry.
/// </summary>
/// <param name="key">Key for this entry on the cache</param>
/// <param name="array">Backend texture array</param>
/// <param name="texturePool">Texture pool where the array textures are located</param>
/// <param name="samplerPool">Sampler pool where the array samplers are located</param>
public CacheEntryFromBuffer(ref CacheEntryFromBufferKey key, ITextureArray array, TexturePool texturePool, SamplerPool samplerPool) : base(array, texturePool, samplerPool)
{ {
return true; Key = key;
} _lastSequenceNumber = -1;
TextureIds = new Dictionary<int, (Texture, TextureDescriptor)>();
SamplerIds = new Dictionary<int, (Sampler, SamplerDescriptor)>();
} }
return false; /// <summary>
/// Creates a new array cache entry.
/// </summary>
/// <param name="key">Key for this entry on the cache</param>
/// <param name="array">Backend image array</param>
/// <param name="texturePool">Texture pool where the array textures are located</param>
/// <param name="samplerPool">Sampler pool where the array samplers are located</param>
public CacheEntryFromBuffer(ref CacheEntryFromBufferKey key, IImageArray array, TexturePool texturePool, SamplerPool samplerPool) : base(array, texturePool, samplerPool)
{
Key = key;
_lastSequenceNumber = -1;
TextureIds = new Dictionary<int, (Texture, TextureDescriptor)>();
SamplerIds = new Dictionary<int, (Sampler, SamplerDescriptor)>();
}
/// <inheritdoc/>
public override void Reset()
{
base.Reset();
TextureIds.Clear();
SamplerIds.Clear();
}
/// <summary>
/// Updates the cached constant buffer data.
/// </summary>
/// <param name="cachedTextureBuffer">Constant buffer data with the texture handles (and sampler handles, if they are combined)</param>
/// <param name="cachedSamplerBuffer">Constant buffer data with the sampler handles</param>
/// <param name="separateSamplerBuffer">Whether <paramref name="cachedTextureBuffer"/> and <paramref name="cachedSamplerBuffer"/> comes from different buffers</param>
public void UpdateData(ReadOnlySpan<int> cachedTextureBuffer, ReadOnlySpan<int> cachedSamplerBuffer, bool separateSamplerBuffer)
{
_cachedTextureBuffer = cachedTextureBuffer.ToArray();
_cachedSamplerBuffer = separateSamplerBuffer ? cachedSamplerBuffer.ToArray() : _cachedTextureBuffer;
} }
/// <summary> /// <summary>
@@ -380,10 +487,51 @@ namespace Ryujinx.Graphics.Gpu.Image
return true; return true;
} }
/// <summary>
/// Checks if the cached texture or sampler pool has been modified since the last call to this method.
/// </summary>
/// <returns>True if any used entries of the pools might have been modified, false otherwise</returns>
public bool PoolsModified()
{
bool texturePoolModified = TexturePoolModified();
bool samplerPoolModified = SamplerPoolModified();
// If both pools were not modified since the last check, we have nothing else to check.
if (!texturePoolModified && !samplerPoolModified)
{
return false;
} }
private readonly Dictionary<CacheEntryKey, CacheEntry> _cache; // If the pools were modified, let's check if any of the entries we care about changed.
private readonly LinkedList<CacheEntry> _lruCache;
// Check if any of our cached textures changed on the pool.
foreach ((int textureId, (Texture texture, TextureDescriptor descriptor)) in TextureIds)
{
if (TexturePool.GetCachedItem(textureId) != texture ||
(texture == null && TexturePool.IsValidId(textureId) && !TexturePool.GetDescriptorRef(textureId).Equals(descriptor)))
{
return true;
}
}
// Check if any of our cached samplers changed on the pool.
foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds)
{
if (SamplerPool.GetCachedItem(samplerId) != sampler ||
(sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor)))
{
return true;
}
}
return false;
}
}
private readonly Dictionary<CacheEntryFromBufferKey, CacheEntryFromBuffer> _cacheFromBuffer;
private readonly Dictionary<CacheEntryFromPoolKey, CacheEntry> _cacheFromPool;
private readonly LinkedList<CacheEntryFromBuffer> _lruCache;
private int _currentTimestamp; private int _currentTimestamp;
@@ -392,14 +540,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="context">GPU context</param> /// <param name="context">GPU context</param>
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="isCompute">Whether the bindings will be used for compute or graphics pipelines</param> public TextureBindingsArrayCache(GpuContext context, GpuChannel channel)
public TextureBindingsArrayCache(GpuContext context, GpuChannel channel, bool isCompute)
{ {
_context = context; _context = context;
_channel = channel; _channel = channel;
_isCompute = isCompute; _cacheFromBuffer = new Dictionary<CacheEntryFromBufferKey, CacheEntryFromBuffer>();
_cache = new Dictionary<CacheEntryKey, CacheEntry>(); _cacheFromPool = new Dictionary<CacheEntryFromPoolKey, CacheEntry>();
_lruCache = new LinkedList<CacheEntry>(); _lruCache = new LinkedList<CacheEntryFromBuffer>();
} }
/// <summary> /// <summary>
@@ -457,15 +604,180 @@ namespace Ryujinx.Graphics.Gpu.Image
bool isImage, bool isImage,
SamplerIndex samplerIndex, SamplerIndex samplerIndex,
TextureBindingInfo bindingInfo) TextureBindingInfo bindingInfo)
{
if (IsDirectHandleType(bindingInfo.Handle))
{
UpdateFromPool(texturePool, samplerPool, stage, isImage, bindingInfo);
}
else
{
UpdateFromBuffer(texturePool, samplerPool, stage, stageIndex, textureBufferIndex, isImage, samplerIndex, bindingInfo);
}
}
/// <summary>
/// Updates a texture or image array bindings and textures from a texture or sampler pool.
/// </summary>
/// <param name="texturePool">Texture pool</param>
/// <param name="samplerPool">Sampler pool</param>
/// <param name="stage">Shader stage where the array is used</param>
/// <param name="isImage">Whether the array is a image or texture array</param>
/// <param name="bindingInfo">Array binding information</param>
private void UpdateFromPool(TexturePool texturePool, SamplerPool samplerPool, ShaderStage stage, bool isImage, TextureBindingInfo bindingInfo)
{
CacheEntry entry = GetOrAddEntry(texturePool, samplerPool, bindingInfo, isImage, out bool isNewEntry);
bool isSampler = bindingInfo.IsSamplerOnly;
bool poolModified = isSampler ? entry.SamplerPoolModified() : entry.TexturePoolModified();
bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
bool resScaleUnsupported = bindingInfo.Flags.HasFlag(TextureUsageFlags.ResScaleUnsupported);
if (!poolModified && !isNewEntry && entry.ValidateTextures())
{
entry.SynchronizeMemory(isStore, resScaleUnsupported);
if (isImage)
{
_context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray);
}
else
{
_context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray);
}
return;
}
if (!isNewEntry)
{
entry.Reset();
}
int length = (isSampler ? samplerPool.MaximumId : texturePool.MaximumId) + 1;
length = Math.Min(length, bindingInfo.ArrayLength);
Format[] formats = isImage ? new Format[bindingInfo.ArrayLength] : null;
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
for (int index = 0; index < length; index++)
{
Texture texture = null;
Sampler sampler = null;
if (isSampler)
{
sampler = samplerPool?.Get(index);
}
else
{
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, out texture);
if (texture != null)
{
entry.Textures[texture] = texture.InvalidatedSequence;
if (isStore)
{
texture.SignalModified();
}
if (resScaleUnsupported && texture.ScaleMode != TextureScaleMode.Blacklisted)
{
// Scaling textures used on arrays is currently not supported.
texture.BlacklistScale();
}
}
}
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
ISampler hostSampler = sampler?.GetHostSampler(texture);
Format format = bindingInfo.Format;
if (hostTexture != null && texture.Target == Target.TextureBuffer)
{
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
if (isImage)
{
if (format == 0 && texture != null)
{
format = texture.Format;
}
_channel.BufferManager.SetBufferTextureStorage(entry.ImageArray, hostTexture, texture.Range, bindingInfo, index, format);
}
else
{
_channel.BufferManager.SetBufferTextureStorage(entry.TextureArray, hostTexture, texture.Range, bindingInfo, index, format);
}
}
else if (isImage)
{
if (format == 0 && texture != null)
{
format = texture.Format;
}
formats[index] = format;
textures[index] = hostTexture;
}
else
{
samplers[index] = hostSampler;
textures[index] = hostTexture;
}
}
if (isImage)
{
entry.ImageArray.SetFormats(0, formats);
entry.ImageArray.SetImages(0, textures);
_context.Renderer.Pipeline.SetImageArray(stage, bindingInfo.Binding, entry.ImageArray);
}
else
{
entry.TextureArray.SetSamplers(0, samplers);
entry.TextureArray.SetTextures(0, textures);
_context.Renderer.Pipeline.SetTextureArray(stage, bindingInfo.Binding, entry.TextureArray);
}
}
/// <summary>
/// Updates a texture or image array bindings and textures from constant buffer handles.
/// </summary>
/// <param name="texturePool">Texture pool</param>
/// <param name="samplerPool">Sampler pool</param>
/// <param name="stage">Shader stage where the array is used</param>
/// <param name="stageIndex">Shader stage index where the array is used</param>
/// <param name="textureBufferIndex">Texture constant buffer index</param>
/// <param name="isImage">Whether the array is a image or texture array</param>
/// <param name="samplerIndex">Sampler handles source</param>
/// <param name="bindingInfo">Array binding information</param>
private void UpdateFromBuffer(
TexturePool texturePool,
SamplerPool samplerPool,
ShaderStage stage,
int stageIndex,
int textureBufferIndex,
bool isImage,
SamplerIndex samplerIndex,
TextureBindingInfo bindingInfo)
{ {
(textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, textureBufferIndex); (textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(bindingInfo.CbufSlot, textureBufferIndex);
bool separateSamplerBuffer = textureBufferIndex != samplerBufferIndex; bool separateSamplerBuffer = textureBufferIndex != samplerBufferIndex;
bool isCompute = stage == ShaderStage.Compute;
ref BufferBounds textureBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex); ref BufferBounds textureBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
ref BufferBounds samplerBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex); ref BufferBounds samplerBufferBounds = ref _channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
CacheEntry entry = GetOrAddEntry( CacheEntryFromBuffer entry = GetOrAddEntry(
texturePool, texturePool,
samplerPool, samplerPool,
bindingInfo, bindingInfo,
@@ -589,8 +901,8 @@ namespace Ryujinx.Graphics.Gpu.Image
Sampler sampler = samplerPool?.Get(samplerId); Sampler sampler = samplerPool?.Get(samplerId);
entry.TextureIds[textureId] = texture; entry.TextureIds[textureId] = (texture, descriptor);
entry.SamplerIds[samplerId] = sampler; entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default);
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
ISampler hostSampler = sampler?.GetHostSampler(texture); ISampler hostSampler = sampler?.GetHostSampler(texture);
@@ -650,13 +962,12 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
/// <summary> /// <summary>
/// Gets a cached texture entry, or creates a new one if not found. /// Gets a cached texture entry from pool, or creates a new one if not found.
/// </summary> /// </summary>
/// <param name="texturePool">Texture pool</param> /// <param name="texturePool">Texture pool</param>
/// <param name="samplerPool">Sampler pool</param> /// <param name="samplerPool">Sampler pool</param>
/// <param name="bindingInfo">Array binding information</param> /// <param name="bindingInfo">Array binding information</param>
/// <param name="isImage">Whether the array is a image or texture array</param> /// <param name="isImage">Whether the array is a image or texture array</param>
/// <param name="textureBufferBounds">Constant buffer bounds with the texture handles</param>
/// <param name="isNew">Whether a new entry was created, or an existing one was returned</param> /// <param name="isNew">Whether a new entry was created, or an existing one was returned</param>
/// <returns>Cache entry</returns> /// <returns>Cache entry</returns>
private CacheEntry GetOrAddEntry( private CacheEntry GetOrAddEntry(
@@ -664,17 +975,11 @@ namespace Ryujinx.Graphics.Gpu.Image
SamplerPool samplerPool, SamplerPool samplerPool,
TextureBindingInfo bindingInfo, TextureBindingInfo bindingInfo,
bool isImage, bool isImage,
ref BufferBounds textureBufferBounds,
out bool isNew) out bool isNew)
{ {
CacheEntryKey key = new CacheEntryKey( CacheEntryFromPoolKey key = new CacheEntryFromPoolKey(isImage, bindingInfo, texturePool, samplerPool);
isImage,
bindingInfo,
texturePool,
samplerPool,
ref textureBufferBounds);
isNew = !_cache.TryGetValue(key, out CacheEntry entry); isNew = !_cacheFromPool.TryGetValue(key, out CacheEntry entry);
if (isNew) if (isNew)
{ {
@@ -684,13 +989,61 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
IImageArray array = _context.Renderer.CreateImageArray(arrayLength, bindingInfo.Target == Target.TextureBuffer); IImageArray array = _context.Renderer.CreateImageArray(arrayLength, bindingInfo.Target == Target.TextureBuffer);
_cache.Add(key, entry = new CacheEntry(ref key, array, texturePool, samplerPool)); _cacheFromPool.Add(key, entry = new CacheEntry(array, texturePool, samplerPool));
} }
else else
{ {
ITextureArray array = _context.Renderer.CreateTextureArray(arrayLength, bindingInfo.Target == Target.TextureBuffer); ITextureArray array = _context.Renderer.CreateTextureArray(arrayLength, bindingInfo.Target == Target.TextureBuffer);
_cache.Add(key, entry = new CacheEntry(ref key, array, texturePool, samplerPool)); _cacheFromPool.Add(key, entry = new CacheEntry(array, texturePool, samplerPool));
}
}
return entry;
}
/// <summary>
/// Gets a cached texture entry from constant buffer, or creates a new one if not found.
/// </summary>
/// <param name="texturePool">Texture pool</param>
/// <param name="samplerPool">Sampler pool</param>
/// <param name="bindingInfo">Array binding information</param>
/// <param name="isImage">Whether the array is a image or texture array</param>
/// <param name="textureBufferBounds">Constant buffer bounds with the texture handles</param>
/// <param name="isNew">Whether a new entry was created, or an existing one was returned</param>
/// <returns>Cache entry</returns>
private CacheEntryFromBuffer GetOrAddEntry(
TexturePool texturePool,
SamplerPool samplerPool,
TextureBindingInfo bindingInfo,
bool isImage,
ref BufferBounds textureBufferBounds,
out bool isNew)
{
CacheEntryFromBufferKey key = new CacheEntryFromBufferKey(
isImage,
bindingInfo,
texturePool,
samplerPool,
ref textureBufferBounds);
isNew = !_cacheFromBuffer.TryGetValue(key, out CacheEntryFromBuffer entry);
if (isNew)
{
int arrayLength = bindingInfo.ArrayLength;
if (isImage)
{
IImageArray array = _context.Renderer.CreateImageArray(arrayLength, bindingInfo.Target == Target.TextureBuffer);
_cacheFromBuffer.Add(key, entry = new CacheEntryFromBuffer(ref key, array, texturePool, samplerPool));
}
else
{
ITextureArray array = _context.Renderer.CreateTextureArray(arrayLength, bindingInfo.Target == Target.TextureBuffer);
_cacheFromBuffer.Add(key, entry = new CacheEntryFromBuffer(ref key, array, texturePool, samplerPool));
} }
} }
@@ -716,15 +1069,52 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
private void RemoveLeastUsedEntries() private void RemoveLeastUsedEntries()
{ {
LinkedListNode<CacheEntry> nextNode = _lruCache.First; LinkedListNode<CacheEntryFromBuffer> nextNode = _lruCache.First;
while (nextNode != null && _currentTimestamp - nextNode.Value.CacheTimestamp >= MinDeltaForRemoval) while (nextNode != null && _currentTimestamp - nextNode.Value.CacheTimestamp >= MinDeltaForRemoval)
{ {
LinkedListNode<CacheEntry> toRemove = nextNode; LinkedListNode<CacheEntryFromBuffer> toRemove = nextNode;
nextNode = nextNode.Next; nextNode = nextNode.Next;
_cache.Remove(toRemove.Value.Key); _cacheFromBuffer.Remove(toRemove.Value.Key);
_lruCache.Remove(toRemove); _lruCache.Remove(toRemove);
} }
} }
/// <summary>
/// Removes all cached texture arrays matching the specified texture pool.
/// </summary>
/// <param name="pool">Texture pool</param>
public void RemoveAllWithPool<T>(IPool<T> pool)
{
List<CacheEntryFromPoolKey> keysToRemove = null;
foreach (CacheEntryFromPoolKey key in _cacheFromPool.Keys)
{
if (key.MatchesPool(pool))
{
(keysToRemove ??= new()).Add(key);
}
}
if (keysToRemove != null)
{
foreach (CacheEntryFromPoolKey key in keysToRemove)
{
_cacheFromPool.Remove(key);
}
}
}
/// <summary>
/// Checks if a handle indicates the binding should have all its textures sourced directly from a pool.
/// </summary>
/// <param name="handle">Handle to check</param>
/// <returns>True if the handle represents direct pool access, false otherwise</returns>
private static bool IsDirectHandleType(int handle)
{
(_, _, TextureHandleType type) = TextureHandle.UnpackOffsets(handle);
return type == TextureHandleType.Direct;
}
} }
} }

View File

@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly TexturePoolCache _texturePoolCache; private readonly TexturePoolCache _texturePoolCache;
private readonly SamplerPoolCache _samplerPoolCache; private readonly SamplerPoolCache _samplerPoolCache;
private readonly TextureBindingsArrayCache _arrayBindingsCache; private readonly TextureBindingsArrayCache _bindingsArrayCache;
private TexturePool _cachedTexturePool; private TexturePool _cachedTexturePool;
private SamplerPool _cachedSamplerPool; private SamplerPool _cachedSamplerPool;
@@ -72,12 +72,14 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="context">The GPU context that the texture bindings manager belongs to</param> /// <param name="context">The GPU context that the texture bindings manager belongs to</param>
/// <param name="channel">The GPU channel that the texture bindings manager belongs to</param> /// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
/// <param name="bindingsArrayCache">Cache of texture array bindings</param>
/// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param> /// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param>
/// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param> /// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param>
/// <param name="isCompute">True if the bindings manager is used for the compute engine</param> /// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
public TextureBindingsManager( public TextureBindingsManager(
GpuContext context, GpuContext context,
GpuChannel channel, GpuChannel channel,
TextureBindingsArrayCache bindingsArrayCache,
TexturePoolCache texturePoolCache, TexturePoolCache texturePoolCache,
SamplerPoolCache samplerPoolCache, SamplerPoolCache samplerPoolCache,
bool isCompute) bool isCompute)
@@ -89,7 +91,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_isCompute = isCompute; _isCompute = isCompute;
_arrayBindingsCache = new TextureBindingsArrayCache(context, channel, isCompute); _bindingsArrayCache = bindingsArrayCache;
int stages = isCompute ? 1 : Constants.ShaderStages; int stages = isCompute ? 1 : Constants.ShaderStages;
@@ -456,7 +458,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (bindingInfo.ArrayLength > 1) if (bindingInfo.ArrayLength > 1)
{ {
_arrayBindingsCache.UpdateTextureArray(texturePool, samplerPool, stage, stageIndex, _textureBufferIndex, _samplerIndex, bindingInfo); _bindingsArrayCache.UpdateTextureArray(texturePool, samplerPool, stage, stageIndex, _textureBufferIndex, _samplerIndex, bindingInfo);
continue; continue;
} }
@@ -594,7 +596,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (bindingInfo.ArrayLength > 1) if (bindingInfo.ArrayLength > 1)
{ {
_arrayBindingsCache.UpdateImageArray(pool, stage, stageIndex, _textureBufferIndex, bindingInfo); _bindingsArrayCache.UpdateImageArray(pool, stage, stageIndex, _textureBufferIndex, bindingInfo);
continue; continue;
} }
@@ -732,7 +734,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId); TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
TextureDescriptor descriptor; TextureDescriptor descriptor;
@@ -770,7 +772,7 @@ namespace Ryujinx.Graphics.Gpu.Image
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex) ? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex); : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
int handle = textureBufferAddress != 0 int handle = textureBufferAddress != MemoryManager.PteUnmapped
? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4) ? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
: 0; : 0;
@@ -790,7 +792,7 @@ namespace Ryujinx.Graphics.Gpu.Image
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex) ? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex); : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
samplerHandle = samplerBufferAddress != 0 samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped
? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4) ? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
: 0; : 0;
} }
@@ -828,7 +830,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (poolAddress != MemoryManager.PteUnmapped) if (poolAddress != MemoryManager.PteUnmapped)
{ {
texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId); texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
_texturePool = texturePool; _texturePool = texturePool;
} }
} }
@@ -839,7 +841,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (poolAddress != MemoryManager.PteUnmapped) if (poolAddress != MemoryManager.PteUnmapped)
{ {
samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId); samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
_samplerPool = samplerPool; _samplerPool = samplerPool;
} }
} }

View File

@@ -8,6 +8,7 @@ using Ryujinx.Graphics.Texture;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
@@ -39,6 +40,8 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly MultiRangeList<Texture> _textures; private readonly MultiRangeList<Texture> _textures;
private readonly HashSet<Texture> _partiallyMappedTextures; private readonly HashSet<Texture> _partiallyMappedTextures;
private readonly ReaderWriterLockSlim _texturesLock;
private Texture[] _textureOverlaps; private Texture[] _textureOverlaps;
private OverlapInfo[] _overlapInfo; private OverlapInfo[] _overlapInfo;
@@ -57,6 +60,8 @@ namespace Ryujinx.Graphics.Gpu.Image
_textures = new MultiRangeList<Texture>(); _textures = new MultiRangeList<Texture>();
_partiallyMappedTextures = new HashSet<Texture>(); _partiallyMappedTextures = new HashSet<Texture>();
_texturesLock = new ReaderWriterLockSlim();
_textureOverlaps = new Texture[OverlapsBufferInitialCapacity]; _textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
_overlapInfo = new OverlapInfo[OverlapsBufferInitialCapacity]; _overlapInfo = new OverlapInfo[OverlapsBufferInitialCapacity];
@@ -75,10 +80,16 @@ namespace Ryujinx.Graphics.Gpu.Image
MultiRange unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size); MultiRange unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
lock (_textures) _texturesLock.EnterReadLock();
try
{ {
overlapCount = _textures.FindOverlaps(unmapped, ref overlaps); overlapCount = _textures.FindOverlaps(unmapped, ref overlaps);
} }
finally
{
_texturesLock.ExitReadLock();
}
if (overlapCount > 0) if (overlapCount > 0)
{ {
@@ -217,7 +228,18 @@ namespace Ryujinx.Graphics.Gpu.Image
public bool UpdateMapping(Texture texture, MultiRange range) public bool UpdateMapping(Texture texture, MultiRange range)
{ {
// There cannot be an existing texture compatible with this mapping in the texture cache already. // There cannot be an existing texture compatible with this mapping in the texture cache already.
int overlapCount = _textures.FindOverlaps(range, ref _textureOverlaps); int overlapCount;
_texturesLock.EnterReadLock();
try
{
overlapCount = _textures.FindOverlaps(range, ref _textureOverlaps);
}
finally
{
_texturesLock.ExitReadLock();
}
for (int i = 0; i < overlapCount; i++) for (int i = 0; i < overlapCount; i++)
{ {
@@ -231,11 +253,20 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
_texturesLock.EnterWriteLock();
try
{
_textures.Remove(texture); _textures.Remove(texture);
texture.ReplaceRange(range); texture.ReplaceRange(range);
_textures.Add(texture); _textures.Add(texture);
}
finally
{
_texturesLock.ExitWriteLock();
}
return true; return true;
} }
@@ -611,11 +642,17 @@ namespace Ryujinx.Graphics.Gpu.Image
int sameAddressOverlapsCount; int sameAddressOverlapsCount;
lock (_textures) _texturesLock.EnterReadLock();
try
{ {
// Try to find a perfect texture match, with the same address and parameters. // Try to find a perfect texture match, with the same address and parameters.
sameAddressOverlapsCount = _textures.FindOverlaps(address, ref _textureOverlaps); sameAddressOverlapsCount = _textures.FindOverlaps(address, ref _textureOverlaps);
} }
finally
{
_texturesLock.ExitReadLock();
}
Texture texture = null; Texture texture = null;
@@ -698,10 +735,16 @@ namespace Ryujinx.Graphics.Gpu.Image
if (info.Target != Target.TextureBuffer) if (info.Target != Target.TextureBuffer)
{ {
lock (_textures) _texturesLock.EnterReadLock();
try
{ {
overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps); overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
} }
finally
{
_texturesLock.ExitReadLock();
}
} }
if (_overlapInfo.Length != _textureOverlaps.Length) if (_overlapInfo.Length != _textureOverlaps.Length)
@@ -1025,10 +1068,16 @@ namespace Ryujinx.Graphics.Gpu.Image
_cache.Add(texture); _cache.Add(texture);
} }
lock (_textures) _texturesLock.EnterWriteLock();
try
{ {
_textures.Add(texture); _textures.Add(texture);
} }
finally
{
_texturesLock.ExitWriteLock();
}
if (partiallyMapped) if (partiallyMapped)
{ {
@@ -1091,7 +1140,19 @@ namespace Ryujinx.Graphics.Gpu.Image
return null; return null;
} }
int addressMatches = _textures.FindOverlaps(address, ref _textureOverlaps); int addressMatches;
_texturesLock.EnterReadLock();
try
{
addressMatches = _textures.FindOverlaps(address, ref _textureOverlaps);
}
finally
{
_texturesLock.ExitReadLock();
}
Texture textureMatch = null; Texture textureMatch = null;
for (int i = 0; i < addressMatches; i++) for (int i = 0; i < addressMatches; i++)
@@ -1232,10 +1293,16 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture to be removed</param> /// <param name="texture">The texture to be removed</param>
public void RemoveTextureFromCache(Texture texture) public void RemoveTextureFromCache(Texture texture)
{ {
lock (_textures) _texturesLock.EnterWriteLock();
try
{ {
_textures.Remove(texture); _textures.Remove(texture);
} }
finally
{
_texturesLock.ExitWriteLock();
}
lock (_partiallyMappedTextures) lock (_partiallyMappedTextures)
{ {
@@ -1324,13 +1391,19 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
lock (_textures) _texturesLock.EnterReadLock();
try
{ {
foreach (Texture texture in _textures) foreach (Texture texture in _textures)
{ {
texture.Dispose(); texture.Dispose();
} }
} }
finally
{
_texturesLock.ExitReadLock();
}
} }
} }
} }

View File

@@ -88,9 +88,9 @@ namespace Ryujinx.Graphics.Gpu.Image
private MultiRange TextureRange => Storage.Range; private MultiRange TextureRange => Storage.Range;
/// <summary> /// <summary>
/// The views list from the storage texture. /// The views array from the storage texture.
/// </summary> /// </summary>
private List<Texture> _views; private Texture[] _views;
private TextureGroupHandle[] _handles; private TextureGroupHandle[] _handles;
private bool[] _loadNeeded; private bool[] _loadNeeded;
@@ -1074,7 +1074,7 @@ namespace Ryujinx.Graphics.Gpu.Image
public void UpdateViews(List<Texture> views, Texture texture) public void UpdateViews(List<Texture> views, Texture texture)
{ {
// This is saved to calculate overlapping views for each handle. // This is saved to calculate overlapping views for each handle.
_views = views; _views = views.ToArray();
bool layerViews = _hasLayerViews; bool layerViews = _hasLayerViews;
bool mipViews = _hasMipViews; bool mipViews = _hasMipViews;
@@ -1136,9 +1136,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Removes a view from the group, removing it from all overlap lists. /// Removes a view from the group, removing it from all overlap lists.
/// </summary> /// </summary>
/// <param name="views">The views list of the storage texture</param>
/// <param name="view">View to remove from the group</param> /// <param name="view">View to remove from the group</param>
public void RemoveView(Texture view) public void RemoveView(List<Texture> views, Texture view)
{ {
// This is saved to calculate overlapping views for each handle.
_views = views.ToArray();
int offset = FindOffset(view); int offset = FindOffset(view);
foreach (TextureGroupHandle handle in _handles) foreach (TextureGroupHandle handle in _handles)
@@ -1605,9 +1609,11 @@ namespace Ryujinx.Graphics.Gpu.Image
Storage.SignalModifiedDirty(); Storage.SignalModifiedDirty();
if (_views != null) Texture[] views = _views;
if (views != null)
{ {
foreach (Texture texture in _views) foreach (Texture texture in views)
{ {
texture.SignalModifiedDirty(); texture.SignalModifiedDirty();
} }

View File

@@ -121,7 +121,7 @@ namespace Ryujinx.Graphics.Gpu.Image
public TextureGroupHandle(TextureGroup group, public TextureGroupHandle(TextureGroup group,
int offset, int offset,
ulong size, ulong size,
List<Texture> views, IEnumerable<Texture> views,
int firstLayer, int firstLayer,
int firstLevel, int firstLevel,
int baseSlice, int baseSlice,
@@ -201,8 +201,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Calculate a list of which views overlap this handle. /// Calculate a list of which views overlap this handle.
/// </summary> /// </summary>
/// <param name="group">The parent texture group, used to find a view's base CPU VA offset</param> /// <param name="group">The parent texture group, used to find a view's base CPU VA offset</param>
/// <param name="views">The list of views to search for overlaps</param> /// <param name="views">The views to search for overlaps</param>
public void RecalculateOverlaps(TextureGroup group, List<Texture> views) public void RecalculateOverlaps(TextureGroup group, IEnumerable<Texture> views)
{ {
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic. // Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
lock (Overlaps) lock (Overlaps)

View File

@@ -15,6 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly TextureBindingsManager _cpBindingsManager; private readonly TextureBindingsManager _cpBindingsManager;
private readonly TextureBindingsManager _gpBindingsManager; private readonly TextureBindingsManager _gpBindingsManager;
private readonly TextureBindingsArrayCache _bindingsArrayCache;
private readonly TexturePoolCache _texturePoolCache; private readonly TexturePoolCache _texturePoolCache;
private readonly SamplerPoolCache _samplerPoolCache; private readonly SamplerPoolCache _samplerPoolCache;
@@ -46,8 +47,9 @@ namespace Ryujinx.Graphics.Gpu.Image
TexturePoolCache texturePoolCache = new(context); TexturePoolCache texturePoolCache = new(context);
SamplerPoolCache samplerPoolCache = new(context); SamplerPoolCache samplerPoolCache = new(context);
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, isCompute: true); _bindingsArrayCache = new TextureBindingsArrayCache(context, channel);
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, isCompute: false); _cpBindingsManager = new TextureBindingsManager(context, channel, _bindingsArrayCache, texturePoolCache, samplerPoolCache, isCompute: true);
_gpBindingsManager = new TextureBindingsManager(context, channel, _bindingsArrayCache, texturePoolCache, samplerPoolCache, isCompute: false);
_texturePoolCache = texturePoolCache; _texturePoolCache = texturePoolCache;
_samplerPoolCache = samplerPoolCache; _samplerPoolCache = samplerPoolCache;
@@ -384,7 +386,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId); TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
return texturePool; return texturePool;
} }

View File

@@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
TextureBindings[i] = stage.Info.Textures.Select(descriptor => TextureBindings[i] = stage.Info.Textures.Select(descriptor =>
{ {
Target target = ShaderTexture.GetTarget(descriptor.Type); Target target = descriptor.Type != SamplerType.None ? ShaderTexture.GetTarget(descriptor.Type) : default;
var result = new TextureBindingInfo( var result = new TextureBindingInfo(
target, target,
@@ -66,7 +66,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
descriptor.ArrayLength, descriptor.ArrayLength,
descriptor.CbufSlot, descriptor.CbufSlot,
descriptor.HandleIndex, descriptor.HandleIndex,
descriptor.Flags); descriptor.Flags,
descriptor.Type == SamplerType.None);
if (descriptor.ArrayLength <= 1) if (descriptor.ArrayLength <= 1)
{ {

View File

@@ -18,6 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private readonly ShaderSpecializationState _newSpecState; private readonly ShaderSpecializationState _newSpecState;
private readonly int _stageIndex; private readonly int _stageIndex;
private readonly bool _isVulkan; private readonly bool _isVulkan;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;
/// <summary> /// <summary>
/// Creates a new instance of the cached GPU state accessor for shader translation. /// Creates a new instance of the cached GPU state accessor for shader translation.
@@ -29,6 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <param name="newSpecState">Shader specialization state of the recompiled shader</param> /// <param name="newSpecState">Shader specialization state of the recompiled shader</param>
/// <param name="counts">Resource counts shared across all shader stages</param> /// <param name="counts">Resource counts shared across all shader stages</param>
/// <param name="stageIndex">Shader stage index</param> /// <param name="stageIndex">Shader stage index</param>
/// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
public DiskCacheGpuAccessor( public DiskCacheGpuAccessor(
GpuContext context, GpuContext context,
ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> data,
@@ -36,7 +39,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
ShaderSpecializationState oldSpecState, ShaderSpecializationState oldSpecState,
ShaderSpecializationState newSpecState, ShaderSpecializationState newSpecState,
ResourceCounts counts, ResourceCounts counts,
int stageIndex) : base(context, counts, stageIndex) int stageIndex,
bool hasGeometryShader) : base(context, counts, stageIndex)
{ {
_data = data; _data = data;
_cb1Data = cb1Data; _cb1Data = cb1Data;
@@ -44,6 +48,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
_newSpecState = newSpecState; _newSpecState = newSpecState;
_stageIndex = stageIndex; _stageIndex = stageIndex;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan; _isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;
if (stageIndex == (int)ShaderStage.Geometry - 1) if (stageIndex == (int)ShaderStage.Geometry - 1)
{ {
@@ -100,7 +106,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// <inheritdoc/> /// <inheritdoc/>
public GpuGraphicsState QueryGraphicsState() public GpuGraphicsState QueryGraphicsState()
{ {
return _oldSpecState.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _oldSpecState.GraphicsState.YNegateEnabled); return _oldSpecState.GraphicsState.CreateShaderGraphicsState(
!_isVulkan,
_supportsQuads,
_hasGeometryShader,
_isVulkan || _oldSpecState.GraphicsState.YNegateEnabled);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -109,6 +119,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
} }
/// <inheritdoc/>
/// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
public int QuerySamplerArrayLengthFromPool()
{
return QueryArrayLengthFromPool(isSampler: true);
}
/// <inheritdoc/> /// <inheritdoc/>
public SamplerType QuerySamplerType(int handle, int cbufSlot) public SamplerType QuerySamplerType(int handle, int cbufSlot)
{ {
@@ -117,6 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
} }
/// <inheritdoc/> /// <inheritdoc/>
/// <exception cref="DiskCacheLoadException">Constant buffer derived length is not available on the cache</exception>
public int QueryTextureArrayLengthFromBuffer(int slot) public int QueryTextureArrayLengthFromBuffer(int slot)
{ {
if (!_oldSpecState.TextureArrayFromBufferRegistered(_stageIndex, 0, slot)) if (!_oldSpecState.TextureArrayFromBufferRegistered(_stageIndex, 0, slot))
@@ -130,6 +148,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return arrayLength; return arrayLength;
} }
/// <inheritdoc/>
/// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
public int QueryTextureArrayLengthFromPool()
{
return QueryArrayLengthFromPool(isSampler: false);
}
/// <inheritdoc/> /// <inheritdoc/>
public TextureFormat QueryTextureFormat(int handle, int cbufSlot) public TextureFormat QueryTextureFormat(int handle, int cbufSlot)
{ {
@@ -170,6 +195,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
} }
/// <inheritdoc/> /// <inheritdoc/>
/// <exception cref="DiskCacheLoadException">Texture information is not available on the cache</exception>
public void RegisterTexture(int handle, int cbufSlot) public void RegisterTexture(int handle, int cbufSlot)
{ {
if (!_oldSpecState.TextureRegistered(_stageIndex, handle, cbufSlot)) if (!_oldSpecState.TextureRegistered(_stageIndex, handle, cbufSlot))
@@ -182,5 +208,24 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
bool coordNormalized = _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot); bool coordNormalized = _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot);
_newSpecState.RegisterTexture(_stageIndex, handle, cbufSlot, format, formatSrgb, target, coordNormalized); _newSpecState.RegisterTexture(_stageIndex, handle, cbufSlot, format, formatSrgb, target, coordNormalized);
} }
/// <summary>
/// Gets the cached texture or sampler pool capacity.
/// </summary>
/// <param name="isSampler">True to get sampler pool length, false for texture pool length</param>
/// <returns>Pool length</returns>
/// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
private int QueryArrayLengthFromPool(bool isSampler)
{
if (!_oldSpecState.TextureArrayFromPoolRegistered(isSampler))
{
throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureArrayLength);
}
int arrayLength = _oldSpecState.GetTextureArrayFromPoolLength(isSampler);
_newSpecState.RegisterTextureArrayLengthFromPool(isSampler, arrayLength);
return arrayLength;
}
} }
} }

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 = 6489; private const uint CodeGenVersion = 5936;
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

@@ -601,6 +601,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
TargetApi api = _context.Capabilities.Api; TargetApi api = _context.Capabilities.Api;
bool hasCachedGs = guestShaders[4].HasValue;
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--) for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
{ {
if (guestShaders[stageIndex + 1].HasValue) if (guestShaders[stageIndex + 1].HasValue)
@@ -610,7 +612,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
byte[] guestCode = shader.Code; byte[] guestCode = shader.Code;
byte[] cb1Data = shader.Cb1Data; byte[] cb1Data = shader.Cb1Data;
DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex); DiskCacheGpuAccessor gpuAccessor = new(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex, hasCachedGs);
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0); TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0);
if (nextStage != null) if (nextStage != null)
@@ -623,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
byte[] guestCodeA = guestShaders[0].Value.Code; byte[] guestCodeA = guestShaders[0].Value.Code;
byte[] cb1DataA = guestShaders[0].Value.Cb1Data; byte[] cb1DataA = guestShaders[0].Value.Cb1Data;
DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0); DiskCacheGpuAccessor gpuAccessorA = new(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0, hasCachedGs);
translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0); translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0);
} }
@@ -711,7 +713,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
GuestCodeAndCbData shader = guestShaders[0].Value; GuestCodeAndCbData shader = guestShaders[0].Value;
ResourceCounts counts = new(); ResourceCounts counts = new();
ShaderSpecializationState newSpecState = new(ref specState.ComputeState); ShaderSpecializationState newSpecState = new(ref specState.ComputeState);
DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0); DiskCacheGpuAccessor gpuAccessor = new(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0, false);
gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false); gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false);
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0); TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0);

View File

@@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly int _stageIndex; private readonly int _stageIndex;
private readonly bool _compute; private readonly bool _compute;
private readonly bool _isVulkan; private readonly bool _isVulkan;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;
/// <summary> /// <summary>
/// Creates a new instance of the GPU state accessor for graphics shader translation. /// Creates a new instance of the GPU state accessor for graphics shader translation.
@@ -25,12 +27,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="state">Current GPU state</param> /// <param name="state">Current GPU state</param>
/// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param> /// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param>
public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state, int stageIndex) : base(context, state.ResourceCounts, stageIndex) /// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
public GpuAccessor(
GpuContext context,
GpuChannel channel,
GpuAccessorState state,
int stageIndex,
bool hasGeometryShader) : base(context, state.ResourceCounts, stageIndex)
{ {
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_channel = channel; _channel = channel;
_state = state; _state = state;
_stageIndex = stageIndex; _stageIndex = stageIndex;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;
if (stageIndex == (int)ShaderStage.Geometry - 1) if (stageIndex == (int)ShaderStage.Geometry - 1)
{ {
@@ -105,7 +115,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <inheritdoc/> /// <inheritdoc/>
public GpuGraphicsState QueryGraphicsState() public GpuGraphicsState QueryGraphicsState()
{ {
return _state.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _state.GraphicsState.YNegateEnabled); return _state.GraphicsState.CreateShaderGraphicsState(
!_isVulkan,
_supportsQuads,
_hasGeometryShader,
_isVulkan || _state.GraphicsState.YNegateEnabled);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -120,6 +134,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
} }
/// <inheritdoc/>
public int QuerySamplerArrayLengthFromPool()
{
int length = _state.SamplerPoolMaximumId + 1;
_state.SpecializationState?.RegisterTextureArrayLengthFromPool(isSampler: true, length);
return length;
}
/// <inheritdoc/> /// <inheritdoc/>
public SamplerType QuerySamplerType(int handle, int cbufSlot) public SamplerType QuerySamplerType(int handle, int cbufSlot)
{ {
@@ -141,6 +164,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
return arrayLength; return arrayLength;
} }
/// <inheritdoc/>
public int QueryTextureArrayLengthFromPool()
{
int length = _state.PoolState.TexturePoolMaximumId + 1;
_state.SpecializationState?.RegisterTextureArrayLengthFromPool(isSampler: false, length);
return length;
}
//// <inheritdoc/> //// <inheritdoc/>
public TextureFormat QueryTextureFormat(int handle, int cbufSlot) public TextureFormat QueryTextureFormat(int handle, int cbufSlot)
{ {

View File

@@ -213,6 +213,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsScaledVertexFormats() => _context.Capabilities.SupportsScaledVertexFormats; public bool QueryHostSupportsScaledVertexFormats() => _context.Capabilities.SupportsScaledVertexFormats;
public bool QueryHostSupportsSeparateSampler() => _context.Capabilities.SupportsSeparateSampler;
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot; public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
public bool QueryHostSupportsShaderBarrierDivergence() => _context.Capabilities.SupportsShaderBarrierDivergence; public bool QueryHostSupportsShaderBarrierDivergence() => _context.Capabilities.SupportsShaderBarrierDivergence;

View File

@@ -5,6 +5,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
class GpuAccessorState class GpuAccessorState
{ {
/// <summary>
/// Maximum ID that a sampler pool entry may have.
/// </summary>
public readonly int SamplerPoolMaximumId;
/// <summary> /// <summary>
/// GPU texture pool state. /// GPU texture pool state.
/// </summary> /// </summary>
@@ -38,18 +43,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary> /// <summary>
/// Creates a new GPU accessor state. /// Creates a new GPU accessor state.
/// </summary> /// </summary>
/// <param name="samplerPoolMaximumId">Maximum ID that a sampler pool entry may have</param>
/// <param name="poolState">GPU texture pool state</param> /// <param name="poolState">GPU texture pool state</param>
/// <param name="computeState">GPU compute state, for compute shaders</param> /// <param name="computeState">GPU compute state, for compute shaders</param>
/// <param name="graphicsState">GPU graphics state, for vertex, tessellation, geometry and fragment shaders</param> /// <param name="graphicsState">GPU graphics state, for vertex, tessellation, geometry and fragment shaders</param>
/// <param name="specializationState">Shader specialization state (shared by all stages)</param> /// <param name="specializationState">Shader specialization state (shared by all stages)</param>
/// <param name="transformFeedbackDescriptors">Transform feedback information, if the shader uses transform feedback. Otherwise, should be null</param> /// <param name="transformFeedbackDescriptors">Transform feedback information, if the shader uses transform feedback. Otherwise, should be null</param>
public GpuAccessorState( public GpuAccessorState(
int samplerPoolMaximumId,
GpuChannelPoolState poolState, GpuChannelPoolState poolState,
GpuChannelComputeState computeState, GpuChannelComputeState computeState,
GpuChannelGraphicsState graphicsState, GpuChannelGraphicsState graphicsState,
ShaderSpecializationState specializationState, ShaderSpecializationState specializationState,
TransformFeedbackDescriptor[] transformFeedbackDescriptors = null) TransformFeedbackDescriptor[] transformFeedbackDescriptors = null)
{ {
SamplerPoolMaximumId = samplerPoolMaximumId;
PoolState = poolState; PoolState = poolState;
GraphicsState = graphicsState; GraphicsState = graphicsState;
ComputeState = computeState; ComputeState = computeState;

View File

@@ -106,8 +106,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// Creates a new graphics state from this state that can be used for shader generation. /// Creates a new graphics state from this state that can be used for shader generation.
/// </summary> /// </summary>
/// <param name="hostSupportsAlphaTest">Indicates if the host API supports alpha test operations</param> /// <param name="hostSupportsAlphaTest">Indicates if the host API supports alpha test operations</param>
/// <param name="hostSupportsQuads">Indicates if the host API supports quad primitives</param>
/// <param name="hasGeometryShader">Indicates if a geometry shader is used</param>
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
/// <returns>GPU graphics state that can be used for shader translation</returns> /// <returns>GPU graphics state that can be used for shader translation</returns>
public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool originUpperLeft) public readonly GpuGraphicsState CreateShaderGraphicsState(bool hostSupportsAlphaTest, bool hostSupportsQuads, bool hasGeometryShader, bool originUpperLeft)
{ {
AlphaTestOp alphaTestOp; AlphaTestOp alphaTestOp;
@@ -130,6 +133,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
}; };
} }
bool isQuad = Topology == PrimitiveTopology.Quads || Topology == PrimitiveTopology.QuadStrip;
bool halvePrimitiveId = !hostSupportsQuads && !hasGeometryShader && isQuad;
return new GpuGraphicsState( return new GpuGraphicsState(
EarlyZForce, EarlyZForce,
ConvertToInputTopology(Topology, TessellationMode), ConvertToInputTopology(Topology, TessellationMode),
@@ -149,7 +155,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
in FragmentOutputTypes, in FragmentOutputTypes,
DualSourceBlendEnable, DualSourceBlendEnable,
YNegateEnabled, YNegateEnabled,
originUpperLeft); originUpperLeft,
halvePrimitiveId);
} }
/// <summary> /// <summary>

View File

@@ -2,7 +2,6 @@ using System;
namespace Ryujinx.Graphics.Gpu.Shader namespace Ryujinx.Graphics.Gpu.Shader
{ {
#pragma warning disable CS0659 // Class overrides Object.Equals(object o) but does not override Object.GetHashCode()
/// <summary> /// <summary>
/// State used by the <see cref="GpuAccessor"/>. /// State used by the <see cref="GpuAccessor"/>.
/// </summary> /// </summary>
@@ -52,6 +51,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
return obj is GpuChannelPoolState state && Equals(state); return obj is GpuChannelPoolState state && Equals(state);
} }
public override int GetHashCode()
{
return HashCode.Combine(TexturePoolGpuVa, TexturePoolMaximumId, TextureBufferIndex);
}
} }
#pragma warning restore CS0659
} }

View File

@@ -192,12 +192,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// This automatically translates, compiles and adds the code to the cache if not present. /// This automatically translates, compiles and adds the code to the cache if not present.
/// </remarks> /// </remarks>
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="samplerPoolMaximumId">Maximum ID that an entry in the sampler pool may have</param>
/// <param name="poolState">Texture pool state</param> /// <param name="poolState">Texture pool state</param>
/// <param name="computeState">Compute engine state</param> /// <param name="computeState">Compute engine state</param>
/// <param name="gpuVa">GPU virtual address of the binary shader code</param> /// <param name="gpuVa">GPU virtual address of the binary shader code</param>
/// <returns>Compiled compute shader code</returns> /// <returns>Compiled compute shader code</returns>
public CachedShaderProgram GetComputeShader( public CachedShaderProgram GetComputeShader(
GpuChannel channel, GpuChannel channel,
int samplerPoolMaximumId,
GpuChannelPoolState poolState, GpuChannelPoolState poolState,
GpuChannelComputeState computeState, GpuChannelComputeState computeState,
ulong gpuVa) ulong gpuVa)
@@ -214,7 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
ShaderSpecializationState specState = new(ref computeState); ShaderSpecializationState specState = new(ref computeState);
GpuAccessorState gpuAccessorState = new(poolState, computeState, default, specState); GpuAccessorState gpuAccessorState = new(samplerPoolMaximumId, poolState, computeState, default, specState);
GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState); GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState);
gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false); gpuAccessor.InitializeReservedCounts(tfEnabled: false, vertexAsCompute: false);
@@ -291,6 +293,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="state">GPU state</param> /// <param name="state">GPU state</param>
/// <param name="pipeline">Pipeline state</param> /// <param name="pipeline">Pipeline state</param>
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="samplerPoolMaximumId">Maximum ID that an entry in the sampler pool may have</param>
/// <param name="poolState">Texture pool state</param> /// <param name="poolState">Texture pool state</param>
/// <param name="graphicsState">3D engine state</param> /// <param name="graphicsState">3D engine state</param>
/// <param name="addresses">Addresses of the shaders for each stage</param> /// <param name="addresses">Addresses of the shaders for each stage</param>
@@ -299,6 +302,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
ref ThreedClassState state, ref ThreedClassState state,
ref ProgramPipelineState pipeline, ref ProgramPipelineState pipeline,
GpuChannel channel, GpuChannel channel,
int samplerPoolMaximumId,
ref GpuChannelPoolState poolState, ref GpuChannelPoolState poolState,
ref GpuChannelGraphicsState graphicsState, ref GpuChannelGraphicsState graphicsState,
ShaderAddresses addresses) ShaderAddresses addresses)
@@ -319,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel); UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel);
ShaderSpecializationState specState = new(ref graphicsState, ref pipeline, transformFeedbackDescriptors); ShaderSpecializationState specState = new(ref graphicsState, ref pipeline, transformFeedbackDescriptors);
GpuAccessorState gpuAccessorState = new(poolState, default, graphicsState, specState, transformFeedbackDescriptors); GpuAccessorState gpuAccessorState = new(samplerPoolMaximumId, poolState, default, graphicsState, specState, transformFeedbackDescriptors);
ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan(); ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan();
@@ -335,7 +339,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (gpuVa != 0) if (gpuVa != 0)
{ {
GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex); GpuAccessor gpuAccessor = new(_context, channel, gpuAccessorState, stageIndex, addresses.Geometry != 0);
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa); TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);
if (nextStage != null) if (nextStage != null)

View File

@@ -185,11 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
if (texture.ArrayLength > 1) if (texture.ArrayLength > 1)
{ {
bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; ResourceType type = GetTextureResourceType(texture, isImage);
ResourceType type = isBuffer
? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
: (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages)); _resourceDescriptors[setIndex].Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages));
} }
@@ -242,16 +238,38 @@ namespace Ryujinx.Graphics.Gpu.Shader
{ {
foreach (TextureDescriptor texture in textures) foreach (TextureDescriptor texture in textures)
{ {
bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer; ResourceType type = GetTextureResourceType(texture, isImage);
ResourceType type = isBuffer
? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
: (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
_resourceUsages[setIndex].Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages)); _resourceUsages[setIndex].Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
} }
} }
private static ResourceType GetTextureResourceType(TextureDescriptor texture, bool isImage)
{
bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
if (isBuffer)
{
return isImage ? ResourceType.BufferImage : ResourceType.BufferTexture;
}
else if (isImage)
{
return ResourceType.Image;
}
else if (texture.Type == SamplerType.None)
{
return ResourceType.Sampler;
}
else if (texture.Separate)
{
return ResourceType.Texture;
}
else
{
return ResourceType.TextureAndSampler;
}
}
/// <summary> /// <summary>
/// Creates a new shader information structure from the added information. /// Creates a new shader information structure from the added information.
/// </summary> /// </summary>

View File

@@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
PrimitiveTopology = 1 << 1, PrimitiveTopology = 1 << 1,
TransformFeedback = 1 << 3, TransformFeedback = 1 << 3,
TextureArrayFromBuffer = 1 << 4, TextureArrayFromBuffer = 1 << 4,
TextureArrayFromPool = 1 << 5,
} }
private QueriedStateFlags _queriedState; private QueriedStateFlags _queriedState;
@@ -154,7 +155,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization; private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
private readonly Dictionary<TextureKey, int> _textureArraySpecialization; private readonly Dictionary<TextureKey, int> _textureArrayFromBufferSpecialization;
private readonly Dictionary<bool, int> _textureArrayFromPoolSpecialization;
private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures; private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures;
private Box<TextureSpecializationState>[][] _textureByBinding; private Box<TextureSpecializationState>[][] _textureByBinding;
private Box<TextureSpecializationState>[][] _imageByBinding; private Box<TextureSpecializationState>[][] _imageByBinding;
@@ -165,7 +167,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
private ShaderSpecializationState() private ShaderSpecializationState()
{ {
_textureSpecialization = new Dictionary<TextureKey, Box<TextureSpecializationState>>(); _textureSpecialization = new Dictionary<TextureKey, Box<TextureSpecializationState>>();
_textureArraySpecialization = new Dictionary<TextureKey, int>(); _textureArrayFromBufferSpecialization = new Dictionary<TextureKey, int>();
_textureArrayFromPoolSpecialization = new Dictionary<bool, int>();
} }
/// <summary> /// <summary>
@@ -327,7 +330,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
/// <summary> /// <summary>
/// Indicates that the coordinate normalization state of a given texture was used during the shader translation process. /// Registers the length of a texture array calculated from a constant buffer size.
/// </summary> /// </summary>
/// <param name="stageIndex">Shader stage where the texture is used</param> /// <param name="stageIndex">Shader stage where the texture is used</param>
/// <param name="handle">Offset in words of the texture handle on the texture buffer</param> /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
@@ -335,10 +338,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="length">Number of elements in the texture array</param> /// <param name="length">Number of elements in the texture array</param>
public void RegisterTextureArrayLengthFromBuffer(int stageIndex, int handle, int cbufSlot, int length) public void RegisterTextureArrayLengthFromBuffer(int stageIndex, int handle, int cbufSlot, int length)
{ {
_textureArraySpecialization[new TextureKey(stageIndex, handle, cbufSlot)] = length; _textureArrayFromBufferSpecialization[new TextureKey(stageIndex, handle, cbufSlot)] = length;
_queriedState |= QueriedStateFlags.TextureArrayFromBuffer; _queriedState |= QueriedStateFlags.TextureArrayFromBuffer;
} }
/// <summary>
/// Registers the length of a texture array calculated from a texture or sampler pool capacity.
/// </summary>
/// <param name="isSampler">True for sampler pool, false for texture pool</param>
/// <param name="length">Number of elements in the texture array</param>
public void RegisterTextureArrayLengthFromPool(bool isSampler, int length)
{
_textureArrayFromPoolSpecialization[isSampler] = length;
_queriedState |= QueriedStateFlags.TextureArrayFromPool;
}
/// <summary> /// <summary>
/// Indicates that the format of a given texture was used during the shader translation process. /// Indicates that the format of a given texture was used during the shader translation process.
/// </summary> /// </summary>
@@ -385,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
/// <summary> /// <summary>
/// Checks if a given texture was registerd on this specialization state. /// Checks if a given texture was registered on this specialization state.
/// </summary> /// </summary>
/// <param name="stageIndex">Shader stage where the texture is used</param> /// <param name="stageIndex">Shader stage where the texture is used</param>
/// <param name="handle">Offset in words of the texture handle on the texture buffer</param> /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
@@ -396,14 +410,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
/// <summary> /// <summary>
/// Checks if a given texture array (from constant buffer) was registerd on this specialization state. /// Checks if a given texture array (from constant buffer) was registered on this specialization state.
/// </summary> /// </summary>
/// <param name="stageIndex">Shader stage where the texture is used</param> /// <param name="stageIndex">Shader stage where the texture is used</param>
/// <param name="handle">Offset in words of the texture handle on the texture buffer</param> /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
/// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
/// <returns>True if the length for the given buffer and stage exists, false otherwise</returns>
public bool TextureArrayFromBufferRegistered(int stageIndex, int handle, int cbufSlot) public bool TextureArrayFromBufferRegistered(int stageIndex, int handle, int cbufSlot)
{ {
return _textureArraySpecialization.ContainsKey(new TextureKey(stageIndex, handle, cbufSlot)); return _textureArrayFromBufferSpecialization.ContainsKey(new TextureKey(stageIndex, handle, cbufSlot));
}
/// <summary>
/// Checks if a given texture array (from a sampler pool or texture pool) was registered on this specialization state.
/// </summary>
/// <param name="isSampler">True for sampler pool, false for texture pool</param>
/// <returns>True if the length for the given pool, false otherwise</returns>
public bool TextureArrayFromPoolRegistered(bool isSampler)
{
return _textureArrayFromPoolSpecialization.ContainsKey(isSampler);
} }
/// <summary> /// <summary>
@@ -412,6 +437,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="stageIndex">Shader stage where the texture is used</param> /// <param name="stageIndex">Shader stage where the texture is used</param>
/// <param name="handle">Offset in words of the texture handle on the texture buffer</param> /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
/// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
/// <returns>Format and sRGB tuple</returns>
public (uint, bool) GetFormat(int stageIndex, int handle, int cbufSlot) public (uint, bool) GetFormat(int stageIndex, int handle, int cbufSlot)
{ {
TextureSpecializationState state = GetTextureSpecState(stageIndex, handle, cbufSlot).Value; TextureSpecializationState state = GetTextureSpecState(stageIndex, handle, cbufSlot).Value;
@@ -424,6 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="stageIndex">Shader stage where the texture is used</param> /// <param name="stageIndex">Shader stage where the texture is used</param>
/// <param name="handle">Offset in words of the texture handle on the texture buffer</param> /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
/// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
/// <returns>Texture target</returns>
public TextureTarget GetTextureTarget(int stageIndex, int handle, int cbufSlot) public TextureTarget GetTextureTarget(int stageIndex, int handle, int cbufSlot)
{ {
return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.TextureTarget; return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.TextureTarget;
@@ -435,6 +462,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="stageIndex">Shader stage where the texture is used</param> /// <param name="stageIndex">Shader stage where the texture is used</param>
/// <param name="handle">Offset in words of the texture handle on the texture buffer</param> /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
/// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
/// <returns>True if coordinates are normalized, false otherwise</returns>
public bool GetCoordNormalized(int stageIndex, int handle, int cbufSlot) public bool GetCoordNormalized(int stageIndex, int handle, int cbufSlot)
{ {
return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized; return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized;
@@ -446,9 +474,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="stageIndex">Shader stage where the texture is used</param> /// <param name="stageIndex">Shader stage where the texture is used</param>
/// <param name="handle">Offset in words of the texture handle on the texture buffer</param> /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
/// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
/// <returns>Texture array length</returns>
public int GetTextureArrayFromBufferLength(int stageIndex, int handle, int cbufSlot) public int GetTextureArrayFromBufferLength(int stageIndex, int handle, int cbufSlot)
{ {
return _textureArraySpecialization[new TextureKey(stageIndex, handle, cbufSlot)]; return _textureArrayFromBufferSpecialization[new TextureKey(stageIndex, handle, cbufSlot)];
}
/// <summary>
/// Gets the recorded length of a given texture array (from a sampler or texture pool).
/// </summary>
/// <param name="isSampler">True to get the sampler pool length, false to get the texture pool length</param>
/// <returns>Texture array length</returns>
public int GetTextureArrayFromPoolLength(bool isSampler)
{
return _textureArrayFromPoolSpecialization[isSampler];
} }
/// <summary> /// <summary>
@@ -894,7 +933,23 @@ namespace Ryujinx.Graphics.Gpu.Shader
dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic); dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic);
dataReader.Read(ref length); dataReader.Read(ref length);
specState._textureArraySpecialization[textureKey] = length; specState._textureArrayFromBufferSpecialization[textureKey] = length;
}
}
if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool))
{
dataReader.Read(ref count);
for (int index = 0; index < count; index++)
{
bool textureKey = default;
int length = 0;
dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic);
dataReader.Read(ref length);
specState._textureArrayFromPoolSpecialization[textureKey] = length;
} }
} }
@@ -965,10 +1020,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer)) if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer))
{ {
count = (ushort)_textureArraySpecialization.Count; count = (ushort)_textureArrayFromBufferSpecialization.Count;
dataWriter.Write(ref count); dataWriter.Write(ref count);
foreach (var kv in _textureArraySpecialization) foreach (var kv in _textureArrayFromBufferSpecialization)
{
var textureKey = kv.Key;
var length = kv.Value;
dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic);
dataWriter.Write(ref length);
}
}
if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool))
{
count = (ushort)_textureArrayFromPoolSpecialization.Count;
dataWriter.Write(ref count);
foreach (var kv in _textureArrayFromPoolSpecialization)
{ {
var textureKey = kv.Key; var textureKey = kv.Key;
var length = kv.Value; var length = kv.Value;

View File

@@ -161,6 +161,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsBgraFormat: false, supportsBgraFormat: false,
supportsR4G4Format: false, supportsR4G4Format: false,
supportsR4G4B4A4Format: true, supportsR4G4B4A4Format: true,
supportsScaledVertexFormats: true,
supportsSnormBufferTextureFormat: false, supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true, supports5BitComponentFormat: true,
supportsSparseBuffer: false, supportsSparseBuffer: false,
@@ -175,7 +176,8 @@ namespace Ryujinx.Graphics.OpenGL
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat, supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
supportsCubemapView: true, supportsCubemapView: true,
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset, supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
supportsScaledVertexFormats: true, supportsQuads: HwCapabilities.SupportsQuads,
supportsSeparateSampler: false,
supportsShaderBallot: HwCapabilities.SupportsShaderBallot, supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsShaderBarrierDivergence: !(intelWindows || intelUnix), supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
supportsShaderFloat64: true, supportsShaderFloat64: true,

View File

@@ -6,7 +6,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Numerics;
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
@@ -352,7 +351,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
arrayDecl = "[]"; arrayDecl = "[]";
} }
string samplerTypeName = definition.Type.ToGlslSamplerType(); string samplerTypeName = definition.Separate ? definition.Type.ToGlslTextureType() : definition.Type.ToGlslSamplerType();
string layout = string.Empty; string layout = string.Empty;

View File

@@ -639,14 +639,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex)
{ {
TextureDefinition definition = context.Properties.Textures[texOp.Binding]; TextureDefinition textureDefinition = context.Properties.Textures[texOp.Binding];
string name = definition.Name; string name = textureDefinition.Name;
if (definition.ArrayLength != 1) if (textureDefinition.ArrayLength != 1)
{ {
name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
} }
if (texOp.IsSeparate)
{
TextureDefinition samplerDefinition = context.Properties.Textures[texOp.SamplerBinding];
string samplerName = samplerDefinition.Name;
if (samplerDefinition.ArrayLength != 1)
{
samplerName = $"{samplerName}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
}
name = $"{texOp.Type.ToGlslSamplerType()}({name}, {samplerName})";
}
return name; return name;
} }

View File

@@ -160,6 +160,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
int setIndex = context.TargetApi == TargetApi.Vulkan ? sampler.Set : 0; int setIndex = context.TargetApi == TargetApi.Vulkan ? sampler.Set : 0;
SpvInstruction imageType;
SpvInstruction sampledImageType;
if (sampler.Type != SamplerType.None)
{
var dim = (sampler.Type & SamplerType.Mask) switch var dim = (sampler.Type & SamplerType.Mask) switch
{ {
SamplerType.Texture1D => Dim.Dim1D, SamplerType.Texture1D => Dim.Dim1D,
@@ -170,7 +175,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
_ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\"."), _ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\"."),
}; };
var imageType = context.TypeImage( imageType = context.TypeImage(
context.TypeFP32(), context.TypeFP32(),
dim, dim,
sampler.Type.HasFlag(SamplerType.Shadow), sampler.Type.HasFlag(SamplerType.Shadow),
@@ -179,18 +184,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
1, 1,
ImageFormat.Unknown); ImageFormat.Unknown);
var sampledImageType = context.TypeSampledImage(imageType); sampledImageType = context.TypeSampledImage(imageType);
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType); }
else
{
imageType = sampledImageType = context.TypeSampler();
}
var sampledOrSeparateImageType = sampler.Separate ? imageType : sampledImageType;
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledOrSeparateImageType);
var sampledImageArrayPointerType = sampledImagePointerType; var sampledImageArrayPointerType = sampledImagePointerType;
if (sampler.ArrayLength == 0) if (sampler.ArrayLength == 0)
{ {
var sampledImageArrayType = context.TypeRuntimeArray(sampledImageType); var sampledImageArrayType = context.TypeRuntimeArray(sampledOrSeparateImageType);
sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType); sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
} }
else if (sampler.ArrayLength != 1) else if (sampler.ArrayLength != 1)
{ {
var sampledImageArrayType = context.TypeArray(sampledImageType, context.Constant(context.TypeU32(), sampler.ArrayLength)); var sampledImageArrayType = context.TypeArray(sampledOrSeparateImageType, context.Constant(context.TypeU32(), sampler.ArrayLength));
sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType); sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
} }

View File

@@ -838,16 +838,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SamplerDeclaration declaration = context.Samplers[texOp.Binding];
SpvInstruction image = declaration.Image; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
if (declaration.IsIndexed)
{
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
image = context.Load(declaration.SampledImageType, image);
int pCount = texOp.Type.GetDimensions(); int pCount = texOp.Type.GetDimensions();
@@ -1171,16 +1162,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SamplerDeclaration declaration = context.Samplers[texOp.Binding];
SpvInstruction image = declaration.Image; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
if (declaration.IsIndexed)
{
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
image = context.Load(declaration.SampledImageType, image);
int coordsCount = texOp.Type.GetDimensions(); int coordsCount = texOp.Type.GetDimensions();
@@ -1449,17 +1431,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
AstTextureOperation texOp = (AstTextureOperation)operation; AstTextureOperation texOp = (AstTextureOperation)operation;
int srcIndex = 0;
SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SamplerDeclaration declaration = context.Samplers[texOp.Binding];
SpvInstruction image = declaration.Image; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
if (declaration.IsIndexed)
{
SpvInstruction textureIndex = context.GetS32(texOp.GetSource(0));
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
image = context.Load(declaration.SampledImageType, image);
image = context.Image(declaration.ImageType, image); image = context.Image(declaration.ImageType, image);
SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image); SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image);
@@ -1471,17 +1447,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{ {
AstTextureOperation texOp = (AstTextureOperation)operation; AstTextureOperation texOp = (AstTextureOperation)operation;
int srcIndex = 0;
SamplerDeclaration declaration = context.Samplers[texOp.Binding]; SamplerDeclaration declaration = context.Samplers[texOp.Binding];
SpvInstruction image = declaration.Image; SpvInstruction image = GenerateSampledImageLoad(context, texOp, declaration, ref srcIndex);
if (declaration.IsIndexed)
{
SpvInstruction textureIndex = context.GetS32(texOp.GetSource(0));
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
image = context.Load(declaration.SampledImageType, image);
image = context.Image(declaration.ImageType, image); image = context.Image(declaration.ImageType, image);
if (texOp.Index == 3) if (texOp.Index == 3)
@@ -1506,8 +1476,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (hasLod) if (hasLod)
{ {
int lodSrcIndex = declaration.IsIndexed ? 1 : 0; var lod = context.GetS32(operation.GetSource(srcIndex));
var lod = context.GetS32(operation.GetSource(lodSrcIndex));
result = context.ImageQuerySizeLod(resultType, image, lod); result = context.ImageQuerySizeLod(resultType, image, lod);
} }
else else
@@ -1905,6 +1874,43 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
} }
private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
{
SpvInstruction image = declaration.Image;
if (declaration.IsIndexed)
{
SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
}
if (texOp.IsSeparate)
{
image = context.Load(declaration.ImageType, image);
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.SamplerBinding];
SpvInstruction sampler = samplerDeclaration.Image;
if (samplerDeclaration.IsIndexed)
{
SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
}
sampler = context.Load(samplerDeclaration.ImageType, sampler);
image = context.SampledImage(declaration.SampledImageType, image, sampler);
}
else
{
image = context.Load(declaration.SampledImageType, image);
}
return image;
}
private static OperationResult GenerateUnary( private static OperationResult GenerateUnary(
CodeGenContext context, CodeGenContext context,
AstOperation operation, AstOperation operation,

View File

@@ -102,6 +102,11 @@ namespace Ryujinx.Graphics.Shader
/// </summary> /// </summary>
public readonly bool OriginUpperLeft; public readonly bool OriginUpperLeft;
/// <summary>
/// Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion.
/// </summary>
public readonly bool HalvePrimitiveId;
/// <summary> /// <summary>
/// Creates a new GPU graphics state. /// Creates a new GPU graphics state.
/// </summary> /// </summary>
@@ -124,6 +129,7 @@ namespace Ryujinx.Graphics.Shader
/// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param> /// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param>
/// <param name="yNegateEnabled">Indicates if negation of the viewport Y axis is enabled</param> /// <param name="yNegateEnabled">Indicates if negation of the viewport Y axis is enabled</param>
/// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param> /// <param name="originUpperLeft">If true, indicates that the fragment origin is the upper left corner of the viewport, otherwise it is the lower left corner</param>
/// <param name="halvePrimitiveId">Indicates that the primitive ID values on the shader should be halved due to quad to triangles conversion</param>
public GpuGraphicsState( public GpuGraphicsState(
bool earlyZForce, bool earlyZForce,
InputTopology topology, InputTopology topology,
@@ -143,7 +149,8 @@ namespace Ryujinx.Graphics.Shader
in Array8<AttributeType> fragmentOutputTypes, in Array8<AttributeType> fragmentOutputTypes,
bool dualSourceBlendEnable, bool dualSourceBlendEnable,
bool yNegateEnabled, bool yNegateEnabled,
bool originUpperLeft) bool originUpperLeft,
bool halvePrimitiveId)
{ {
EarlyZForce = earlyZForce; EarlyZForce = earlyZForce;
Topology = topology; Topology = topology;
@@ -164,6 +171,7 @@ namespace Ryujinx.Graphics.Shader
DualSourceBlendEnable = dualSourceBlendEnable; DualSourceBlendEnable = dualSourceBlendEnable;
YNegateEnabled = yNegateEnabled; YNegateEnabled = yNegateEnabled;
OriginUpperLeft = originUpperLeft; OriginUpperLeft = originUpperLeft;
HalvePrimitiveId = halvePrimitiveId;
} }
} }
} }

View File

@@ -26,13 +26,6 @@ namespace Ryujinx.Graphics.Shader
/// <returns>Span of the memory location</returns> /// <returns>Span of the memory location</returns>
ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize); ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize);
/// <summary>
/// Gets the size in bytes of a bound constant buffer for the current shader stage.
/// </summary>
/// <param name="slot">The number of the constant buffer to get the size from</param>
/// <returns>Size in bytes</returns>
int QueryTextureArrayLengthFromBuffer(int slot);
/// <summary> /// <summary>
/// Queries the binding number of a constant buffer. /// Queries the binding number of a constant buffer.
/// </summary> /// </summary>
@@ -142,6 +135,7 @@ namespace Ryujinx.Graphics.Shader
default, default,
false, false,
false, false,
false,
false); false);
} }
@@ -298,6 +292,15 @@ namespace Ryujinx.Graphics.Shader
return true; return true;
} }
/// <summary>
/// Queries host API support for separate textures and samplers.
/// </summary>
/// <returns>True if the API supports samplers and textures to be combined on the shader, false otherwise</returns>
bool QueryHostSupportsSeparateSampler()
{
return true;
}
/// <summary> /// <summary>
/// Queries host GPU shader ballot support. /// Queries host GPU shader ballot support.
/// </summary> /// </summary>
@@ -388,6 +391,12 @@ namespace Ryujinx.Graphics.Shader
return true; return true;
} }
/// <summary>
/// Gets the maximum number of samplers that the bound texture pool may have.
/// </summary>
/// <returns>Maximum amount of samplers that the pool may have</returns>
int QuerySamplerArrayLengthFromPool();
/// <summary> /// <summary>
/// Queries sampler type information. /// Queries sampler type information.
/// </summary> /// </summary>
@@ -399,6 +408,19 @@ namespace Ryujinx.Graphics.Shader
return SamplerType.Texture2D; return SamplerType.Texture2D;
} }
/// <summary>
/// Gets the size in bytes of a bound constant buffer for the current shader stage.
/// </summary>
/// <param name="slot">The number of the constant buffer to get the size from</param>
/// <returns>Size in bytes</returns>
int QueryTextureArrayLengthFromBuffer(int slot);
/// <summary>
/// Gets the maximum number of textures that the bound texture pool may have.
/// </summary>
/// <returns>Maximum amount of textures that the pool may have</returns>
int QueryTextureArrayLengthFromPool();
/// <summary> /// <summary>
/// Queries texture coordinate normalization information. /// Queries texture coordinate normalization information.
/// </summary> /// </summary>

View File

@@ -84,6 +84,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
value = context.IConvertU32ToFP32(value); value = context.IConvertU32ToFP32(value);
} }
} }
else if (offset == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
{
value = context.ShiftRightS32(value, Const(1));
}
context.Copy(Register(rd), value); context.Copy(Register(rd), value);
} }
@@ -187,6 +191,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
} }
} }
} }
else if (op.Imm10 == AttributeConsts.PrimitiveId && context.TranslatorContext.Definitions.HalvePrimitiveId)
{
// If quads are used, but the host does not support them, they need to be converted to triangles.
// Since each quad becomes 2 triangles, we need to compensate here and divide primitive ID by 2.
res = context.ShiftRightS32(res, Const(1));
}
else if (op.Imm10 == AttributeConsts.FrontFacing && context.TranslatorContext.GpuAccessor.QueryHostHasFrontFacingBug()) else if (op.Imm10 == AttributeConsts.FrontFacing && context.TranslatorContext.GpuAccessor.QueryHostHasFrontFacingBug())
{ {
// gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs. // gl_FrontFacing sometimes has incorrect (flipped) values depending how it is accessed on Intel GPUs.

View File

@@ -216,6 +216,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
newSources[index] = source; newSources[index] = source;
if (source != null && source.Type == OperandType.LocalVariable)
{
source.UseOps.Add(this);
}
_sources = newSources; _sources = newSources;
} }

View File

@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
public TextureFlags Flags { get; private set; } public TextureFlags Flags { get; private set; }
public int Binding { get; private set; } public int Binding { get; private set; }
public int SamplerBinding { get; private set; }
public TextureOperation( public TextureOperation(
Instruction inst, Instruction inst,
@@ -24,6 +25,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Format = format; Format = format;
Flags = flags; Flags = flags;
Binding = binding; Binding = binding;
SamplerBinding = -1;
} }
public void TurnIntoArray(int binding) public void TurnIntoArray(int binding)
@@ -32,6 +34,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
Binding = binding; Binding = binding;
} }
public void TurnIntoArray(int textureBinding, int samplerBinding)
{
TurnIntoArray(textureBinding);
SamplerBinding = samplerBinding;
}
public void SetBinding(int binding) public void SetBinding(int binding)
{ {
if ((Flags & TextureFlags.Bindless) != 0) if ((Flags & TextureFlags.Bindless) != 0)

View File

@@ -69,6 +69,7 @@ namespace Ryujinx.Graphics.Shader
{ {
string typeName = (type & SamplerType.Mask) switch string typeName = (type & SamplerType.Mask) switch
{ {
SamplerType.None => "sampler",
SamplerType.Texture1D => "sampler1D", SamplerType.Texture1D => "sampler1D",
SamplerType.TextureBuffer => "samplerBuffer", SamplerType.TextureBuffer => "samplerBuffer",
SamplerType.Texture2D => "sampler2D", SamplerType.Texture2D => "sampler2D",
@@ -95,6 +96,31 @@ namespace Ryujinx.Graphics.Shader
return typeName; return typeName;
} }
public static string ToGlslTextureType(this SamplerType type)
{
string typeName = (type & SamplerType.Mask) switch
{
SamplerType.Texture1D => "texture1D",
SamplerType.TextureBuffer => "textureBuffer",
SamplerType.Texture2D => "texture2D",
SamplerType.Texture3D => "texture3D",
SamplerType.TextureCube => "textureCube",
_ => throw new ArgumentException($"Invalid texture type \"{type}\"."),
};
if ((type & SamplerType.Multisample) != 0)
{
typeName += "MS";
}
if ((type & SamplerType.Array) != 0)
{
typeName += "Array";
}
return typeName;
}
public static string ToGlslImageType(this SamplerType type, AggregateType componentType) public static string ToGlslImageType(this SamplerType type, AggregateType componentType)
{ {
string typeName = (type & SamplerType.Mask) switch string typeName = (type & SamplerType.Mask) switch

View File

@@ -9,6 +9,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public TextureFlags Flags { get; } public TextureFlags Flags { get; }
public int Binding { get; } public int Binding { get; }
public int SamplerBinding { get; }
public bool IsSeparate => SamplerBinding >= 0;
public AstTextureOperation( public AstTextureOperation(
Instruction inst, Instruction inst,
@@ -16,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
TextureFormat format, TextureFormat format,
TextureFlags flags, TextureFlags flags,
int binding, int binding,
int samplerBinding,
int index, int index,
params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length) params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length)
{ {
@@ -23,6 +27,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Format = format; Format = format;
Flags = flags; Flags = flags;
Binding = binding; Binding = binding;
SamplerBinding = samplerBinding;
} }
} }
} }

View File

@@ -169,7 +169,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
AstTextureOperation GetAstTextureOperation(TextureOperation texOp) AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
{ {
return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.Index, sources); return new AstTextureOperation(inst, texOp.Type, texOp.Format, texOp.Flags, texOp.Binding, texOp.SamplerBinding, texOp.Index, sources);
} }
int componentsCount = BitOperations.PopCount((uint)operation.Index); int componentsCount = BitOperations.PopCount((uint)operation.Index);

View File

@@ -5,25 +5,39 @@ namespace Ryujinx.Graphics.Shader
public int Set { get; } public int Set { get; }
public int Binding { get; } public int Binding { get; }
public int ArrayLength { get; } public int ArrayLength { get; }
public bool Separate { get; }
public string Name { get; } public string Name { get; }
public SamplerType Type { get; } public SamplerType Type { get; }
public TextureFormat Format { get; } public TextureFormat Format { get; }
public TextureUsageFlags Flags { get; } public TextureUsageFlags Flags { get; }
public TextureDefinition(int set, int binding, int arrayLength, string name, SamplerType type, TextureFormat format, TextureUsageFlags flags) public TextureDefinition(
int set,
int binding,
int arrayLength,
bool separate,
string name,
SamplerType type,
TextureFormat format,
TextureUsageFlags flags)
{ {
Set = set; Set = set;
Binding = binding; Binding = binding;
ArrayLength = arrayLength; ArrayLength = arrayLength;
Separate = separate;
Name = name; Name = name;
Type = type; Type = type;
Format = format; Format = format;
Flags = flags; Flags = flags;
} }
public TextureDefinition(int set, int binding, string name, SamplerType type) : this(set, binding, 1, false, name, type, TextureFormat.Unknown, TextureUsageFlags.None)
{
}
public TextureDefinition SetFlag(TextureUsageFlags flag) public TextureDefinition SetFlag(TextureUsageFlags flag)
{ {
return new TextureDefinition(Set, Binding, ArrayLength, Name, Type, Format, Flags | flag); return new TextureDefinition(Set, Binding, ArrayLength, Separate, Name, Type, Format, Flags | flag);
} }
} }
} }

View File

@@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.Shader
public readonly int HandleIndex; public readonly int HandleIndex;
public readonly int ArrayLength; public readonly int ArrayLength;
public readonly bool Separate;
public readonly TextureUsageFlags Flags; public readonly TextureUsageFlags Flags;
public TextureDescriptor( public TextureDescriptor(
@@ -22,6 +24,7 @@ namespace Ryujinx.Graphics.Shader
int cbufSlot, int cbufSlot,
int handleIndex, int handleIndex,
int arrayLength, int arrayLength,
bool separate,
TextureUsageFlags flags) TextureUsageFlags flags)
{ {
Binding = binding; Binding = binding;
@@ -30,6 +33,7 @@ namespace Ryujinx.Graphics.Shader
CbufSlot = cbufSlot; CbufSlot = cbufSlot;
HandleIndex = handleIndex; HandleIndex = handleIndex;
ArrayLength = arrayLength; ArrayLength = arrayLength;
Separate = separate;
Flags = flags; Flags = flags;
} }
} }

View File

@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader
SeparateSamplerHandle = 1, SeparateSamplerHandle = 1,
SeparateSamplerId = 2, SeparateSamplerId = 2,
SeparateConstantSamplerHandle = 3, SeparateConstantSamplerHandle = 3,
Direct = 4,
} }
public static class TextureHandle public static class TextureHandle

View File

@@ -1,6 +1,7 @@
using Ryujinx.Graphics.Shader.Instructions; using Ryujinx.Graphics.Shader.Instructions;
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.StructuredIr;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Shader.Translation.Optimizations namespace Ryujinx.Graphics.Shader.Translation.Optimizations
@@ -31,7 +32,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
continue; continue;
} }
if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp)) if (!TryConvertBindless(block, resourceManager, gpuAccessor, texOp) &&
!GenerateBindlessAccess(block, resourceManager, gpuAccessor, texOp, node))
{ {
// If we can't do bindless elimination, remove the texture operation. // If we can't do bindless elimination, remove the texture operation.
// Set any destination variables to zero. // Set any destination variables to zero.
@@ -46,6 +48,88 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
} }
} }
private static bool GenerateBindlessAccess(
BasicBlock block,
ResourceManager resourceManager,
IGpuAccessor gpuAccessor,
TextureOperation texOp,
LinkedListNode<INode> node)
{
if (!gpuAccessor.QueryHostSupportsSeparateSampler())
{
// We depend on combining samplers and textures in the shader being supported for this.
return false;
}
Operand nvHandle = texOp.GetSource(0);
if (nvHandle.AsgOp is not Operation handleOp ||
handleOp.Inst != Instruction.Load ||
(handleOp.StorageKind != StorageKind.Input && handleOp.StorageKind != StorageKind.StorageBuffer))
{
// Right now, we only allow bindless access when the handle comes from a shader input or storage buffer.
// This is an artificial limitation to prevent it from being used in cases where it
// would have a large performance impact of loading all textures in the pool.
// It might be removed in the future, if we can mitigate the performance impact.
return false;
}
Operand textureHandle = OperandHelper.Local();
Operand samplerHandle = OperandHelper.Local();
Operand textureIndex = OperandHelper.Local();
block.Operations.AddBefore(node, new Operation(Instruction.BitwiseAnd, textureHandle, nvHandle, OperandHelper.Const(0xfffff)));
block.Operations.AddBefore(node, new Operation(Instruction.ShiftRightU32, samplerHandle, nvHandle, OperandHelper.Const(20)));
int texturePoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QueryTextureArrayLengthFromPool());
block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, textureIndex, textureHandle, OperandHelper.Const(texturePoolLength - 1)));
texOp.SetSource(0, textureIndex);
bool hasSampler = !texOp.Inst.IsImage();
int textureBinding = resourceManager.GetTextureOrImageBinding(
texOp.Inst,
texOp.Type,
texOp.Format,
texOp.Flags & ~TextureFlags.Bindless,
0,
TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
texturePoolLength,
hasSampler);
if (hasSampler)
{
Operand samplerIndex = OperandHelper.Local();
int samplerPoolLength = Math.Max(BindlessToArray.MinimumArrayLength, gpuAccessor.QuerySamplerArrayLengthFromPool());
block.Operations.AddBefore(node, new Operation(Instruction.MinimumU32, samplerIndex, samplerHandle, OperandHelper.Const(samplerPoolLength - 1)));
texOp.InsertSource(1, samplerIndex);
int samplerBinding = resourceManager.GetTextureOrImageBinding(
texOp.Inst,
SamplerType.None,
texOp.Format,
TextureFlags.None,
0,
TextureHandle.PackOffsets(0, 0, TextureHandleType.Direct),
samplerPoolLength);
texOp.TurnIntoArray(textureBinding, samplerBinding);
}
else
{
texOp.TurnIntoArray(textureBinding);
}
return true;
}
private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp) private static bool TryConvertBindless(BasicBlock block, ResourceManager resourceManager, IGpuAccessor gpuAccessor, TextureOperation texOp)
{ {
if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery()) if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())

View File

@@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
private const int HardcodedArrayLengthOgl = 4; private const int HardcodedArrayLengthOgl = 4;
// 1 and 0 elements are not considered arrays anymore. // 1 and 0 elements are not considered arrays anymore.
private const int MinimumArrayLength = 2; public const int MinimumArrayLength = 2;
public static void RunPassOgl(BasicBlock block, ResourceManager resourceManager) public static void RunPassOgl(BasicBlock block, ResourceManager resourceManager)
{ {

View File

@@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly HashSet<int> _usedConstantBufferBindings; private readonly HashSet<int> _usedConstantBufferBindings;
private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, SamplerType Type, TextureFormat Format); private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, bool Separate, SamplerType Type, TextureFormat Format);
private struct TextureMeta private struct TextureMeta
{ {
@@ -225,7 +225,8 @@ namespace Ryujinx.Graphics.Shader.Translation
TextureFlags flags, TextureFlags flags,
int cbufSlot, int cbufSlot,
int handle, int handle,
int arrayLength = 1) int arrayLength = 1,
bool separate = false)
{ {
inst &= Instruction.Mask; inst &= Instruction.Mask;
bool isImage = inst.IsImage(); bool isImage = inst.IsImage();
@@ -239,7 +240,18 @@ namespace Ryujinx.Graphics.Shader.Translation
format = TextureFormat.Unknown; format = TextureFormat.Unknown;
} }
int binding = GetTextureOrImageBinding(cbufSlot, handle, arrayLength, type, format, isImage, intCoords, isWrite, accurateType, coherent); int binding = GetTextureOrImageBinding(
cbufSlot,
handle,
arrayLength,
type,
format,
isImage,
intCoords,
isWrite,
accurateType,
coherent,
separate);
_gpuAccessor.RegisterTexture(handle, cbufSlot); _gpuAccessor.RegisterTexture(handle, cbufSlot);
@@ -256,9 +268,10 @@ namespace Ryujinx.Graphics.Shader.Translation
bool intCoords, bool intCoords,
bool write, bool write,
bool accurateType, bool accurateType,
bool coherent) bool coherent,
bool separate)
{ {
var dimensions = type.GetDimensions(); var dimensions = type == SamplerType.None ? 0 : type.GetDimensions();
var dict = isImage ? _usedImages : _usedTextures; var dict = isImage ? _usedImages : _usedTextures;
var usageFlags = TextureUsageFlags.None; var usageFlags = TextureUsageFlags.None;
@@ -290,7 +303,7 @@ namespace Ryujinx.Graphics.Shader.Translation
// For array textures, we also want to use type as key, // For array textures, we also want to use type as key,
// since we may have texture handles stores in the same buffer, but for textures with different types. // since we may have texture handles stores in the same buffer, but for textures with different types.
var keyType = arrayLength > 1 ? type : SamplerType.None; var keyType = arrayLength > 1 ? type : SamplerType.None;
var info = new TextureInfo(cbufSlot, handle, arrayLength, keyType, format); var info = new TextureInfo(cbufSlot, handle, arrayLength, separate, keyType, format);
var meta = new TextureMeta() var meta = new TextureMeta()
{ {
AccurateType = accurateType, AccurateType = accurateType,
@@ -332,6 +345,10 @@ namespace Ryujinx.Graphics.Shader.Translation
? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}" ? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}"
: $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}"; : $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}";
} }
else if (type == SamplerType.None)
{
nameSuffix = cbufSlot < 0 ? $"s_tcb_{handle:X}" : $"s_cb{cbufSlot}_{handle:X}";
}
else else
{ {
nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}"; nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}";
@@ -341,6 +358,7 @@ namespace Ryujinx.Graphics.Shader.Translation
isImage ? 3 : 2, isImage ? 3 : 2,
binding, binding,
arrayLength, arrayLength,
separate,
$"{_stagePrefix}_{nameSuffix}", $"{_stagePrefix}_{nameSuffix}",
meta.Type, meta.Type,
info.Format, info.Format,
@@ -495,6 +513,7 @@ namespace Ryujinx.Graphics.Shader.Translation
info.CbufSlot, info.CbufSlot,
info.Handle, info.Handle,
info.ArrayLength, info.ArrayLength,
info.Separate,
meta.UsageFlags)); meta.UsageFlags));
} }
@@ -514,6 +533,7 @@ namespace Ryujinx.Graphics.Shader.Translation
info.CbufSlot, info.CbufSlot,
info.Handle, info.Handle,
info.ArrayLength, info.ArrayLength,
info.Separate,
meta.UsageFlags)); meta.UsageFlags));
} }
} }

View File

@@ -45,6 +45,8 @@ namespace Ryujinx.Graphics.Shader.Translation
public bool YNegateEnabled => _graphicsState.YNegateEnabled; public bool YNegateEnabled => _graphicsState.YNegateEnabled;
public bool OriginUpperLeft => _graphicsState.OriginUpperLeft; public bool OriginUpperLeft => _graphicsState.OriginUpperLeft;
public bool HalvePrimitiveId => _graphicsState.HalvePrimitiveId;
public ImapPixelType[] ImapTypes { get; } public ImapPixelType[] ImapTypes { get; }
public bool IaIndexing { get; private set; } public bool IaIndexing { get; private set; }
public bool OaIndexing { get; private set; } public bool OaIndexing { get; private set; }

View File

@@ -413,7 +413,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Stage == ShaderStage.Vertex) if (Stage == ShaderStage.Vertex)
{ {
int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding; int ibBinding = resourceManager.Reservations.IndexBufferTextureBinding;
TextureDefinition indexBuffer = new(2, ibBinding, 1, "ib_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); TextureDefinition indexBuffer = new(2, ibBinding, "ib_data", SamplerType.TextureBuffer);
resourceManager.Properties.AddOrUpdateTexture(indexBuffer); resourceManager.Properties.AddOrUpdateTexture(indexBuffer);
int inputMap = _program.AttributeUsage.UsedInputAttributes; int inputMap = _program.AttributeUsage.UsedInputAttributes;
@@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
int location = BitOperations.TrailingZeroCount(inputMap); int location = BitOperations.TrailingZeroCount(inputMap);
int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location); int binding = resourceManager.Reservations.GetVertexBufferTextureBinding(location);
TextureDefinition vaBuffer = new(2, binding, 1, $"vb_data{location}", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); TextureDefinition vaBuffer = new(2, binding, $"vb_data{location}", SamplerType.TextureBuffer);
resourceManager.Properties.AddOrUpdateTexture(vaBuffer); resourceManager.Properties.AddOrUpdateTexture(vaBuffer);
inputMap &= ~(1 << location); inputMap &= ~(1 << location);
@@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Shader.Translation
else if (Stage == ShaderStage.Geometry) else if (Stage == ShaderStage.Geometry)
{ {
int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding; int trbBinding = resourceManager.Reservations.TopologyRemapBufferTextureBinding;
TextureDefinition remapBuffer = new(2, trbBinding, 1, "trb_data", SamplerType.TextureBuffer, TextureFormat.Unknown, TextureUsageFlags.None); TextureDefinition remapBuffer = new(2, trbBinding, "trb_data", SamplerType.TextureBuffer);
resourceManager.Properties.AddOrUpdateTexture(remapBuffer); resourceManager.Properties.AddOrUpdateTexture(remapBuffer);
int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;

View File

@@ -302,12 +302,12 @@ namespace Ryujinx.Graphics.Vulkan
SubmitInfo sInfo = new() SubmitInfo sInfo = new()
{ {
SType = StructureType.SubmitInfo, SType = StructureType.SubmitInfo,
WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0, WaitSemaphoreCount = !waitSemaphores.IsEmpty ? (uint)waitSemaphores.Length : 0,
PWaitSemaphores = pWaitSemaphores, PWaitSemaphores = pWaitSemaphores,
PWaitDstStageMask = pWaitDstStageMask, PWaitDstStageMask = pWaitDstStageMask,
CommandBufferCount = 1, CommandBufferCount = 1,
PCommandBuffers = &commandBuffer, PCommandBuffers = &commandBuffer,
SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0, SignalSemaphoreCount = !signalSemaphores.IsEmpty ? (uint)signalSemaphores.Length : 0,
PSignalSemaphores = pSignalSemaphores, PSignalSemaphores = pSignalSemaphores,
}; };

View File

@@ -43,11 +43,11 @@ namespace Ryujinx.Graphics.Vulkan
int binding = segment.Binding; int binding = segment.Binding;
int count = segment.Count; int count = segment.Count;
if (setIndex == PipelineBase.UniformSetIndex) if (IsBufferType(segment.Type))
{ {
entries[seg] = new DescriptorUpdateTemplateEntry() entries[seg] = new DescriptorUpdateTemplateEntry()
{ {
DescriptorType = DescriptorType.UniformBuffer, DescriptorType = segment.Type.Convert(),
DstBinding = (uint)binding, DstBinding = (uint)binding,
DescriptorCount = (uint)count, DescriptorCount = (uint)count,
Offset = structureOffset, Offset = structureOffset,
@@ -56,39 +56,11 @@ namespace Ryujinx.Graphics.Vulkan
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count); structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
} }
else if (setIndex == PipelineBase.StorageSetIndex) else if (IsBufferTextureType(segment.Type))
{ {
entries[seg] = new DescriptorUpdateTemplateEntry() entries[seg] = new DescriptorUpdateTemplateEntry()
{ {
DescriptorType = DescriptorType.StorageBuffer, DescriptorType = segment.Type.Convert(),
DstBinding = (uint)binding,
DescriptorCount = (uint)count,
Offset = structureOffset,
Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>()
};
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
}
else if (setIndex == PipelineBase.TextureSetIndex)
{
if (segment.Type != ResourceType.BufferTexture)
{
entries[seg] = new DescriptorUpdateTemplateEntry()
{
DescriptorType = DescriptorType.CombinedImageSampler,
DstBinding = (uint)binding,
DescriptorCount = (uint)count,
Offset = structureOffset,
Stride = (nuint)Unsafe.SizeOf<DescriptorImageInfo>()
};
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count);
}
else
{
entries[seg] = new DescriptorUpdateTemplateEntry()
{
DescriptorType = DescriptorType.UniformTexelBuffer,
DstBinding = (uint)binding, DstBinding = (uint)binding,
DescriptorCount = (uint)count, DescriptorCount = (uint)count,
Offset = structureOffset, Offset = structureOffset,
@@ -97,14 +69,11 @@ namespace Ryujinx.Graphics.Vulkan
structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count); structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count);
} }
} else
else if (setIndex == PipelineBase.ImageSetIndex)
{
if (segment.Type != ResourceType.BufferImage)
{ {
entries[seg] = new DescriptorUpdateTemplateEntry() entries[seg] = new DescriptorUpdateTemplateEntry()
{ {
DescriptorType = DescriptorType.StorageImage, DescriptorType = segment.Type.Convert(),
DstBinding = (uint)binding, DstBinding = (uint)binding,
DescriptorCount = (uint)count, DescriptorCount = (uint)count,
Offset = structureOffset, Offset = structureOffset,
@@ -113,20 +82,6 @@ namespace Ryujinx.Graphics.Vulkan
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count); structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count);
} }
else
{
entries[seg] = new DescriptorUpdateTemplateEntry()
{
DescriptorType = DescriptorType.StorageTexelBuffer,
DstBinding = (uint)binding,
DescriptorCount = (uint)count,
Offset = structureOffset,
Stride = (nuint)Unsafe.SizeOf<BufferView>()
};
structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count);
}
}
} }
Size = (int)structureOffset; Size = (int)structureOffset;
@@ -237,6 +192,16 @@ namespace Ryujinx.Graphics.Vulkan
Template = result; Template = result;
} }
private static bool IsBufferType(ResourceType type)
{
return type == ResourceType.UniformBuffer || type == ResourceType.StorageBuffer;
}
private static bool IsBufferTextureType(ResourceType type)
{
return type == ResourceType.BufferTexture || type == ResourceType.BufferImage;
}
public unsafe void Dispose() public unsafe void Dispose()
{ {
_gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null); _gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null);

View File

@@ -691,6 +691,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsBgraFormat: true, supportsBgraFormat: true,
supportsR4G4Format: false, supportsR4G4Format: false,
supportsR4G4B4A4Format: supportsR4G4B4A4Format, supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(),
supportsSnormBufferTextureFormat: true, supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat, supports5BitComponentFormat: supports5BitComponentFormat,
supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit), supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit),
@@ -705,7 +706,8 @@ namespace Ryujinx.Graphics.Vulkan
supportsMismatchingViewFormat: true, supportsMismatchingViewFormat: true,
supportsCubemapView: !IsAmdGcn, supportsCubemapView: !IsAmdGcn,
supportsNonConstantTextureOffset: false, supportsNonConstantTextureOffset: false,
supportsScaledVertexFormats: FormatCapabilities.SupportsScaledVertexFormats(), supportsQuads: false,
supportsSeparateSampler: true,
supportsShaderBallot: false, supportsShaderBallot: false,
supportsShaderBarrierDivergence: Vendor != Vendor.Intel, supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
supportsShaderFloat64: Capabilities.SupportsShaderFloat64, supportsShaderFloat64: Capabilities.SupportsShaderFloat64,

View File

@@ -75,7 +75,7 @@ namespace Ryujinx
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
{ {
MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning); MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning);
} }
// Parse arguments // Parse arguments

View File

@@ -63,7 +63,7 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'"> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
<Content Include="..\..\distribution\linux\Ryujinx.sh"> <Content Include="..\..\distribution\linux\Ryujinx.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>

View File

@@ -0,0 +1,158 @@
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Input;
using System;
using System.Collections.Generic;
using Key = Ryujinx.Common.Configuration.Hid.Key;
using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
namespace Ryujinx.UI.Helper
{
public static class ButtonHelper
{
private static readonly Dictionary<Key, string> _keysMap = new()
{
{ Key.Unknown, "Unknown" },
{ Key.ShiftLeft, "ShiftLeft" },
{ Key.ShiftRight, "ShiftRight" },
{ Key.ControlLeft, "CtrlLeft" },
{ Key.ControlRight, "CtrlRight" },
{ Key.AltLeft, OperatingSystem.IsMacOS() ? "OptLeft" : "AltLeft" },
{ Key.AltRight, OperatingSystem.IsMacOS() ? "OptRight" : "AltRight" },
{ Key.WinLeft, OperatingSystem.IsMacOS() ? "CmdLeft" : "WinLeft" },
{ Key.WinRight, OperatingSystem.IsMacOS() ? "CmdRight" : "WinRight" },
{ Key.Up, "Up" },
{ Key.Down, "Down" },
{ Key.Left, "Left" },
{ Key.Right, "Right" },
{ Key.Enter, "Enter" },
{ Key.Escape, "Escape" },
{ Key.Space, "Space" },
{ Key.Tab, "Tab" },
{ Key.BackSpace, "Backspace" },
{ Key.Insert, "Insert" },
{ Key.Delete, "Delete" },
{ Key.PageUp, "PageUp" },
{ Key.PageDown, "PageDown" },
{ Key.Home, "Home" },
{ Key.End, "End" },
{ Key.CapsLock, "CapsLock" },
{ Key.ScrollLock, "ScrollLock" },
{ Key.PrintScreen, "PrintScreen" },
{ Key.Pause, "Pause" },
{ Key.NumLock, "NumLock" },
{ Key.Clear, "Clear" },
{ Key.Keypad0, "Keypad0" },
{ Key.Keypad1, "Keypad1" },
{ Key.Keypad2, "Keypad2" },
{ Key.Keypad3, "Keypad3" },
{ Key.Keypad4, "Keypad4" },
{ Key.Keypad5, "Keypad5" },
{ Key.Keypad6, "Keypad6" },
{ Key.Keypad7, "Keypad7" },
{ Key.Keypad8, "Keypad8" },
{ Key.Keypad9, "Keypad9" },
{ Key.KeypadDivide, "KeypadDivide" },
{ Key.KeypadMultiply, "KeypadMultiply" },
{ Key.KeypadSubtract, "KeypadSubtract" },
{ Key.KeypadAdd, "KeypadAdd" },
{ Key.KeypadDecimal, "KeypadDecimal" },
{ Key.KeypadEnter, "KeypadEnter" },
{ Key.Number0, "0" },
{ Key.Number1, "1" },
{ Key.Number2, "2" },
{ Key.Number3, "3" },
{ Key.Number4, "4" },
{ Key.Number5, "5" },
{ Key.Number6, "6" },
{ Key.Number7, "7" },
{ Key.Number8, "8" },
{ Key.Number9, "9" },
{ Key.Tilde, "~" },
{ Key.Grave, "`" },
{ Key.Minus, "-" },
{ Key.Plus, "+" },
{ Key.BracketLeft, "[" },
{ Key.BracketRight, "]" },
{ Key.Semicolon, ";" },
{ Key.Quote, "'" },
{ Key.Comma, "," },
{ Key.Period, "." },
{ Key.Slash, "/" },
{ Key.BackSlash, "\\" },
{ Key.Unbound, "Unbound" },
};
private static readonly Dictionary<GamepadInputId, string> _gamepadInputIdMap = new()
{
{ GamepadInputId.LeftStick, "LeftStick" },
{ GamepadInputId.RightStick, "RightStick" },
{ GamepadInputId.LeftShoulder, "LeftShoulder" },
{ GamepadInputId.RightShoulder, "RightShoulder" },
{ GamepadInputId.LeftTrigger, "LeftTrigger" },
{ GamepadInputId.RightTrigger, "RightTrigger" },
{ GamepadInputId.DpadUp, "DpadUp" },
{ GamepadInputId.DpadDown, "DpadDown" },
{ GamepadInputId.DpadLeft, "DpadLeft" },
{ GamepadInputId.DpadRight, "DpadRight" },
{ GamepadInputId.Minus, "Minus" },
{ GamepadInputId.Plus, "Plus" },
{ GamepadInputId.Guide, "Guide" },
{ GamepadInputId.Misc1, "Misc1" },
{ GamepadInputId.Paddle1, "Paddle1" },
{ GamepadInputId.Paddle2, "Paddle2" },
{ GamepadInputId.Paddle3, "Paddle3" },
{ GamepadInputId.Paddle4, "Paddle4" },
{ GamepadInputId.Touchpad, "Touchpad" },
{ GamepadInputId.SingleLeftTrigger0, "SingleLeftTrigger0" },
{ GamepadInputId.SingleRightTrigger0, "SingleRightTrigger0" },
{ GamepadInputId.SingleLeftTrigger1, "SingleLeftTrigger1" },
{ GamepadInputId.SingleRightTrigger1, "SingleRightTrigger1" },
{ GamepadInputId.Unbound, "Unbound" },
};
private static readonly Dictionary<StickInputId, string> _stickInputIdMap = new()
{
{ StickInputId.Left, "StickLeft" },
{ StickInputId.Right, "StickRight" },
{ StickInputId.Unbound, "Unbound" },
};
public static string ToString(Button button)
{
string keyString = "";
switch (button.Type)
{
case ButtonType.Key:
var key = button.AsHidType<Key>();
if (!_keysMap.TryGetValue(button.AsHidType<Key>(), out keyString))
{
keyString = key.ToString();
}
break;
case ButtonType.GamepadButtonInputId:
var gamepadButton = button.AsHidType<GamepadInputId>();
if (!_gamepadInputIdMap.TryGetValue(button.AsHidType<GamepadInputId>(), out keyString))
{
keyString = gamepadButton.ToString();
}
break;
case ButtonType.StickId:
var stickInput = button.AsHidType<StickInputId>();
if (!_stickInputIdMap.TryGetValue(button.AsHidType<StickInputId>(), out keyString))
{
keyString = stickInput.ToString();
}
break;
}
return keyString;
}
}
}

View File

@@ -10,6 +10,7 @@ using Ryujinx.Input;
using Ryujinx.Input.Assigner; using Ryujinx.Input.Assigner;
using Ryujinx.Input.GTK3; using Ryujinx.Input.GTK3;
using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Helper;
using Ryujinx.UI.Widgets; using Ryujinx.UI.Widgets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -17,6 +18,7 @@ using System.IO;
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using Button = Ryujinx.Input.Button;
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;
@@ -887,13 +889,13 @@ namespace Ryujinx.UI.Windows
Thread.Sleep(10); Thread.Sleep(10);
assigner.ReadInput(); assigner.ReadInput();
if (_mousePressed || keyboard.IsPressed(Ryujinx.Input.Key.Escape) || assigner.HasAnyButtonPressed() || assigner.ShouldCancel()) if (_mousePressed || keyboard.IsPressed(Ryujinx.Input.Key.Escape) || assigner.IsAnyButtonPressed() || assigner.ShouldCancel())
{ {
break; break;
} }
} }
string pressedButton = assigner.GetPressedButton(); string pressedButton = ButtonHelper.ToString(assigner.GetPressedButton() ?? new Button(Input.Key.Unknown));
Application.Invoke(delegate Application.Invoke(delegate
{ {

View File

@@ -570,7 +570,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
} }
else else
{ {
serverProcess.CpuMemory.Write(copyDst, clientProcess.CpuMemory.GetSpan(copySrc, (int)copySize)); serverProcess.CpuMemory.Write(copyDst, clientProcess.CpuMemory.GetReadOnlySequence(copySrc, (int)copySize));
} }
if (clientResult != Result.Success) if (clientResult != Result.Success)
@@ -858,7 +858,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
} }
else else
{ {
clientProcess.CpuMemory.Write(copyDst, serverProcess.CpuMemory.GetSpan(copySrc, (int)copySize)); clientProcess.CpuMemory.Write(copyDst, serverProcess.CpuMemory.GetReadOnlySequence(copySrc, (int)copySize));
} }
} }

View File

@@ -2,6 +2,7 @@ using Ryujinx.Horizon.Common;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -34,6 +35,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
} }
} }
/// <inheritdoc/>
protected override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size)
{
return _cpuMemory.GetReadOnlySequence(va, size);
}
/// <inheritdoc/> /// <inheritdoc/>
protected override ReadOnlySpan<byte> GetSpan(ulong va, int size) protected override ReadOnlySpan<byte> GetSpan(ulong va, int size)
{ {
@@ -247,6 +254,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_cpuMemory.SignalMemoryTracking(va, size, write); _cpuMemory.SignalMemoryTracking(va, size, write);
} }
/// <inheritdoc/>
protected override void Write(ulong va, ReadOnlySequence<byte> data)
{
_cpuMemory.Write(va, data);
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void Write(ulong va, ReadOnlySpan<byte> data) protected override void Write(ulong va, ReadOnlySpan<byte> data)
{ {

View File

@@ -5,6 +5,7 @@ using Ryujinx.Horizon.Common;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -1568,7 +1569,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
while (size > 0) while (size > 0)
{ {
ulong copySize = 0x100000; // Copy chunck size. Any value will do, moderate sizes are recommended. ulong copySize = int.MaxValue;
if (copySize > size) if (copySize > size)
{ {
@@ -1577,11 +1578,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (toServer) if (toServer)
{ {
currentProcess.CpuMemory.Write(serverAddress, GetSpan(clientAddress, (int)copySize)); currentProcess.CpuMemory.Write(serverAddress, GetReadOnlySequence(clientAddress, (int)copySize));
} }
else else
{ {
Write(clientAddress, currentProcess.CpuMemory.GetSpan(serverAddress, (int)copySize)); Write(clientAddress, currentProcess.CpuMemory.GetReadOnlySequence(serverAddress, (int)copySize));
} }
serverAddress += copySize; serverAddress += copySize;
@@ -1911,9 +1912,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
Context.Memory.Fill(GetDramAddressFromPa(dstFirstPagePa), unusedSizeBefore, (byte)_ipcFillValue); Context.Memory.Fill(GetDramAddressFromPa(dstFirstPagePa), unusedSizeBefore, (byte)_ipcFillValue);
ulong copySize = addressRounded <= endAddr ? addressRounded - address : size; ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
var data = srcPageTable.GetSpan(addressTruncated + unusedSizeBefore, (int)copySize); var data = srcPageTable.GetReadOnlySequence(addressTruncated + unusedSizeBefore, (int)copySize);
Context.Memory.Write(GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore), data); ((IWritableBlock)Context.Memory).Write(GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore), data);
firstPageFillAddress += unusedSizeBefore + copySize; firstPageFillAddress += unusedSizeBefore + copySize;
@@ -1977,9 +1978,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if (send) if (send)
{ {
ulong copySize = endAddr - endAddrTruncated; ulong copySize = endAddr - endAddrTruncated;
var data = srcPageTable.GetSpan(endAddrTruncated, (int)copySize); var data = srcPageTable.GetReadOnlySequence(endAddrTruncated, (int)copySize);
Context.Memory.Write(GetDramAddressFromPa(dstLastPagePa), data); ((IWritableBlock)Context.Memory).Write(GetDramAddressFromPa(dstLastPagePa), data);
lastPageFillAddr += copySize; lastPageFillAddr += copySize;
@@ -2943,6 +2944,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
/// <param name="pageList">Page list where the ranges will be added</param> /// <param name="pageList">Page list where the ranges will be added</param>
protected abstract void GetPhysicalRegions(ulong va, ulong size, KPageList pageList); protected abstract void GetPhysicalRegions(ulong va, ulong size, KPageList pageList);
/// <summary>
/// Gets a read-only sequence of data from CPU mapped memory.
/// </summary>
/// <remarks>
/// Allows reading non-contiguous memory without first copying it to a newly allocated single contiguous block.
/// </remarks>
/// <param name="va">Virtual address of the data</param>
/// <param name="size">Size of the data</param>
/// <returns>A read-only sequence of the data</returns>
/// <exception cref="Ryujinx.Memory.InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
protected abstract ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size);
/// <summary> /// <summary>
/// Gets a read-only span of data from CPU mapped memory. /// Gets a read-only span of data from CPU mapped memory.
/// </summary> /// </summary>
@@ -2952,7 +2965,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
/// </remarks> /// </remarks>
/// <param name="va">Virtual address of the data</param> /// <param name="va">Virtual address of the data</param>
/// <param name="size">Size of the data</param> /// <param name="size">Size of the data</param>
/// <param name="tracked">True if read tracking is triggered on the span</param>
/// <returns>A read-only span of the data</returns> /// <returns>A read-only span of the data</returns>
/// <exception cref="Ryujinx.Memory.InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception> /// <exception cref="Ryujinx.Memory.InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
protected abstract ReadOnlySpan<byte> GetSpan(ulong va, int size); protected abstract ReadOnlySpan<byte> GetSpan(ulong va, int size);
@@ -3060,6 +3072,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
protected abstract void SignalMemoryTracking(ulong va, ulong size, bool write); protected abstract void SignalMemoryTracking(ulong va, ulong size, bool write);
/// <summary>
/// Writes data to CPU mapped memory, with write tracking.
/// </summary>
/// <param name="va">Virtual address to write the data into</param>
/// <param name="data">Data to be written</param>
/// <exception cref="Ryujinx.Memory.InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
protected abstract void Write(ulong va, ReadOnlySequence<byte> data);
/// <summary> /// <summary>
/// Writes data to CPU mapped memory, with write tracking. /// Writes data to CPU mapped memory, with write tracking.
/// </summary> /// </summary>

View File

@@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal
errorReport.AppendLine($"\tResultCode: {((int)resultCode & 0x1FF) + 2000}-{((int)resultCode >> 9) & 0x3FFF:d4}"); errorReport.AppendLine($"\tResultCode: {((int)resultCode & 0x1FF) + 2000}-{((int)resultCode >> 9) & 0x3FFF:d4}");
errorReport.AppendLine($"\tFatalPolicy: {fatalPolicy}"); errorReport.AppendLine($"\tFatalPolicy: {fatalPolicy}");
if (cpuContext != null) if (!cpuContext.IsEmpty)
{ {
errorReport.AppendLine("CPU Context:"); errorReport.AppendLine("CPU Context:");

View File

@@ -23,8 +23,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
newState.Buttons = (MouseButton)buttons; newState.Buttons = (MouseButton)buttons;
newState.X = mouseX; newState.X = mouseX;
newState.Y = mouseY; newState.Y = mouseY;
newState.DeltaX = mouseX - previousEntry.DeltaX; newState.DeltaX = mouseX - previousEntry.X;
newState.DeltaY = mouseY - previousEntry.DeltaY; newState.DeltaY = mouseY - previousEntry.Y;
newState.WheelDeltaX = scrollX; newState.WheelDeltaX = scrollX;
newState.WheelDeltaY = scrollY; newState.WheelDeltaY = scrollY;
newState.Attributes = connected ? MouseAttribute.IsConnected : MouseAttribute.None; newState.Attributes = connected ? MouseAttribute.IsConnected : MouseAttribute.None;

View File

@@ -22,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private bool _sixAxisSensorFusionEnabled; private bool _sixAxisSensorFusionEnabled;
private bool _unintendedHomeButtonInputProtectionEnabled; private bool _unintendedHomeButtonInputProtectionEnabled;
private bool _npadAnalogStickCenterClampEnabled;
private bool _vibrationPermitted; private bool _vibrationPermitted;
private bool _usbFullKeyControllerEnabled; private bool _usbFullKeyControllerEnabled;
private readonly bool _isFirmwareUpdateAvailableForSixAxisSensor; private readonly bool _isFirmwareUpdateAvailableForSixAxisSensor;
@@ -1107,6 +1108,19 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// If not, it returns nothing. // If not, it returns nothing.
} }
[CommandCmif(134)] // 6.1.0+
// SetNpadUseAnalogStickUseCenterClamp(bool Enable, nn::applet::AppletResourceUserId)
public ResultCode SetNpadUseAnalogStickUseCenterClamp(ServiceCtx context)
{
ulong pid = context.RequestData.ReadUInt64();
_npadAnalogStickCenterClampEnabled = context.RequestData.ReadUInt32() != 0;
long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { pid, appletResourceUserId, _npadAnalogStickCenterClampEnabled });
return ResultCode.Success;
}
[CommandCmif(200)] [CommandCmif(200)]
// GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo // GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo
public ResultCode GetVibrationDeviceInfo(ServiceCtx context) public ResultCode GetVibrationDeviceInfo(ServiceCtx context)

View File

@@ -13,10 +13,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
ResultCode OnTransact(uint code, uint flags, ReadOnlySpan<byte> inputParcel, Span<byte> outputParcel) ResultCode OnTransact(uint code, uint flags, ReadOnlySpan<byte> inputParcel, Span<byte> outputParcel)
{ {
Parcel inputParcelReader = new(inputParcel.ToArray()); using Parcel inputParcelReader = new(inputParcel);
// TODO: support objects? // TODO: support objects?
Parcel outputParcelWriter = new((uint)(outputParcel.Length - Unsafe.SizeOf<ParcelHeader>()), 0); using Parcel outputParcelWriter = new((uint)(outputParcel.Length - Unsafe.SizeOf<ParcelHeader>()), 0);
string inputInterfaceToken = inputParcelReader.ReadInterfaceToken(); string inputInterfaceToken = inputParcelReader.ReadInterfaceToken();

View File

@@ -1,7 +1,9 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
using System; using System;
using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -9,13 +11,13 @@ using System.Text;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{ {
class Parcel sealed class Parcel : IDisposable
{ {
private readonly byte[] _rawData; private readonly IMemoryOwner<byte> _rawDataOwner;
private Span<byte> Raw => new(_rawData); private Span<byte> Raw => _rawDataOwner.Memory.Span;
private ref ParcelHeader Header => ref MemoryMarshal.Cast<byte, ParcelHeader>(_rawData)[0]; private ref ParcelHeader Header => ref MemoryMarshal.Cast<byte, ParcelHeader>(Raw)[0];
private Span<byte> Payload => Raw.Slice((int)Header.PayloadOffset, (int)Header.PayloadSize); private Span<byte> Payload => Raw.Slice((int)Header.PayloadOffset, (int)Header.PayloadSize);
@@ -24,9 +26,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private int _payloadPosition; private int _payloadPosition;
private int _objectPosition; private int _objectPosition;
public Parcel(byte[] rawData) private bool _isDisposed;
public Parcel(ReadOnlySpan<byte> data)
{ {
_rawData = rawData; _rawDataOwner = ByteMemoryPool.RentCopy(data);
_payloadPosition = 0; _payloadPosition = 0;
_objectPosition = 0; _objectPosition = 0;
@@ -36,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{ {
uint headerSize = (uint)Unsafe.SizeOf<ParcelHeader>(); uint headerSize = (uint)Unsafe.SizeOf<ParcelHeader>();
_rawData = new byte[BitUtils.AlignUp<uint>(headerSize + payloadSize + objectsSize, 4)]; _rawDataOwner = ByteMemoryPool.RentCleared(BitUtils.AlignUp<uint>(headerSize + payloadSize + objectsSize, 4));
Header.PayloadSize = payloadSize; Header.PayloadSize = payloadSize;
Header.ObjectsSize = objectsSize; Header.ObjectsSize = objectsSize;
@@ -132,7 +136,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
// TODO: figure out what this value is // TODO: figure out what this value is
WriteInplaceObject(new byte[4] { 0, 0, 0, 0 }); Span<byte> fourBytes = stackalloc byte[4];
WriteInplaceObject(fourBytes);
} }
public AndroidStrongPointer<T> ReadStrongPointer<T>() where T : unmanaged, IFlattenable public AndroidStrongPointer<T> ReadStrongPointer<T>() where T : unmanaged, IFlattenable
@@ -219,5 +225,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
return Raw[..(int)(Header.PayloadSize + Header.ObjectsSize + Unsafe.SizeOf<ParcelHeader>())]; return Raw[..(int)(Header.PayloadSize + Header.ObjectsSize + Unsafe.SizeOf<ParcelHeader>())];
} }
public void Dispose()
{
if (!_isDisposed)
{
_isDisposed = true;
_rawDataOwner.Dispose();
}
}
} }
} }

View File

@@ -250,7 +250,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
Parcel parcel = new(0x28, 0x4); using Parcel parcel = new(0x28, 0x4);
parcel.WriteObject(producer, "dispdrv\0"); parcel.WriteObject(producer, "dispdrv\0");
@@ -288,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
Parcel parcel = new(0x28, 0x4); using Parcel parcel = new(0x28, 0x4);
parcel.WriteObject(producer, "dispdrv\0"); parcel.WriteObject(producer, "dispdrv\0");

View File

@@ -143,7 +143,7 @@ namespace Ryujinx.HLE.HOS
try try
{ {
ControllerKeys pressedKeys = (ControllerKeys)Thread.VolatileRead(ref _pressedKeys); ControllerKeys pressedKeys = (ControllerKeys)Volatile.Read(ref _pressedKeys);
program.Process.TamperedCodeMemory = false; program.Process.TamperedCodeMemory = false;
program.Execute(pressedKeys); program.Execute(pressedKeys);
@@ -175,14 +175,14 @@ namespace Ryujinx.HLE.HOS
{ {
if (input.PlayerId == PlayerIndex.Player1 || input.PlayerId == PlayerIndex.Handheld) if (input.PlayerId == PlayerIndex.Player1 || input.PlayerId == PlayerIndex.Handheld)
{ {
Thread.VolatileWrite(ref _pressedKeys, (long)input.Buttons); Volatile.Write(ref _pressedKeys, (long)input.Buttons);
return; return;
} }
} }
// Clear the input because player one is not conected. // Clear the input because player one is not conected.
Thread.VolatileWrite(ref _pressedKeys, 0); Volatile.Write(ref _pressedKeys, 0);
} }
} }
} }

View File

@@ -48,7 +48,7 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'"> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64' OR ('$(RuntimeIdentifier)' == '' AND $([MSBuild]::IsOSPlatform('Linux')))">
<Content Include="..\..\distribution\linux\Ryujinx.sh"> <Content Include="..\..\distribution\linux\Ryujinx.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>

View File

@@ -49,9 +49,9 @@ namespace Ryujinx.Input.Assigner
CollectButtonStats(); CollectButtonStats();
} }
public bool HasAnyButtonPressed() public bool IsAnyButtonPressed()
{ {
return _detector.HasAnyButtonPressed(); return _detector.IsAnyButtonPressed();
} }
public bool ShouldCancel() public bool ShouldCancel()
@@ -59,16 +59,11 @@ namespace Ryujinx.Input.Assigner
return _gamepad == null || !_gamepad.IsConnected; return _gamepad == null || !_gamepad.IsConnected;
} }
public string GetPressedButton() public Button? GetPressedButton()
{ {
IEnumerable<GamepadButtonInputId> pressedButtons = _detector.GetPressedButtons(); IEnumerable<GamepadButtonInputId> pressedButtons = _detector.GetPressedButtons();
if (pressedButtons.Any()) return !_forStick ? new(pressedButtons.FirstOrDefault()) : new((StickInputId)pressedButtons.FirstOrDefault());
{
return !_forStick ? pressedButtons.First().ToString() : ((StickInputId)pressedButtons.First()).ToString();
}
return "";
} }
private void CollectButtonStats() private void CollectButtonStats()
@@ -123,7 +118,7 @@ namespace Ryujinx.Input.Assigner
_stats = new Dictionary<GamepadButtonInputId, InputSummary>(); _stats = new Dictionary<GamepadButtonInputId, InputSummary>();
} }
public bool HasAnyButtonPressed() public bool IsAnyButtonPressed()
{ {
return _stats.Values.Any(CheckButtonPressed); return _stats.Values.Any(CheckButtonPressed);
} }

View File

@@ -19,7 +19,7 @@ namespace Ryujinx.Input.Assigner
/// Check if a button was pressed. /// Check if a button was pressed.
/// </summary> /// </summary>
/// <returns>True if a button was pressed</returns> /// <returns>True if a button was pressed</returns>
bool HasAnyButtonPressed(); bool IsAnyButtonPressed();
/// <summary> /// <summary>
/// Indicate if the user of this API should cancel operations. This is triggered for example when a gamepad get disconnected or when a user cancel assignation operations. /// Indicate if the user of this API should cancel operations. This is triggered for example when a gamepad get disconnected or when a user cancel assignation operations.
@@ -31,6 +31,6 @@ namespace Ryujinx.Input.Assigner
/// Get the pressed button that was read in <see cref="ReadInput"/> by the button assigner. /// Get the pressed button that was read in <see cref="ReadInput"/> by the button assigner.
/// </summary> /// </summary>
/// <returns>The pressed button that was read</returns> /// <returns>The pressed button that was read</returns>
string GetPressedButton(); Button? GetPressedButton();
} }
} }

View File

@@ -21,9 +21,9 @@ namespace Ryujinx.Input.Assigner
_keyboardState = _keyboard.GetKeyboardStateSnapshot(); _keyboardState = _keyboard.GetKeyboardStateSnapshot();
} }
public bool HasAnyButtonPressed() public bool IsAnyButtonPressed()
{ {
return GetPressedButton().Length != 0; return GetPressedButton() is not null;
} }
public bool ShouldCancel() public bool ShouldCancel()
@@ -31,20 +31,20 @@ namespace Ryujinx.Input.Assigner
return _keyboardState.IsPressed(Key.Escape); return _keyboardState.IsPressed(Key.Escape);
} }
public string GetPressedButton() public Button? GetPressedButton()
{ {
string keyPressed = ""; Button? keyPressed = null;
for (Key key = Key.Unknown; key < Key.Count; key++) for (Key key = Key.Unknown; key < Key.Count; key++)
{ {
if (_keyboardState.IsPressed(key)) if (_keyboardState.IsPressed(key))
{ {
keyPressed = key.ToString(); keyPressed = new(key);
break; break;
} }
} }
return !ShouldCancel() ? keyPressed : ""; return !ShouldCancel() ? keyPressed : null;
} }
} }
} }

View File

@@ -0,0 +1,33 @@
using System;
namespace Ryujinx.Input
{
public readonly struct Button
{
public readonly ButtonType Type;
private readonly uint _rawValue;
public Button(Key key)
{
Type = ButtonType.Key;
_rawValue = (uint)key;
}
public Button(GamepadButtonInputId gamepad)
{
Type = ButtonType.GamepadButtonInputId;
_rawValue = (uint)gamepad;
}
public Button(StickInputId stick)
{
Type = ButtonType.StickId;
_rawValue = (uint)stick;
}
public T AsHidType<T>() where T : Enum
{
return (T)Enum.ToObject(typeof(T), _rawValue);
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Ryujinx.Input
{
public enum ButtonType
{
Key,
GamepadButtonInputId,
StickId,
}
}

View File

@@ -203,8 +203,6 @@ namespace Ryujinx.Input.HLE
new(Key.NumLock, 10), new(Key.NumLock, 10),
}; };
private bool _isValid;
private MotionInput _leftMotionInput; private MotionInput _leftMotionInput;
private MotionInput _rightMotionInput; private MotionInput _rightMotionInput;
@@ -222,7 +220,6 @@ namespace Ryujinx.Input.HLE
{ {
State = default; State = default;
Id = null; Id = null;
_isValid = false;
_cemuHookClient = cemuHookClient; _cemuHookClient = cemuHookClient;
} }
@@ -234,19 +231,18 @@ namespace Ryujinx.Input.HLE
Id = config.Id; Id = config.Id;
_gamepad = GamepadDriver.GetGamepad(Id); _gamepad = GamepadDriver.GetGamepad(Id);
_isValid = _gamepad != null;
UpdateUserConfiguration(config); UpdateUserConfiguration(config);
return _isValid; return _gamepad != null;
} }
public void UpdateUserConfiguration(InputConfig config) public void UpdateUserConfiguration(InputConfig config)
{ {
if (config is StandardControllerInputConfig controllerConfig) if (config is StandardControllerInputConfig controllerConfig)
{ {
bool needsMotionInputUpdate = _config == null || (_config is StandardControllerInputConfig oldControllerConfig && bool needsMotionInputUpdate = _config is not StandardControllerInputConfig oldControllerConfig ||
(oldControllerConfig.Motion.EnableMotion != controllerConfig.Motion.EnableMotion) && ((oldControllerConfig.Motion.EnableMotion != controllerConfig.Motion.EnableMotion) &&
(oldControllerConfig.Motion.MotionBackend != controllerConfig.Motion.MotionBackend)); (oldControllerConfig.Motion.MotionBackend != controllerConfig.Motion.MotionBackend));
if (needsMotionInputUpdate) if (needsMotionInputUpdate)
@@ -262,10 +258,7 @@ namespace Ryujinx.Input.HLE
_config = config; _config = config;
if (_isValid) _gamepad?.SetConfiguration(config);
{
_gamepad.SetConfiguration(config);
}
} }
private void UpdateMotionInput(MotionConfigController motionConfig) private void UpdateMotionInput(MotionConfigController motionConfig)
@@ -282,18 +275,21 @@ namespace Ryujinx.Input.HLE
public void Update() public void Update()
{ {
if (_isValid && GamepadDriver != null) // _gamepad may be altered by other threads
var gamepad = _gamepad;
if (gamepad != null && GamepadDriver != null)
{ {
State = _gamepad.GetMappedStateSnapshot(); State = gamepad.GetMappedStateSnapshot();
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion) if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion)
{ {
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver) if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
{ {
if (_gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion)) if (gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion))
{ {
Vector3 accelerometer = _gamepad.GetMotionData(MotionInputId.Accelerometer); Vector3 accelerometer = gamepad.GetMotionData(MotionInputId.Accelerometer);
Vector3 gyroscope = _gamepad.GetMotionData(MotionInputId.Gyroscope); Vector3 gyroscope = gamepad.GetMotionData(MotionInputId.Gyroscope);
accelerometer = new Vector3(accelerometer.X, -accelerometer.Z, accelerometer.Y); accelerometer = new Vector3(accelerometer.X, -accelerometer.Z, accelerometer.Y);
gyroscope = new Vector3(gyroscope.X, -gyroscope.Z, gyroscope.Y); gyroscope = new Vector3(gyroscope.X, -gyroscope.Z, gyroscope.Y);
@@ -491,10 +487,10 @@ namespace Ryujinx.Input.HLE
return value; return value;
} }
public KeyboardInput? GetHLEKeyboardInput() public static KeyboardInput GetHLEKeyboardInput(IGamepadDriver KeyboardDriver)
{
if (_gamepad is IKeyboard keyboard)
{ {
var keyboard = KeyboardDriver.GetGamepad("0") as IKeyboard;
KeyboardStateSnapshot keyboardState = keyboard.GetKeyboardStateSnapshot(); KeyboardStateSnapshot keyboardState = keyboard.GetKeyboardStateSnapshot();
KeyboardInput hidKeyboard = new() KeyboardInput hidKeyboard = new()
@@ -518,12 +514,9 @@ namespace Ryujinx.Input.HLE
} }
return hidKeyboard; return hidKeyboard;
}
return null;
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)

View File

@@ -231,11 +231,6 @@ namespace Ryujinx.Input.HLE
var altMotionState = isJoyconPair ? controller.GetHLEMotionState(true) : default; var altMotionState = isJoyconPair ? controller.GetHLEMotionState(true) : default;
motionState = (controller.GetHLEMotionState(), altMotionState); motionState = (controller.GetHLEMotionState(), altMotionState);
if (_enableKeyboard)
{
hleKeyboardInput = controller.GetHLEKeyboardInput();
}
} }
else else
{ {
@@ -257,6 +252,11 @@ namespace Ryujinx.Input.HLE
} }
} }
if (!_blockInputUpdates && _enableKeyboard)
{
hleKeyboardInput = NpadController.GetHLEKeyboardInput(_keyboardDriver);
}
_device.Hid.Npads.Update(hleInputStates); _device.Hid.Npads.Update(hleInputStates);
_device.Hid.Npads.UpdateSixAxis(hleMotionStates); _device.Hid.Npads.UpdateSixAxis(hleMotionStates);

View File

@@ -1,9 +1,25 @@
using System; using System;
using System.Buffers;
namespace Ryujinx.Memory namespace Ryujinx.Memory
{ {
public interface IWritableBlock public interface IWritableBlock
{ {
/// <summary>
/// Writes data to CPU mapped memory, with write tracking.
/// </summary>
/// <param name="va">Virtual address to write the data into</param>
/// <param name="data">Data to be written</param>
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
void Write(ulong va, ReadOnlySequence<byte> data)
{
foreach (ReadOnlyMemory<byte> segment in data)
{
Write(va, segment.Span);
va += (ulong)segment.Length;
}
}
void Write(ulong va, ReadOnlySpan<byte> data); void Write(ulong va, ReadOnlySpan<byte> data);
void WriteUntracked(ulong va, ReadOnlySpan<byte> data) => Write(va, data); void WriteUntracked(ulong va, ReadOnlySpan<byte> data) => Write(va, data);

View File

@@ -8,6 +8,11 @@ namespace Ryujinx.Memory
private readonly T* _pointer; private readonly T* _pointer;
private readonly int _length; private readonly int _length;
public NativeMemoryManager(nuint pointer, int length)
: this((T*)pointer, length)
{
}
public NativeMemoryManager(T* pointer, int length) public NativeMemoryManager(T* pointer, int length)
{ {
_pointer = pointer; _pointer = pointer;

View File

@@ -58,10 +58,20 @@ namespace Ryujinx.ShaderTools
return MemoryMarshal.Cast<byte, ulong>(new ReadOnlySpan<byte>(_data)[(int)address..]); return MemoryMarshal.Cast<byte, ulong>(new ReadOnlySpan<byte>(_data)[(int)address..]);
} }
public int QuerySamplerArrayLengthFromPool()
{
return DefaultArrayLength;
}
public int QueryTextureArrayLengthFromBuffer(int slot) public int QueryTextureArrayLengthFromBuffer(int slot)
{ {
return DefaultArrayLength; return DefaultArrayLength;
} }
public int QueryTextureArrayLengthFromPool()
{
return DefaultArrayLength;
}
} }
private class Options private class Options

View File

@@ -388,14 +388,14 @@ namespace Ryujinx.Tests.Memory
{ {
rwLock.AcquireReaderLock(); rwLock.AcquireReaderLock();
int originalValue = Thread.VolatileRead(ref value); int originalValue = Volatile.Read(ref value);
count++; count++;
// Spin a bit. // Spin a bit.
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ {
if (Thread.VolatileRead(ref readersAllowed) == 0) if (Volatile.Read(ref readersAllowed) == 0)
{ {
error = true; error = true;
running = false; running = false;
@@ -403,7 +403,7 @@ namespace Ryujinx.Tests.Memory
} }
// Should not change while the lock is held. // Should not change while the lock is held.
if (Thread.VolatileRead(ref value) != originalValue) if (Volatile.Read(ref value) != originalValue)
{ {
error = true; error = true;
running = false; running = false;

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.UI.Common.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 50; public const int CurrentVersion = 51;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@@ -162,6 +162,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary> /// </summary>
public bool ShowConfirmExit { get; set; } public bool ShowConfirmExit { get; set; }
/// <summary>
/// Enables or disables save window size, position and state on close.
/// </summary>
public bool RememberWindowState { get; set; }
/// <summary> /// <summary>
/// Enables hardware-accelerated rendering for Avalonia /// Enables hardware-accelerated rendering for Avalonia
/// </summary> /// </summary>

View File

@@ -626,6 +626,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary> /// </summary>
public ReactiveObject<bool> ShowConfirmExit { get; private set; } public ReactiveObject<bool> ShowConfirmExit { get; private set; }
/// <summary>
/// Enables or disables save window size, position and state on close.
/// </summary>
public ReactiveObject<bool> RememberWindowState { get; private set; }
/// <summary> /// <summary>
/// Enables hardware-accelerated rendering for Avalonia /// Enables hardware-accelerated rendering for Avalonia
/// </summary> /// </summary>
@@ -647,6 +652,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableDiscordIntegration = new ReactiveObject<bool>(); EnableDiscordIntegration = new ReactiveObject<bool>();
CheckUpdatesOnStart = new ReactiveObject<bool>(); CheckUpdatesOnStart = new ReactiveObject<bool>();
ShowConfirmExit = new ReactiveObject<bool>(); ShowConfirmExit = new ReactiveObject<bool>();
RememberWindowState = new ReactiveObject<bool>();
EnableHardwareAcceleration = new ReactiveObject<bool>(); EnableHardwareAcceleration = new ReactiveObject<bool>();
HideCursor = new ReactiveObject<HideCursorMode>(); HideCursor = new ReactiveObject<HideCursorMode>();
} }
@@ -684,6 +690,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableDiscordIntegration = EnableDiscordIntegration, EnableDiscordIntegration = EnableDiscordIntegration,
CheckUpdatesOnStart = CheckUpdatesOnStart, CheckUpdatesOnStart = CheckUpdatesOnStart,
ShowConfirmExit = ShowConfirmExit, ShowConfirmExit = ShowConfirmExit,
RememberWindowState = RememberWindowState,
EnableHardwareAcceleration = EnableHardwareAcceleration, EnableHardwareAcceleration = EnableHardwareAcceleration,
HideCursor = HideCursor, HideCursor = HideCursor,
EnableVsync = Graphics.EnableVsync, EnableVsync = Graphics.EnableVsync,
@@ -792,6 +799,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableDiscordIntegration.Value = true; EnableDiscordIntegration.Value = true;
CheckUpdatesOnStart.Value = true; CheckUpdatesOnStart.Value = true;
ShowConfirmExit.Value = true; ShowConfirmExit.Value = true;
RememberWindowState.Value = true;
EnableHardwareAcceleration.Value = true; EnableHardwareAcceleration.Value = true;
HideCursor.Value = HideCursorMode.OnIdle; HideCursor.Value = HideCursorMode.OnIdle;
Graphics.EnableVsync.Value = true; Graphics.EnableVsync.Value = true;
@@ -1459,6 +1467,15 @@ namespace Ryujinx.UI.Common.Configuration
configurationFileUpdated = true; configurationFileUpdated = true;
} }
if (configurationFileFormat.Version < 51)
{
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 51.");
configurationFileFormat.RememberWindowState = true;
configurationFileUpdated = true;
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
@@ -1489,6 +1506,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit; ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit;
RememberWindowState.Value = configurationFileFormat.RememberWindowState;
EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration; EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration;
HideCursor.Value = configurationFileFormat.HideCursor; HideCursor.Value = configurationFileFormat.HideCursor;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;

View File

@@ -1,6 +1,7 @@
using DiscordRPC; using DiscordRPC;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using System.Text;
namespace Ryujinx.UI.Common namespace Ryujinx.UI.Common
{ {
@@ -9,6 +10,9 @@ namespace Ryujinx.UI.Common
private const string Description = "A simple, experimental Nintendo Switch emulator."; private const string Description = "A simple, experimental Nintendo Switch emulator.";
private const string ApplicationId = "1216775165866807456"; private const string ApplicationId = "1216775165866807456";
private const int ApplicationByteLimit = 128;
private const string Ellipsis = "…";
private static DiscordRpcClient _discordClient; private static DiscordRpcClient _discordClient;
private static RichPresence _discordPresenceMain; private static RichPresence _discordPresenceMain;
@@ -60,18 +64,18 @@ namespace Ryujinx.UI.Common
} }
} }
public static void SwitchToPlayingState(string titleId, string titleName) public static void SwitchToPlayingState(string titleId, string applicationName)
{ {
_discordClient?.SetPresence(new RichPresence _discordClient?.SetPresence(new RichPresence
{ {
Assets = new Assets Assets = new Assets
{ {
LargeImageKey = "game", LargeImageKey = "game",
LargeImageText = titleName, LargeImageText = TruncateToByteLength(applicationName, ApplicationByteLimit),
SmallImageKey = "ryujinx", SmallImageKey = "ryujinx",
SmallImageText = Description, SmallImageText = Description,
}, },
Details = $"Playing {titleName}", Details = TruncateToByteLength($"Playing {applicationName}", ApplicationByteLimit),
State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(), State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(),
Timestamps = Timestamps.Now, Timestamps = Timestamps.Now,
Buttons = Buttons =
@@ -90,6 +94,28 @@ namespace Ryujinx.UI.Common
_discordClient?.SetPresence(_discordPresenceMain); _discordClient?.SetPresence(_discordPresenceMain);
} }
private static string TruncateToByteLength(string input, int byteLimit)
{
if (Encoding.UTF8.GetByteCount(input) <= byteLimit)
{
return input;
}
// Find the length to trim the string to guarantee we have space for the trailing ellipsis.
int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis);
// Basic trim to best case scenario of 1 byte characters.
input = input[..trimLimit];
while (Encoding.UTF8.GetByteCount(input) > trimLimit)
{
// Remove one character from the end of the string at a time.
input = input[..^1];
}
return input.TrimEnd() + Ellipsis;
}
public static void Exit() public static void Exit()
{ {
_discordClient?.Dispose(); _discordClient?.Dispose();

View File

@@ -1,8 +1,10 @@
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Platform;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
@@ -84,7 +86,7 @@ namespace Ryujinx.Ava
ApplyConfiguredTheme(); ApplyConfiguredTheme();
} }
private void ApplyConfiguredTheme() public void ApplyConfiguredTheme()
{ {
try try
{ {
@@ -92,13 +94,18 @@ namespace Ryujinx.Ava
if (string.IsNullOrWhiteSpace(baseStyle)) if (string.IsNullOrWhiteSpace(baseStyle))
{ {
ConfigurationState.Instance.UI.BaseStyle.Value = "Dark"; ConfigurationState.Instance.UI.BaseStyle.Value = "Auto";
baseStyle = ConfigurationState.Instance.UI.BaseStyle; baseStyle = ConfigurationState.Instance.UI.BaseStyle;
} }
ThemeVariant systemTheme = DetectSystemTheme();
ThemeManager.OnThemeChanged();
RequestedThemeVariant = baseStyle switch RequestedThemeVariant = baseStyle switch
{ {
"Auto" => systemTheme,
"Light" => ThemeVariant.Light, "Light" => ThemeVariant.Light,
"Dark" => ThemeVariant.Dark, "Dark" => ThemeVariant.Dark,
_ => ThemeVariant.Default, _ => ThemeVariant.Default,
@@ -111,5 +118,28 @@ namespace Ryujinx.Ava
ShowRestartDialog(); ShowRestartDialog();
} }
} }
/// <summary>
/// Converts a PlatformThemeVariant value to the corresponding ThemeVariant value.
/// </summary>
public static ThemeVariant ConvertThemeVariant(PlatformThemeVariant platformThemeVariant) =>
platformThemeVariant switch
{
PlatformThemeVariant.Dark => ThemeVariant.Dark,
PlatformThemeVariant.Light => ThemeVariant.Light,
_ => ThemeVariant.Default,
};
public static ThemeVariant DetectSystemTheme()
{
if (Application.Current is App app)
{
var colorValues = app.PlatformSettings.GetColorValues();
return ConvertThemeVariant(colorValues.ThemeVariant);
}
return ThemeVariant.Default;
}
} }
} }

View File

@@ -94,6 +94,17 @@ namespace Ryujinx.Ava
private long _lastCursorMoveTime; private long _lastCursorMoveTime;
private bool _isCursorInRenderer = true; private bool _isCursorInRenderer = true;
private bool _ignoreCursorState = false;
private enum CursorStates
{
CursorIsHidden,
CursorIsVisible,
ForceChangeCursor
};
private CursorStates _cursorState = !ConfigurationState.Instance.Hid.EnableMouse.Value ?
CursorStates.CursorIsVisible : CursorStates.CursorIsHidden;
private bool _isStopped; private bool _isStopped;
private bool _isActive; private bool _isActive;
@@ -201,23 +212,65 @@ namespace Ryujinx.Ava
private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e) private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e)
{ {
if (!_viewModel.IsActive)
{
_isCursorInRenderer = false;
_ignoreCursorState = false;
return;
}
if (sender is MainWindow window) if (sender is MainWindow window)
{
if (ConfigurationState.Instance.HideCursor.Value == HideCursorMode.OnIdle)
{ {
_lastCursorMoveTime = Stopwatch.GetTimestamp(); _lastCursorMoveTime = Stopwatch.GetTimestamp();
}
var point = e.GetCurrentPoint(window).Position; var point = e.GetCurrentPoint(window).Position;
var bounds = RendererHost.EmbeddedWindow.Bounds; var bounds = RendererHost.EmbeddedWindow.Bounds;
var windowYOffset = bounds.Y + window.MenuBarHeight;
var windowYLimit = (int)window.Bounds.Height - window.StatusBarHeight - 1;
if (!_viewModel.ShowMenuAndStatusBar)
{
windowYOffset -= window.MenuBarHeight;
windowYLimit += window.StatusBarHeight + 1;
}
_isCursorInRenderer = point.X >= bounds.X && _isCursorInRenderer = point.X >= bounds.X &&
point.X <= bounds.Width + bounds.X && Math.Ceiling(point.X) <= (int)window.Bounds.Width &&
point.Y >= bounds.Y && point.Y >= windowYOffset &&
point.Y <= bounds.Height + bounds.Y; point.Y <= windowYLimit &&
!_viewModel.IsSubMenuOpen;
_ignoreCursorState = false;
} }
} }
private void TopLevel_PointerExited(object sender, PointerEventArgs e) private void TopLevel_PointerExited(object sender, PointerEventArgs e)
{ {
_isCursorInRenderer = false; _isCursorInRenderer = false;
if (sender is MainWindow window)
{
var point = e.GetCurrentPoint(window).Position;
var bounds = RendererHost.EmbeddedWindow.Bounds;
var windowYOffset = bounds.Y + window.MenuBarHeight;
var windowYLimit = (int)window.Bounds.Height - window.StatusBarHeight - 1;
if (!_viewModel.ShowMenuAndStatusBar)
{
windowYOffset -= window.MenuBarHeight;
windowYLimit += window.StatusBarHeight + 1;
}
_ignoreCursorState = (point.X == bounds.X ||
Math.Ceiling(point.X) == (int)window.Bounds.Width) &&
point.Y >= windowYOffset &&
point.Y <= windowYLimit;
}
_cursorState = CursorStates.ForceChangeCursor;
} }
private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e) private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
@@ -244,10 +297,15 @@ namespace Ryujinx.Ava
_viewModel.Cursor = Cursor.Default; _viewModel.Cursor = Cursor.Default;
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{
if (_cursorState != CursorStates.CursorIsHidden && !_ignoreCursorState)
{ {
SetCursor(_defaultCursorWin); SetCursor(_defaultCursorWin);
} }
}
}); });
_cursorState = CursorStates.CursorIsVisible;
} }
private void HideCursor() private void HideCursor()
@@ -261,6 +319,8 @@ namespace Ryujinx.Ava
SetCursor(_invisibleCursorWin); SetCursor(_invisibleCursorWin);
} }
}); });
_cursorState = CursorStates.CursorIsHidden;
} }
private void SetRendererWindowSize(Size size) private void SetRendererWindowSize(Size size)
@@ -523,6 +583,8 @@ namespace Ryujinx.Ava
{ {
_lastCursorMoveTime = Stopwatch.GetTimestamp(); _lastCursorMoveTime = Stopwatch.GetTimestamp();
} }
_cursorState = CursorStates.ForceChangeCursor;
} }
public async Task<bool> LoadGuestApplication() public async Task<bool> LoadGuestApplication()
@@ -1037,39 +1099,33 @@ namespace Ryujinx.Ava
if (_viewModel.IsActive) if (_viewModel.IsActive)
{ {
if (_isCursorInRenderer) bool isCursorVisible = true;
if (_isCursorInRenderer && !_viewModel.ShowLoadProgress)
{ {
if (ConfigurationState.Instance.Hid.EnableMouse) if (ConfigurationState.Instance.Hid.EnableMouse.Value)
{ {
HideCursor(); isCursorVisible = ConfigurationState.Instance.HideCursor.Value == HideCursorMode.Never;
} }
else else
{ {
switch (ConfigurationState.Instance.HideCursor.Value) isCursorVisible = ConfigurationState.Instance.HideCursor.Value == HideCursorMode.Never ||
(ConfigurationState.Instance.HideCursor.Value == HideCursorMode.OnIdle &&
Stopwatch.GetTimestamp() - _lastCursorMoveTime < CursorHideIdleTime * Stopwatch.Frequency);
}
}
if (_cursorState != (isCursorVisible ? CursorStates.CursorIsVisible : CursorStates.CursorIsHidden))
{
if (isCursorVisible)
{ {
case HideCursorMode.Never:
ShowCursor(); ShowCursor();
break;
case HideCursorMode.OnIdle:
if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency)
{
HideCursor();
} }
else else
{ {
ShowCursor();
}
break;
case HideCursorMode.Always:
HideCursor(); HideCursor();
break;
} }
} }
}
else
{
ShowCursor();
}
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
@@ -1154,7 +1210,7 @@ namespace Ryujinx.Ava
// Touchscreen. // Touchscreen.
bool hasTouch = false; bool hasTouch = false;
if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse.Value)
{ {
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
} }

View File

@@ -1,38 +1,42 @@
{ {
"Language": "العربية", "Language": "اَلْعَرَبِيَّةُ",
"MenuBarFileOpenApplet": "فتح التطبيق المُصغَّر", "MenuBarFileOpenApplet": "فتح التطبيق المصغر",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "افتح تطبيق محرر الـMii المُصغَّر في الوضع المستقل", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "افتح تطبيق تحرير Mii في الوضع المستقل",
"SettingsTabInputDirectMouseAccess": "الوصول المباشر للفأرة", "SettingsTabInputDirectMouseAccess": "الوصول المباشر للفأرة",
"SettingsTabSystemMemoryManagerMode": "وضع إدارة الذاكرة:", "SettingsTabSystemMemoryManagerMode": "وضع إدارة الذاكرة:",
"SettingsTabSystemMemoryManagerModeSoftware": "البرنامج", "SettingsTabSystemMemoryManagerModeSoftware": "البرنامج",
"SettingsTabSystemMemoryManagerModeHost": "المُضيف (سريع)", "SettingsTabSystemMemoryManagerModeHost": "المُضيف (سريع)",
"SettingsTabSystemMemoryManagerModeHostUnchecked": "المضيف غير محدد (سريع، غير آمن)", "SettingsTabSystemMemoryManagerModeHostUnchecked": "المضيف (غير مفحوص) (أسرع، غير آمن)",
"SettingsTabSystemUseHypervisor": "استخدم الهايبرڤايزور", "SettingsTabSystemUseHypervisor": "استخدم مراقب الأجهزة الافتراضية",
"MenuBarFile": "_ملف", "MenuBarFile": "_ملف",
"MenuBarFileOpenFromFile": "_تحميل تطبيق من ملف", "MenuBarFileOpenFromFile": "_تحميل تطبيق من ملف",
"MenuBarFileOpenUnpacked": "تحميل لعبه غير محزومه", "MenuBarFileOpenUnpacked": "تحميل لُعْبَة غير محزومة",
"MenuBarFileOpenEmuFolder": "فتح مجلد Ryujinx", "MenuBarFileOpenEmuFolder": "فتح مجلد Ryujinx",
"MenuBarFileOpenLogsFolder": "فتح مجلد السجلات", "MenuBarFileOpenLogsFolder": "فتح مجلد السجلات",
"MenuBarFileExit": "_خروج", "MenuBarFileExit": "_خروج",
"MenuBarOptions": "_خيارات", "MenuBarOptions": "_خيارات",
"MenuBarOptionsToggleFullscreen": "وضع ملء الشاشة", "MenuBarOptionsToggleFullscreen": "التبديل إلى وضع ملء الشاشة",
"MenuBarOptionsStartGamesInFullscreen": "ابدأ الألعاب في وضع ملء الشاشة", "MenuBarOptionsStartGamesInFullscreen": "ابدأ الألعاب في وضع ملء الشاشة",
"MenuBarOptionsStopEmulation": "إيقاف المحاكاة", "MenuBarOptionsStopEmulation": "إيقاف المحاكاة",
"MenuBarOptionsSettings": "الإعدادات", "MenuBarOptionsSettings": "_الإعدادات",
"MenuBarOptionsManageUserProfiles": "إدارة الملفات الشخصية للمستخدم", "MenuBarOptionsManageUserProfiles": "_إدارة الملفات الشخصية للمستخدم",
"MenuBarActions": "الإجراءات", "MenuBarActions": "_الإجراءات",
"MenuBarOptionsSimulateWakeUpMessage": "محاكاة رسالة الاستيقاظ", "MenuBarOptionsSimulateWakeUpMessage": "محاكاة رسالة الاستيقاظ",
"MenuBarActionsScanAmiibo": "فحص Amiibo", "MenuBarActionsScanAmiibo": "فحص Amiibo",
"MenuBarTools": "الأدوات", "MenuBarTools": "_الأدوات",
"MenuBarToolsInstallFirmware": "تثبيت البرامج الثابتة", "MenuBarToolsInstallFirmware": "تثبيت البرنامج الثابت",
"MenuBarFileToolsInstallFirmwareFromFile": "تثبيت البرنامج الثابت من XCI أو ZIP", "MenuBarFileToolsInstallFirmwareFromFile": "تثبيت برنامج ثابت من XCI أو ZIP",
"MenuBarFileToolsInstallFirmwareFromDirectory": "تثبيت برنامج ثابت من مجلد", "MenuBarFileToolsInstallFirmwareFromDirectory": "تثبيت برنامج ثابت من مجلد",
"MenuBarToolsManageFileTypes": "إدارة أنواع الملفات", "MenuBarToolsManageFileTypes": "إدارة أنواع الملفات",
"MenuBarToolsInstallFileTypes": "تثبيت أنواع الملفات", "MenuBarToolsInstallFileTypes": "تثبيت أنواع الملفات",
"MenuBarToolsUninstallFileTypes": "إزالة أنواع الملفات", "MenuBarToolsUninstallFileTypes": "إزالة أنواع الملفات",
"MenuBarView": "_عرض",
"MenuBarViewWindow": "حجم النافذة",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_مساعدة", "MenuBarHelp": "_مساعدة",
"MenuBarHelpCheckForUpdates": "تحقق من التحديثات", "MenuBarHelpCheckForUpdates": "تحقق من التحديثات",
"MenuBarHelpAbout": "عن البرنامج", "MenuBarHelpAbout": "حول",
"MenuSearch": "بحث...", "MenuSearch": "بحث...",
"GameListHeaderFavorite": "مفضلة", "GameListHeaderFavorite": "مفضلة",
"GameListHeaderIcon": "الأيقونة", "GameListHeaderIcon": "الأيقونة",
@@ -40,62 +44,63 @@
"GameListHeaderDeveloper": "المطور", "GameListHeaderDeveloper": "المطور",
"GameListHeaderVersion": "الإصدار", "GameListHeaderVersion": "الإصدار",
"GameListHeaderTimePlayed": "وقت اللعب", "GameListHeaderTimePlayed": "وقت اللعب",
"GameListHeaderLastPlayed": "اخر تشغيل", "GameListHeaderLastPlayed": "آخر مرة لُعبت",
"GameListHeaderFileExtension": "امتداد الملف", "GameListHeaderFileExtension": "صيغة الملف",
"GameListHeaderFileSize": "حجم الملف", "GameListHeaderFileSize": "حجم الملف",
"GameListHeaderPath": "المسار", "GameListHeaderPath": "المسار",
"GameListContextMenuOpenUserSaveDirectory": "فتح مجلد حفظ المستخدم", "GameListContextMenuOpenUserSaveDirectory": "فتح مجلد حفظ المستخدم",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "يفتح المجلد الذي يحتوي على حفظ المستخدم للتطبيق", "GameListContextMenuOpenUserSaveDirectoryToolTip": "يفتح المجلد الذي يحتوي على حفظ المستخدم للتطبيق",
"GameListContextMenuOpenDeviceSaveDirectory": "فتح مجلد حفظ الجهاز", "GameListContextMenuOpenDeviceSaveDirectory": "فتح مجلد حفظ الجهاز",
"GameListContextMenuOpenDeviceSaveDirectoryToolTip": "يفتح المجلد الذي يحتوي على حفظ الجهاز للتطبيق", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "يفتح المجلد الذي يحتوي على حفظ الجهاز للتطبيق",
"GameListContextMenuOpenBcatSaveDirectory": "فتح مجلد حفظ الـBCAT", "GameListContextMenuOpenBcatSaveDirectory": "فتح مجلد حفظ الـBCAT",
"GameListContextMenuOpenBcatSaveDirectoryToolTip": "يفتح المجلد الذي يحتوي على حفظ الـBCAT للتطبيق", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "يفتح المجلد الذي يحتوي على حفظ الـBCAT للتطبيق",
"GameListContextMenuManageTitleUpdates": "إدارة تحديثات العنوان", "GameListContextMenuManageTitleUpdates": "إدارة تحديثات اللُعبة",
"GameListContextMenuManageTitleUpdatesToolTip": "يفتح نافذة إدارة تحديث العنوان", "GameListContextMenuManageTitleUpdatesToolTip": "يفتح نافذة إدارة تحديث اللُعبة",
"GameListContextMenuManageDlc": "إدارة المحتوي الإضافي", "GameListContextMenuManageDlc": "إدارة المحتوي الإضافي",
"GameListContextMenuManageDlcToolTip": "يفتح نافذة إدارة المحتوي الإضافي", "GameListContextMenuManageDlcToolTip": "يفتح نافذة إدارة المحتوي الإضافي",
"GameListContextMenuCacheManagement": "إدارة ذاكرة التخزين المؤقت", "GameListContextMenuCacheManagement": "إدارة ذاكرة التخزين المؤقت",
"GameListContextMenuCacheManagementPurgePptc": "إعادة بناء PPTC في قائمة الانتظار", "GameListContextMenuCacheManagementPurgePptc": "قائمة انتظار إعادة بناء الـPPTC",
"GameListContextMenuCacheManagementPurgePptcToolTip": "تنشيط PPTC لإعادة البناء في وقت التمهيد عند بدء تشغيل اللعبة التالية", "GameListContextMenuCacheManagementPurgePptcToolTip": "تنشيط PPTC لإعادة البناء في وقت الإقلاع عند بدء تشغيل اللعبة التالي",
"GameListContextMenuCacheManagementPurgeShaderCache": "إزالة ذاكرة التشغيل المؤقتة للمظللات ", "GameListContextMenuCacheManagementPurgeShaderCache": "تنظيف ذاكرة مرشحات الفيديو المؤقتة",
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "حذف الذاكرة المؤقتة للمظللات الخاصة بالتطبيق", "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "يحذف ذاكرة مرشحات الفيديو المؤقتة الخاصة بالتطبيق",
"GameListContextMenuCacheManagementOpenPptcDirectory": "فتح مجلد PPTC", "GameListContextMenuCacheManagementOpenPptcDirectory": "فتح مجلد PPTC",
"GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "يفتح المجلد الذي يحتوي على الـPPTC للتطبيق", "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "يفتح المجلد الذي يحتوي على ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) للتطبيق",
"GameListContextMenuCacheManagementOpenShaderCacheDirectory": "فتح مجلد الذاكرة المؤقتة للمظللات ", "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "فتح مجلد الذاكرة المؤقتة لمرشحات الفيديو ",
"GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "يفتح المجلد الذي يحتوي على ذاكرة التشغيل المؤقتة للمظللات الخاصة بالتطبيق", "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "يفتح المجلد الذي يحتوي على ذاكرة المظللات المؤقتة للتطبيق",
"GameListContextMenuExtractData": "إستخراج البيانات", "GameListContextMenuExtractData": "استخراج البيانات",
"GameListContextMenuExtractDataExeFS": "ExeFS", "GameListContextMenuExtractDataExeFS": "ExeFS",
"GameListContextMenuExtractDataExeFSToolTip": "إستخراج قسم ExeFS من التكوين الحالي للتطبيقات (بما في ذلك التحديثات)", "GameListContextMenuExtractDataExeFSToolTip": " استخراج قسم نظام الملفات القابل للتنفيذ (ExeFS) من الإعدادات الحالية للتطبيقات (يتضمن التحديثات)",
"GameListContextMenuExtractDataRomFS": "RomFS", "GameListContextMenuExtractDataRomFS": "RomFS",
"GameListContextMenuExtractDataRomFSToolTip": "استخراج قسم RomFS من التكوين الحالي للتطبيقات (بما في ذلك التحديثات)", "GameListContextMenuExtractDataRomFSToolTip": "استخراج قسم RomFS من الإعدادات الحالية للتطبيقات (يتضمن التحديثات)",
"GameListContextMenuExtractDataLogo": "شعار", "GameListContextMenuExtractDataLogo": "شعار",
"GameListContextMenuExtractDataLogoToolTip": "استخراج قسم الشعار من التكوين الحالي للتطبيقات (بما في ذلك التحديثات)", "GameListContextMenuExtractDataLogoToolTip": "استخراج قسم الشعار من الإعدادات الحالية للتطبيقات (يتضمن التحديثات)",
"GameListContextMenuCreateShortcut": "إنشاء اختصار للتطبيق", "GameListContextMenuCreateShortcut": "إنشاء اختصار للتطبيق",
"GameListContextMenuCreateShortcutToolTip": "قم بإنشاء اختصار لسطح المكتب لتشغيل التطبيق المحدد", "GameListContextMenuCreateShortcutToolTip": "أنشئ اختصار سطح مكتب لتشغيل التطبيق المحدد",
"GameListContextMenuCreateShortcutToolTipMacOS": "قم بإنشاء اختصار في مجلد تطبيقات نظام التشغيل MacOS الذي يقوم بتشغيل التطبيق المحدد", "GameListContextMenuCreateShortcutToolTipMacOS": "أنشئ اختصار يُشغل التطبيق المحدد في مجلد تطبيقات macOS",
"GameListContextMenuOpenModsDirectory": "فتح مجلد التعديلات", "GameListContextMenuOpenModsDirectory": "فتح مجلد التعديلات (Mods)",
"GameListContextMenuOpenModsDirectoryToolTip": "يفتح المجلد الذي يحتوي على تعديلات التطبيق", "GameListContextMenuOpenModsDirectoryToolTip": "يفتح المجلد الذي يحتوي على تعديلات(mods) التطبيق",
"GameListContextMenuOpenSdModsDirectory": "فتح مجلد تعديلات Atmosphere", "GameListContextMenuOpenSdModsDirectory": "فتح مجلد تعديلات(mods) أتموسفير",
"GameListContextMenuOpenSdModsDirectoryToolTip": "يفتح دليل Atmosphere لبطاقة SD البديلة الذي يحتوي على تعديلات التطبيق. مفيد للتعديلات التي تم تعبئتها للأجهزة الحقيقية.", "GameListContextMenuOpenSdModsDirectoryToolTip": "يفتح مجلد أتموسفير لبطاقة SD البديلة الذي يحتوي على تعديلات التطبيق. مفيد للتعديلات التي تم تعبئتها للأجهزة الحقيقية.",
"StatusBarGamesLoaded": "{0}/{1} الألعاب التي تم تحميلها", "StatusBarGamesLoaded": "{0}/{1} لعبة تم تحميلها",
"StatusBarSystemVersion": "إصدار النظام: {0}", "StatusBarSystemVersion": "إصدار النظام: {0}",
"LinuxVmMaxMapCountDialogTitle": "الحد الأدنى لتعيينات الذاكرة المكتشفة", "LinuxVmMaxMapCountDialogTitle": "الحد الأدنى لتعيينات الذاكرة المكتشفة",
"LinuxVmMaxMapCountDialogTextPrimary": "هل ترغب في زيادة قيمة vm.max_map_count إلى {0}", "LinuxVmMaxMapCountDialogTextPrimary": "هل ترغب في زيادة قيمة vm.max_map_count إلى {0}",
"LinuxVmMaxMapCountDialogTextSecondary": "قد تحاول بعض الألعاب إنشاء المزيد من تعيينات الذاكرة أكثر مما هو مسموح به حاليا. سيتحطم Ryujinx بمجرد تجاوز هذا الحد.", "LinuxVmMaxMapCountDialogTextSecondary": "قد تحاول بعض الألعاب إنشاء المزيد من تعيينات الذاكرة أكثر مما هو مسموح به حاليا. سيغلق ريوجينكس بمجرد تجاوز هذا الحد.",
"LinuxVmMaxMapCountDialogButtonUntilRestart": "نعم، حتى إعادة التشغيل التالية", "LinuxVmMaxMapCountDialogButtonUntilRestart": "نعم، حتى إعادة التشغيل التالية",
"LinuxVmMaxMapCountDialogButtonPersistent": "نعم، بشكل دائم", "LinuxVmMaxMapCountDialogButtonPersistent": "نعم، دائمًا",
"LinuxVmMaxMapCountWarningTextPrimary": "الحد الأقصى لمقدار تعيينات الذاكرة أقل من الموصى به.", "LinuxVmMaxMapCountWarningTextPrimary": "الحد الأقصى لمقدار تعيينات الذاكرة أقل من الموصى به.",
"LinuxVmMaxMapCountWarningTextSecondary": "القيمة الحالية لـ vm.max_map_count ({0}) أقل من {1}. قد تحاول بعض الألعاب إنشاء المزيد من تعيينات الذاكرة أكثر مما هو مسموح به حاليا. سيتحطم Ryujinx بمجرد تجاوز هذا الحد.\n\nقد ترغب في زيادة الحد اليدوي أو تثبيت pkexec، مما يسمح لـ Ryujinx بالمساعدة في ذلك.", "LinuxVmMaxMapCountWarningTextSecondary": "القيمة الحالية لـ vm.max_map_count ({0}) أقل من {1}. قد تحاول بعض الألعاب إنشاء المزيد من تعيينات الذاكرة أكثر مما هو مسموح به حاليا. سيغلق ريوجينكس بمجرد تجاوز هذا الحد.\n\nقد ترغب إما في زيادة الحد يدويا أو تثبيت pkexec، مما يسمح لـ ريوجينكس بالمساعدة في ذلك.",
"Settings": "إعدادات", "Settings": "إعدادات",
"SettingsTabGeneral": "واجهة المستخدم", "SettingsTabGeneral": "واجهة المستخدم",
"SettingsTabGeneralGeneral": "العامة", "SettingsTabGeneralGeneral": "عام",
"SettingsTabGeneralEnableDiscordRichPresence": "تمكين وجود ديسكورد الغني", "SettingsTabGeneralEnableDiscordRichPresence": "تمكين وجود ديسكورد الغني",
"SettingsTabGeneralCheckUpdatesOnLaunch": "التحقق من وجود تحديثات عند التشغيل", "SettingsTabGeneralCheckUpdatesOnLaunch": "التحقق من وجود تحديثات عند التشغيل",
"SettingsTabGeneralShowConfirmExitDialog": "إظهار مربع حوار \"تأكيد الخروج\"", "SettingsTabGeneralShowConfirmExitDialog": "إظهار مربع حوار \"تأكيد الخروج\"",
"SettingsTabGeneralRememberWindowState": "تذكر حجم/موضع النافذة",
"SettingsTabGeneralHideCursor": "إخفاء المؤشر:", "SettingsTabGeneralHideCursor": "إخفاء المؤشر:",
"SettingsTabGeneralHideCursorNever": "مطلقاً", "SettingsTabGeneralHideCursorNever": "مطلقا",
"SettingsTabGeneralHideCursorOnIdle": "عند الخمول", "SettingsTabGeneralHideCursorOnIdle": "عند الخمول",
"SettingsTabGeneralHideCursorAlways": "دائماً", "SettingsTabGeneralHideCursorAlways": "دائما",
"SettingsTabGeneralGameDirectories": "مجلدات الألعاب", "SettingsTabGeneralGameDirectories": "مجلدات الألعاب",
"SettingsTabGeneralAdd": "إضافة", "SettingsTabGeneralAdd": "إضافة",
"SettingsTabGeneralRemove": "إزالة", "SettingsTabGeneralRemove": "إزالة",
@@ -127,24 +132,24 @@
"SettingsTabSystemSystemLanguageLatinAmericanSpanish": "إسبانية أمريكا اللاتينية", "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "إسبانية أمريكا اللاتينية",
"SettingsTabSystemSystemLanguageSimplifiedChinese": "الصينية المبسطة", "SettingsTabSystemSystemLanguageSimplifiedChinese": "الصينية المبسطة",
"SettingsTabSystemSystemLanguageTraditionalChinese": "الصينية التقليدية", "SettingsTabSystemSystemLanguageTraditionalChinese": "الصينية التقليدية",
"SettingsTabSystemSystemTimeZone": "نظام التوقيت للنظام:", "SettingsTabSystemSystemTimeZone": "النطاق الزمني للنظام:",
"SettingsTabSystemSystemTime": "توقيت النظام:", "SettingsTabSystemSystemTime": "توقيت النظام:",
"SettingsTabSystemEnableVsync": "VSync", "SettingsTabSystemEnableVsync": "VSync",
"SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnablePptc": "PPTC (ذاكرة التخزين المؤقت للترجمة المستمرة)",
"SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks", "SettingsTabSystemEnableFsIntegrityChecks": "التحقق من سلامة نظام الملفات",
"SettingsTabSystemAudioBackend": "خلفية الصوت:", "SettingsTabSystemAudioBackend": "خلفية الصوت:",
"SettingsTabSystemAudioBackendDummy": "زائف", "SettingsTabSystemAudioBackendDummy": "زائف",
"SettingsTabSystemAudioBackendOpenAL": "OpenAL", "SettingsTabSystemAudioBackendOpenAL": "OpenAL",
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "الاختراقات", "SettingsTabSystemHacks": "هاكات",
"SettingsTabSystemHacksNote": "قد يتسبب في عدم الاستقرار", "SettingsTabSystemHacksNote": "قد يتسبب في عدم الاستقرار",
"SettingsTabSystemExpandDramSize": "استخدام تخطيط الذاكرة البديل (المطورين)", "SettingsTabSystemExpandDramSize": "استخدام تخطيط الذاكرة البديل (المطورين)",
"SettingsTabSystemIgnoreMissingServices": "تجاهل الخدمات المفقودة", "SettingsTabSystemIgnoreMissingServices": "تجاهل الخدمات المفقودة",
"SettingsTabGraphics": "الرسومات", "SettingsTabGraphics": "الرسومات",
"SettingsTabGraphicsAPI": "الرسومات API", "SettingsTabGraphicsAPI": "API الرسومات ",
"SettingsTabGraphicsEnableShaderCache": "تفعيل ذاكرة المظللات المؤقتة", "SettingsTabGraphicsEnableShaderCache": "تفعيل ذاكرة المظللات المؤقتة",
"SettingsTabGraphicsAnisotropicFiltering": "تصفية متباين الخواص:", "SettingsTabGraphicsAnisotropicFiltering": "تصفية:",
"SettingsTabGraphicsAnisotropicFilteringAuto": "تلقائي", "SettingsTabGraphicsAnisotropicFilteringAuto": "تلقائي",
"SettingsTabGraphicsAnisotropicFiltering2x": "2x", "SettingsTabGraphicsAnisotropicFiltering2x": "2x",
"SettingsTabGraphicsAnisotropicFiltering4x": "4×", "SettingsTabGraphicsAnisotropicFiltering4x": "4×",
@@ -152,7 +157,7 @@
"SettingsTabGraphicsAnisotropicFiltering16x": "16x", "SettingsTabGraphicsAnisotropicFiltering16x": "16x",
"SettingsTabGraphicsResolutionScale": "مقياس الدقة", "SettingsTabGraphicsResolutionScale": "مقياس الدقة",
"SettingsTabGraphicsResolutionScaleCustom": "مخصص (لا ينصح به)", "SettingsTabGraphicsResolutionScaleCustom": "مخصص (لا ينصح به)",
"SettingsTabGraphicsResolutionScaleNative": "الأصل (720p/1080p)", "SettingsTabGraphicsResolutionScaleNative": "الأصل (720p/1080p)",
"SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)",
"SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)",
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (لا ينصح به)", "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (لا ينصح به)",
@@ -162,11 +167,11 @@
"SettingsTabGraphicsAspectRatio16x10": "16:10", "SettingsTabGraphicsAspectRatio16x10": "16:10",
"SettingsTabGraphicsAspectRatio21x9": "21:9", "SettingsTabGraphicsAspectRatio21x9": "21:9",
"SettingsTabGraphicsAspectRatio32x9": "32:9", "SettingsTabGraphicsAspectRatio32x9": "32:9",
"SettingsTabGraphicsAspectRatioStretch": "تمدد لتلائم حجم النافذة", "SettingsTabGraphicsAspectRatioStretch": "تمديد لتناسب النافذة",
"SettingsTabGraphicsDeveloperOptions": "خيارات المطور", "SettingsTabGraphicsDeveloperOptions": "خيارات المطور",
"SettingsTabGraphicsShaderDumpPath": "مسار تفريغ الرسومات:", "SettingsTabGraphicsShaderDumpPath": "مسار تفريغ المظللات:",
"SettingsTabLogging": "التسجيل", "SettingsTabLogging": "تسجيل",
"SettingsTabLoggingLogging": "التسجيل", "SettingsTabLoggingLogging": "تسجيل",
"SettingsTabLoggingEnableLoggingToFile": "تفعيل التسجيل إلى ملف", "SettingsTabLoggingEnableLoggingToFile": "تفعيل التسجيل إلى ملف",
"SettingsTabLoggingEnableStubLogs": "تفعيل سجلات الـStub", "SettingsTabLoggingEnableStubLogs": "تفعيل سجلات الـStub",
"SettingsTabLoggingEnableInfoLogs": "تفعيل سجلات المعلومات", "SettingsTabLoggingEnableInfoLogs": "تفعيل سجلات المعلومات",
@@ -174,8 +179,8 @@
"SettingsTabLoggingEnableErrorLogs": "تفعيل سجلات الأخطاء", "SettingsTabLoggingEnableErrorLogs": "تفعيل سجلات الأخطاء",
"SettingsTabLoggingEnableTraceLogs": "تفعيل سجلات التتبع", "SettingsTabLoggingEnableTraceLogs": "تفعيل سجلات التتبع",
"SettingsTabLoggingEnableGuestLogs": "تفعيل سجلات الضيوف", "SettingsTabLoggingEnableGuestLogs": "تفعيل سجلات الضيوف",
"SettingsTabLoggingEnableFsAccessLogs": "تمكين سجلات الوصول لـ Fs", "SettingsTabLoggingEnableFsAccessLogs": "تمكين سجلات الوصول إلى نظام الملفات",
"SettingsTabLoggingFsGlobalAccessLogMode": "وضع سجل وصول عالمي Fs :", "SettingsTabLoggingFsGlobalAccessLogMode": "وضع سجل الوصول العالمي لنظام الملفات:",
"SettingsTabLoggingDeveloperOptions": "خيارات المطور", "SettingsTabLoggingDeveloperOptions": "خيارات المطور",
"SettingsTabLoggingDeveloperOptionsNote": "تحذير: سوف يقلل من الأداء", "SettingsTabLoggingDeveloperOptionsNote": "تحذير: سوف يقلل من الأداء",
"SettingsTabLoggingGraphicsBackendLogLevel": "مستوى سجل خلفية الرسومات:", "SettingsTabLoggingGraphicsBackendLogLevel": "مستوى سجل خلفية الرسومات:",
@@ -185,15 +190,15 @@
"SettingsTabLoggingGraphicsBackendLogLevelAll": "الكل", "SettingsTabLoggingGraphicsBackendLogLevelAll": "الكل",
"SettingsTabLoggingEnableDebugLogs": "تمكين سجلات التصحيح", "SettingsTabLoggingEnableDebugLogs": "تمكين سجلات التصحيح",
"SettingsTabInput": "الإدخال", "SettingsTabInput": "الإدخال",
"SettingsTabInputEnableDockedMode": "مركب بالمنصة", "SettingsTabInputEnableDockedMode": "تركيب بالمنصة",
"SettingsTabInputDirectKeyboardAccess": "الوصول المباشر إلى لوحة المفاتيح", "SettingsTabInputDirectKeyboardAccess": "الوصول المباشر للوحة المفاتيح",
"SettingsButtonSave": "حفظ", "SettingsButtonSave": "حفظ",
"SettingsButtonClose": "إغلاق", "SettingsButtonClose": "إغلاق",
"SettingsButtonOk": "موافق", "SettingsButtonOk": "موافق",
"SettingsButtonCancel": "إلغاء", "SettingsButtonCancel": "إلغاء",
"SettingsButtonApply": "تطبيق", "SettingsButtonApply": "تطبيق",
"ControllerSettingsPlayer": "اللاعب", "ControllerSettingsPlayer": "اللاعب",
"ControllerSettingsPlayer1": "اللاعب ١", "ControllerSettingsPlayer1": "اللاعب 1",
"ControllerSettingsPlayer2": "اللاعب 2", "ControllerSettingsPlayer2": "اللاعب 2",
"ControllerSettingsPlayer3": "اللاعب 3", "ControllerSettingsPlayer3": "اللاعب 3",
"ControllerSettingsPlayer4": "اللاعب 4", "ControllerSettingsPlayer4": "اللاعب 4",
@@ -205,12 +210,12 @@
"ControllerSettingsInputDevice": "جهاز الإدخال", "ControllerSettingsInputDevice": "جهاز الإدخال",
"ControllerSettingsRefresh": "تحديث", "ControllerSettingsRefresh": "تحديث",
"ControllerSettingsDeviceDisabled": "معطل", "ControllerSettingsDeviceDisabled": "معطل",
"ControllerSettingsControllerType": "نوع ذراع التحكم", "ControllerSettingsControllerType": "نوع وحدة التحكم",
"ControllerSettingsControllerTypeHandheld": "محمول", "ControllerSettingsControllerTypeHandheld": "محمول",
"ControllerSettingsControllerTypeProController": "Pro Controller", "ControllerSettingsControllerTypeProController": "وحدة تحكم برو",
"ControllerSettingsControllerTypeJoyConPair": "اقتران جوي كون", "ControllerSettingsControllerTypeJoyConPair": "زوج جوي كون",
"ControllerSettingsControllerTypeJoyConLeft": "يسار جوي كون", "ControllerSettingsControllerTypeJoyConLeft": "جوي كون اليسار ",
"ControllerSettingsControllerTypeJoyConRight": "يمين جوي كون", "ControllerSettingsControllerTypeJoyConRight": " جوي كون اليمين",
"ControllerSettingsProfile": "الملف الشخصي", "ControllerSettingsProfile": "الملف الشخصي",
"ControllerSettingsProfileDefault": "افتراضي", "ControllerSettingsProfileDefault": "افتراضي",
"ControllerSettingsLoad": "تحميل", "ControllerSettingsLoad": "تحميل",
@@ -223,13 +228,13 @@
"ControllerSettingsButtonY": "Y", "ControllerSettingsButtonY": "Y",
"ControllerSettingsButtonPlus": "+", "ControllerSettingsButtonPlus": "+",
"ControllerSettingsButtonMinus": "-", "ControllerSettingsButtonMinus": "-",
"ControllerSettingsDPad": "لوحة الاتجاه", "ControllerSettingsDPad": "أسهم الاتجاهات",
"ControllerSettingsDPadUp": "اعلى", "ControllerSettingsDPadUp": "اعلى",
"ControllerSettingsDPadDown": "أسفل", "ControllerSettingsDPadDown": "أسفل",
"ControllerSettingsDPadLeft": "يسار", "ControllerSettingsDPadLeft": "يسار",
"ControllerSettingsDPadRight": "يمين", "ControllerSettingsDPadRight": "يمين",
"ControllerSettingsStickButton": "زر", "ControllerSettingsStickButton": "زر",
"ControllerSettingsStickUp": "اعلى", "ControllerSettingsStickUp": "فوق",
"ControllerSettingsStickDown": "أسفل", "ControllerSettingsStickDown": "أسفل",
"ControllerSettingsStickLeft": "يسار", "ControllerSettingsStickLeft": "يسار",
"ControllerSettingsStickRight": "يمين", "ControllerSettingsStickRight": "يمين",
@@ -239,11 +244,11 @@
"ControllerSettingsStickDeadzone": "المنطقة الميتة:", "ControllerSettingsStickDeadzone": "المنطقة الميتة:",
"ControllerSettingsLStick": "العصا اليسرى", "ControllerSettingsLStick": "العصا اليسرى",
"ControllerSettingsRStick": "العصا اليمنى", "ControllerSettingsRStick": "العصا اليمنى",
"ControllerSettingsTriggersLeft": "المحفزات اليسرى", "ControllerSettingsTriggersLeft": "الأزندة اليسرى",
"ControllerSettingsTriggersRight": "المحفزات اليمني", "ControllerSettingsTriggersRight": "الأزندة اليمني",
"ControllerSettingsTriggersButtonsLeft": "أزرار التحفيز اليسرى", "ControllerSettingsTriggersButtonsLeft": "أزرار الزناد اليسرى",
"ControllerSettingsTriggersButtonsRight": "أزرار التحفيز اليمنى", "ControllerSettingsTriggersButtonsRight": "أزرار الزناد اليمنى",
"ControllerSettingsTriggers": "المحفزات", "ControllerSettingsTriggers": "أزندة",
"ControllerSettingsTriggerL": "L", "ControllerSettingsTriggerL": "L",
"ControllerSettingsTriggerR": "R", "ControllerSettingsTriggerR": "R",
"ControllerSettingsTriggerZL": "ZL", "ControllerSettingsTriggerZL": "ZL",
@@ -258,27 +263,128 @@
"ControllerSettingsTriggerThreshold": "قوة التحفيز:", "ControllerSettingsTriggerThreshold": "قوة التحفيز:",
"ControllerSettingsMotion": "الحركة", "ControllerSettingsMotion": "الحركة",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "استخدام الحركة المتوافقة مع CemuHook", "ControllerSettingsMotionUseCemuhookCompatibleMotion": "استخدام الحركة المتوافقة مع CemuHook",
"ControllerSettingsMotionControllerSlot": "خانة ذراع التحكم:", "ControllerSettingsMotionControllerSlot": "خانة وحدة التحكم:",
"ControllerSettingsMotionMirrorInput": "إعادة الإدخال", "ControllerSettingsMotionMirrorInput": "إعادة الإدخال",
"ControllerSettingsMotionRightJoyConSlot": "خانة اليمين JoyCon :", "ControllerSettingsMotionRightJoyConSlot": "خانة جويكون اليمين :",
"ControllerSettingsMotionServerHost": "مضيف الخادم:", "ControllerSettingsMotionServerHost": "مضيف الخادم:",
"ControllerSettingsMotionGyroSensitivity": "حساسية الغيرو:", "ControllerSettingsMotionGyroSensitivity": "حساسية مستشعر الحركة:",
"ControllerSettingsMotionGyroDeadzone": "منطقة الغيرو الميتة:", "ControllerSettingsMotionGyroDeadzone": "منطقة مستشعر الحركة الميتة:",
"ControllerSettingsSave": "حفظ", "ControllerSettingsSave": "حفظ",
"ControllerSettingsClose": "إغلاق", "ControllerSettingsClose": "إغلاق",
"KeyUnknown": "مجهول",
"KeyShiftLeft": "زر Shift الأيسر",
"KeyShiftRight": "زر Shift الأيمن",
"KeyControlLeft": "زر Ctrl الأيسر",
"KeyMacControlLeft": "زر ⌃ الأيسر",
"KeyControlRight": "زر Ctrl الأيمن",
"KeyMacControlRight": "زر ⌃ الأيمن",
"KeyAltLeft": "زر Alt الأيسر",
"KeyMacAltLeft": "زر ⌥ الأيسر",
"KeyAltRight": "زر Alt الأيمن",
"KeyMacAltRight": "زر ⌥ الأيمن",
"KeyWinLeft": "زر ⊞ الأيسر",
"KeyMacWinLeft": "زر ⌘ الأيسر",
"KeyWinRight": "زر ⊞ الأيمن",
"KeyMacWinRight": "زر ⌘ الأيمن",
"KeyMenu": "زر القائمة",
"KeyUp": "فوق",
"KeyDown": "اسفل",
"KeyLeft": "يسار",
"KeyRight": "يمين",
"KeyEnter": "مفتاح الإدخال",
"KeyEscape": "زر Escape",
"KeySpace": "مسافة",
"KeyTab": "زر Tab",
"KeyBackSpace": "زر المسح للخلف",
"KeyInsert": "زر Insert",
"KeyDelete": "زر الحذف",
"KeyPageUp": "زر Page Up",
"KeyPageDown": "زر Page Down",
"KeyHome": "زر Home",
"KeyEnd": "زر End",
"KeyCapsLock": "زر الحروف الكبيرة",
"KeyScrollLock": "زر Scroll Lock",
"KeyPrintScreen": "زر Print Screen",
"KeyPause": "زر Pause",
"KeyNumLock": "زر Num Lock",
"KeyClear": "زر Clear",
"KeyKeypad0": "لوحة الأرقام 0",
"KeyKeypad1": "لوحة الأرقام 1",
"KeyKeypad2": "لوحة الأرقام 2",
"KeyKeypad3": "لوحة الأرقام 3",
"KeyKeypad4": "لوحة الأرقام 4",
"KeyKeypad5": "لوحة الأرقام 5",
"KeyKeypad6": "لوحة الأرقام 6",
"KeyKeypad7": "لوحة الأرقام 7",
"KeyKeypad8": "لوحة الأرقام 8",
"KeyKeypad9": "لوحة الأرقام 9",
"KeyKeypadDivide": "لوحة الأرقام علامة القسمة",
"KeyKeypadMultiply": "لوحة الأرقام علامة الضرب",
"KeyKeypadSubtract": "لوحة الأرقام علامة الطرح\n",
"KeyKeypadAdd": "لوحة الأرقام علامة الزائد",
"KeyKeypadDecimal": "لوحة الأرقام الفاصلة العشرية",
"KeyKeypadEnter": "لوحة الأرقام زر الإدخال",
"KeyNumber0": "٠",
"KeyNumber1": "١",
"KeyNumber2": "٢",
"KeyNumber3": "٣",
"KeyNumber4": "٤",
"KeyNumber5": "٥",
"KeyNumber6": "٦",
"KeyNumber7": "٧",
"KeyNumber8": "٨",
"KeyNumber9": "٩",
"KeyTilde": "~",
"KeyGrave": "`",
"KeyMinus": "-",
"KeyPlus": "+",
"KeyBracketLeft": "[",
"KeyBracketRight": "]",
"KeySemicolon": ";",
"KeyQuote": "\"",
"KeyComma": ",",
"KeyPeriod": ".",
"KeySlash": "/",
"KeyBackSlash": "\\",
"KeyUnbound": "غير مرتبط",
"GamepadLeftStick": "زر عصا التحكم اليسرى",
"GamepadRightStick": "زر عصا التحكم اليمنى",
"GamepadLeftShoulder": "زر الكتف الأيسر‫ L",
"GamepadRightShoulder": "زر الكتف الأيمن‫ R",
"GamepadLeftTrigger": "زر الزناد الأيسر‫ (ZL)",
"GamepadRightTrigger": "زر الزناد الأيمن‫ (ZR)",
"GamepadDpadUp": "فوق",
"GamepadDpadDown": "اسفل",
"GamepadDpadLeft": "يسار",
"GamepadDpadRight": "يمين",
"GamepadMinus": "-",
"GamepadPlus": "+",
"GamepadGuide": "دليل",
"GamepadMisc1": "متنوع",
"GamepadPaddle1": "Paddle 1",
"GamepadPaddle2": "Paddle 2",
"GamepadPaddle3": "Paddle 3",
"GamepadPaddle4": "Paddle 4",
"GamepadTouchpad": "لوحة اللمس",
"GamepadSingleLeftTrigger0": "زر الزناد الأيسر 0",
"GamepadSingleRightTrigger0": "زر الزناد الأيمن 0",
"GamepadSingleLeftTrigger1": "زر الزناد الأيسر 1",
"GamepadSingleRightTrigger1": "زر الزناد الأيمن 1",
"StickLeft": "عصا التحكم اليسرى",
"StickRight": "عصا التحكم اليمنى",
"UserProfilesSelectedUserProfile": "الملف الشخصي المحدد للمستخدم:", "UserProfilesSelectedUserProfile": "الملف الشخصي المحدد للمستخدم:",
"UserProfilesSaveProfileName": "حفظ اسم الملف الشخصي", "UserProfilesSaveProfileName": "حفظ اسم الملف الشخصي",
"UserProfilesChangeProfileImage": "تغيير صورة الملف الشخصي", "UserProfilesChangeProfileImage": "تغيير صورة الملف الشخصي",
"UserProfilesAvailableUserProfiles": "الملفات الشخصية للمستخدم المتاحة:", "UserProfilesAvailableUserProfiles": "الملفات الشخصية للمستخدم المتاحة:",
"UserProfilesAddNewProfile": "أنشئ ملف شخصي", "UserProfilesAddNewProfile": "إنشاء ملف الشخصي",
"UserProfilesDelete": "حذف", "UserProfilesDelete": "حذف",
"UserProfilesClose": "إغلاق", "UserProfilesClose": "إغلاق",
"ProfileNameSelectionWatermark": "اختر اسم مستعار", "ProfileNameSelectionWatermark": "اختر اسم مستعار",
"ProfileImageSelectionTitle": "تحديد صورة الملف الشخصي", "ProfileImageSelectionTitle": "تحديد صورة الملف الشخصي",
"ProfileImageSelectionHeader": "اختر صورة الملف الشخصي", "ProfileImageSelectionHeader": "اختر صورة الملف الشخصي",
"ProfileImageSelectionNote": "يمكنك استيراد صورة ملف تعريف مخصصة، أو تحديد صورة رمزية من البرامج الثابتة للنظام", "ProfileImageSelectionNote": "يمكنك استيراد صورة ملف شخصي مخصصة، أو تحديد صورة رمزية من البرامج الثابتة للنظام",
"ProfileImageSelectionImportImage": "استيراد ملف الصورة", "ProfileImageSelectionImportImage": "استيراد ملف الصورة",
"ProfileImageSelectionSelectAvatar": "حدد الصورة الرمزية للبرنامج الثابت", "ProfileImageSelectionSelectAvatar": "حدد الصورة الرمزية من البرنامج الثابتة",
"InputDialogTitle": "حوار الإدخال", "InputDialogTitle": "حوار الإدخال",
"InputDialogOk": "موافق", "InputDialogOk": "موافق",
"InputDialogCancel": "إلغاء", "InputDialogCancel": "إلغاء",
@@ -289,13 +395,13 @@
"AvatarSetBackgroundColor": "تعيين لون الخلفية", "AvatarSetBackgroundColor": "تعيين لون الخلفية",
"AvatarClose": "إغلاق", "AvatarClose": "إغلاق",
"ControllerSettingsLoadProfileToolTip": "تحميل الملف الشخصي", "ControllerSettingsLoadProfileToolTip": "تحميل الملف الشخصي",
"ControllerSettingsAddProfileToolTip": "إضافة ملف تعريف", "ControllerSettingsAddProfileToolTip": "إضافة ملف شخصي",
"ControllerSettingsRemoveProfileToolTip": "إزالة ملف التعريف", "ControllerSettingsRemoveProfileToolTip": "إزالة الملف الشخصي",
"ControllerSettingsSaveProfileToolTip": "حفظ ملف التعريف", "ControllerSettingsSaveProfileToolTip": "حفظ الملف الشخصي",
"MenuBarFileToolsTakeScreenshot": "أخذ لقطة للشاشة", "MenuBarFileToolsTakeScreenshot": "أخذ لقطة للشاشة",
"MenuBarFileToolsHideUi": "إخفاء واجهة المستخدم", "MenuBarFileToolsHideUi": "إخفاء واجهة المستخدم",
"GameListContextMenuRunApplication": "تشغيل التطبيق", "GameListContextMenuRunApplication": "تشغيل التطبيق",
"GameListContextMenuToggleFavorite": بديل المفضلة", "GameListContextMenuToggleFavorite": عيين كمفضل",
"GameListContextMenuToggleFavoriteToolTip": "تبديل الحالة المفضلة للعبة", "GameListContextMenuToggleFavoriteToolTip": "تبديل الحالة المفضلة للعبة",
"SettingsTabGeneralTheme": "السمة:", "SettingsTabGeneralTheme": "السمة:",
"SettingsTabGeneralThemeDark": "داكن", "SettingsTabGeneralThemeDark": "داكن",
@@ -304,44 +410,44 @@
"ControllerSettingsRumble": "الاهتزاز", "ControllerSettingsRumble": "الاهتزاز",
"ControllerSettingsRumbleStrongMultiplier": "مضاعف اهتزاز قوي", "ControllerSettingsRumbleStrongMultiplier": "مضاعف اهتزاز قوي",
"ControllerSettingsRumbleWeakMultiplier": "مضاعف اهتزاز ضعيف", "ControllerSettingsRumbleWeakMultiplier": "مضاعف اهتزاز ضعيف",
"DialogMessageSaveNotAvailableMessage": "لا يوجد حفظ لـ {0} [{1:x16}]", "DialogMessageSaveNotAvailableMessage": "لا توجد بيانات الحفظ لـ {0} [{1:x16}]",
"DialogMessageSaveNotAvailableCreateSaveMessage": "هل ترغب في إنشاء حفظ لهذه اللعبة؟", "DialogMessageSaveNotAvailableCreateSaveMessage": "هل ترغب في إنشاء بيانات الحفظ لهذه اللعبة؟",
"DialogConfirmationTitle": "Ryujinx - تأكيد", "DialogConfirmationTitle": "ريوجينكس - تأكيد",
"DialogUpdaterTitle": "تحديث Ryujinx", "DialogUpdaterTitle": "ريوجينكس - المحدث",
"DialogErrorTitle": "Ryujinx - خطأ", "DialogErrorTitle": "ريوجينكس - خطأ",
"DialogWarningTitle": "Ryujinx - تحذير", "DialogWarningTitle": "ريوجينكس - تحذير",
"DialogExitTitle": "Ryujinx - الخروج", "DialogExitTitle": "ريوجينكس - الخروج",
"DialogErrorMessage": "واجه Ryujinx خطأ", "DialogErrorMessage": "واجه ريوجينكس خطأ",
"DialogExitMessage": "هل أنت متأكد من أنك تريد إغلاق Ryujinx؟", "DialogExitMessage": "هل أنت متأكد من أنك تريد إغلاق ريوجينكس؟",
"DialogExitSubMessage": "سيتم فقدان كافة البيانات غير المحفوظة!", "DialogExitSubMessage": "سيتم فقدان كافة البيانات غير المحفوظة!",
"DialogMessageCreateSaveErrorMessage": "حدث خطأ أثناء إنشاء المحفوظة المحددة: {0}", "DialogMessageCreateSaveErrorMessage": "حدث خطأ أثناء إنشاء بيانات الحفظ المحددة: {0}",
"DialogMessageFindSaveErrorMessage": "حدث خطأ أثناء البحث عن البيانات المحفوظة المحددة: {0}", "DialogMessageFindSaveErrorMessage": "حدث خطأ أثناء البحث عن بيانات الحفظ المحددة: {0}",
"FolderDialogExtractTitle": "اختر المجلد الذي تريد الاستخراج إليه", "FolderDialogExtractTitle": "اختر المجلد الذي تريد الاستخراج إليه",
"DialogNcaExtractionMessage": "استخراج قسم {0} من {1}...", "DialogNcaExtractionMessage": "استخراج قسم {0} من {1}...",
"DialogNcaExtractionTitle": "Ryujinx - مستخرج قسم NCA", "DialogNcaExtractionTitle": "ريوجينكس - مستخرج قسم NCA",
"DialogNcaExtractionMainNcaNotFoundErrorMessage": "فشل الاستخراج. لم يكن NCA الرئيسي موجودًا في الملف المحدد.", "DialogNcaExtractionMainNcaNotFoundErrorMessage": "فشل الاستخراج. لم يكن NCA الرئيسي موجودا في الملف المحدد.",
"DialogNcaExtractionCheckLogErrorMessage": "فشل الاستخراج. اقرأ ملف السجل لمزيد من المعلومات.", "DialogNcaExtractionCheckLogErrorMessage": "فشل الاستخراج. اقرأ ملف التسجيل لمزيد من المعلومات.",
"DialogNcaExtractionSuccessMessage": "تم الاستخراج بنجاح.", "DialogNcaExtractionSuccessMessage": "تم الاستخراج بنجاح.",
"DialogUpdaterConvertFailedMessage": "فشل تحويل إصدار Ryujinx الحالي.", "DialogUpdaterConvertFailedMessage": "فشل تحويل إصدار ريوجينكس الحالي.",
"DialogUpdaterCancelUpdateMessage": "إلغاء التحديث", "DialogUpdaterCancelUpdateMessage": "إلغاء التحديث",
"DialogUpdaterAlreadyOnLatestVersionMessage": "أنت تستخدم بالفعل أحدث إصدار من Ryujinx!", "DialogUpdaterAlreadyOnLatestVersionMessage": "أنت تستخدم بالفعل أحدث إصدار من ريوجينكس!",
"DialogUpdaterFailedToGetVersionMessage": "حدث خطأ أثناء محاولة الحصول على معلومات الإصدار من إصدار GitHub. يمكن أن يحدث هذا إذا تم تجميع إصدار جديد بواسطة GitHub Actions. جرب مجددا بعد دقائق.", "DialogUpdaterFailedToGetVersionMessage": "حدث خطأ أثناء محاولة الحصول على معلومات الإصدار من إصدار غيت هاب. يمكن أن يحدث هذا إذا تم تجميع إصدار جديد بواسطة إجراءات غيت هاب. جرب مجددا بعد دقائق.",
"DialogUpdaterConvertFailedGithubMessage": "فشل تحويل إصدار Ryujinx المستلم من إصدار Github.", "DialogUpdaterConvertFailedGithubMessage": "فشل تحويل إصدار ريوجينكس المستلم من إصدار غيت هاب.",
"DialogUpdaterDownloadingMessage": "تحميل التحديث...", "DialogUpdaterDownloadingMessage": "جاري تنزيل التحديث...",
"DialogUpdaterExtractionMessage": "استخراج التحديث...", "DialogUpdaterExtractionMessage": "جاري استخراج التحديث...",
"DialogUpdaterRenamingMessage": "إعادة تسمية التحديث...", "DialogUpdaterRenamingMessage": "إعادة تسمية التحديث...",
"DialogUpdaterAddingFilesMessage": "إضافة تحديث جديد...", "DialogUpdaterAddingFilesMessage": "إضافة تحديث جديد...",
"DialogUpdaterCompleteMessage": "اكتمل التحديث", "DialogUpdaterCompleteMessage": "اكتمل التحديث",
"DialogUpdaterRestartMessage": "هل تريد إعادة تشغيل Ryujinx الآن؟", "DialogUpdaterRestartMessage": "هل تريد إعادة تشغيل ريوجينكس الآن؟",
"DialogUpdaterNoInternetMessage": "أنت غير متصل بالإنترنت.", "DialogUpdaterNoInternetMessage": "أنت غير متصل بالإنترنت.",
"DialogUpdaterNoInternetSubMessage": "يرجى التحقق من أن لديك اتصال إنترنت فعال!", "DialogUpdaterNoInternetSubMessage": "يرجى التحقق من أن لديك اتصال إنترنت فعال!",
"DialogUpdaterDirtyBuildMessage": "لا يمكنك تحديث البناء القذر من Ryujinx!", "DialogUpdaterDirtyBuildMessage": "لا يمكنك تحديث نسخة القذرة من ريوجينكس!",
"DialogUpdaterDirtyBuildSubMessage": "الرجاء تحميل Ryujinx على https://ryujinx.org/ إذا كنت تبحث عن إصدار مدعوم.", "DialogUpdaterDirtyBuildSubMessage": "الرجاء تحميل ريوجينكس من https://ryujinx.org إذا كنت تبحث عن إصدار مدعوم.",
"DialogRestartRequiredMessage": "يتطلب إعادة التشغيل", "DialogRestartRequiredMessage": "يتطلب إعادة التشغيل",
"DialogThemeRestartMessage": "تم حفظ السمة. إعادة التشغيل مطلوبة لتطبيق السمة.", "DialogThemeRestartMessage": "تم حفظ السمة. إعادة التشغيل مطلوبة لتطبيق السمة.",
"DialogThemeRestartSubMessage": "هل تريد إعادة التشغيل", "DialogThemeRestartSubMessage": "هل تريد إعادة التشغيل",
"DialogFirmwareInstallEmbeddedMessage": "هل ترغب في تثبيت البرنامج الثابت المدمج في هذه اللعبة؟ (البرنامج الثابت {0})", "DialogFirmwareInstallEmbeddedMessage": "هل ترغب في تثبيت البرنامج الثابت المدمج في هذه اللعبة؟ (البرنامج الثابت {0})",
"DialogFirmwareInstallEmbeddedSuccessMessage": "لم يتم العثور على أي برنامج ثابت مثبت ولكن Ryujinx كان قادراً على تثبيت البرنامج الثابت {0} من اللعبة المقدمة.\nسيبدأ المحاكي الآن.", "DialogFirmwareInstallEmbeddedSuccessMessage": "لم يتم العثور على أي برنامج ثابت مثبت ولكن ريوجينكس كان قادرا على تثبيت البرنامج الثابت {0} من اللعبة المقدمة.\nسيبدأ المحاكي الآن.",
"DialogFirmwareNoFirmwareInstalledMessage": "لا يوجد برنامج ثابت مثبت", "DialogFirmwareNoFirmwareInstalledMessage": "لا يوجد برنامج ثابت مثبت",
"DialogFirmwareInstalledMessage": "تم تثبيت البرنامج الثابت {0}", "DialogFirmwareInstalledMessage": "تم تثبيت البرنامج الثابت {0}",
"DialogInstallFileTypesSuccessMessage": "تم تثبيت أنواع الملفات بنجاح!", "DialogInstallFileTypesSuccessMessage": "تم تثبيت أنواع الملفات بنجاح!",
@@ -349,38 +455,38 @@
"DialogUninstallFileTypesSuccessMessage": "تم إلغاء تثبيت أنواع الملفات بنجاح!", "DialogUninstallFileTypesSuccessMessage": "تم إلغاء تثبيت أنواع الملفات بنجاح!",
"DialogUninstallFileTypesErrorMessage": "فشل إلغاء تثبيت أنواع الملفات.", "DialogUninstallFileTypesErrorMessage": "فشل إلغاء تثبيت أنواع الملفات.",
"DialogOpenSettingsWindowLabel": "فتح نافذة الإعدادات", "DialogOpenSettingsWindowLabel": "فتح نافذة الإعدادات",
"DialogControllerAppletTitle": "برنامج التحكم", "DialogControllerAppletTitle": "تطبيق وحدة التحكم المصغر",
"DialogMessageDialogErrorExceptionMessage": "خطأ في عرض مربع حوار الرسالة: {0}", "DialogMessageDialogErrorExceptionMessage": "خطأ في عرض مربع حوار الرسالة: {0}",
"DialogSoftwareKeyboardErrorExceptionMessage": "خطأ في عرض لوحة مفاتيح البرامج: {0}", "DialogSoftwareKeyboardErrorExceptionMessage": "خطأ في عرض لوحة مفاتيح البرامج: {0}",
"DialogErrorAppletErrorExceptionMessage": "Error displaying ErrorApplet Dialog: {0}", "DialogErrorAppletErrorExceptionMessage": "خطأ في عرض مربع حوار خطأ التطبيق المصغر: {0}",
"DialogUserErrorDialogMessage": "{0}: {1}", "DialogUserErrorDialogMessage": "{0}: {1}",
"DialogUserErrorDialogInfoMessage": "لمزيد من المعلومات حول كيفية إصلاح هذا الخطأ، اتبع دليل الإعداد الخاص بنا.", "DialogUserErrorDialogInfoMessage": "لمزيد من المعلومات حول كيفية إصلاح هذا الخطأ، اتبع دليل الإعداد الخاص بنا.",
"DialogUserErrorDialogTitle": "خطأ ريوجينكس ({0})", "DialogUserErrorDialogTitle": "خطأ ريوجينكس ({0})",
"DialogAmiiboApiTitle": "أميبو API", "DialogAmiiboApiTitle": "أميبو API",
"DialogAmiiboApiFailFetchMessage": "حدث خطأ أثناء جلب المعلومات من واجهة برمجة التطبيقات.", "DialogAmiiboApiFailFetchMessage": "حدث خطأ أثناء جلب المعلومات من API.",
"DialogAmiiboApiConnectErrorMessage": "غير قادر على الاتصال بخادم API Amiibo. قد تكون الخدمة متوقفة أو قد تحتاج إلى التحقق من اتصال الإنترنت الخاص بك على الإنترنت.", "DialogAmiiboApiConnectErrorMessage": "غير قادر على الاتصال بخادم API أميبو. قد تكون الخدمة معطلة أو قد تحتاج إلى التحقق من اتصالك بالإنترنت.",
"DialogProfileInvalidProfileErrorMessage": "الملف الشخصي {0} غير متوافق مع نظام تكوين الإدخال الحالي.", "DialogProfileInvalidProfileErrorMessage": "الملف الشخصي {0} غير متوافق مع نظام تكوين الإدخال الحالي.",
"DialogProfileDefaultProfileOverwriteErrorMessage": "لا يمكن الكتابة فوق الملف الشخصي الافتراضي", "DialogProfileDefaultProfileOverwriteErrorMessage": "لا يمكن الكتابة فوق الملف الشخصي الافتراضي",
"DialogProfileDeleteProfileTitle": "حذف ملف التعريف", "DialogProfileDeleteProfileTitle": "حذف الملف الشخصي",
"DialogProfileDeleteProfileMessage": "هذا الإجراء لا رجعة فيه، هل أنت متأكد من أنك تريد المتابعة؟", "DialogProfileDeleteProfileMessage": "هذا الإجراء لا رجعة فيه، هل أنت متأكد من أنك تريد المتابعة؟",
"DialogWarning": "تحذير", "DialogWarning": "تحذير",
"DialogPPTCDeletionMessage": "أنت على وشك الإنتظار لإعادة بناء PTC على التمهيد التالي :\n\n{0}\n\nهل أنت متأكد من أنك تريد المتابعة؟", "DialogPPTCDeletionMessage": "أنت على وشك الإنتظار لإعادة بناء ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) عند الإقلاع التالي لـ:\n\n{0}\n\nأمتأكد من رغبتك في المتابعة؟",
"DialogPPTCDeletionErrorMessage": "خطأ في إزالة ذاكرة التخزين المؤقت PPTC في {0}: {1}", "DialogPPTCDeletionErrorMessage": "خطأ خلال تنظيف ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) في {0}: {1}",
"DialogShaderDeletionMessage": "أنت على وشك حذف ذاكرة التخزين المؤقت لـ Shader من أجل:\n\n{0}\n\nهل انت متأكد انك تريد المتابعة؟", "DialogShaderDeletionMessage": "أنت على وشك حذف ذاكرة المظللات المؤقتة ل:\n\n{0}\n\nهل انت متأكد انك تريد المتابعة؟",
"DialogShaderDeletionErrorMessage": "Error purging Shader cache at {0}: {1}", "DialogShaderDeletionErrorMessage": "حدث خطأ أثناء تنظيف ذاكرة المظللات المؤقتة في {0}: {1}",
"DialogRyujinxErrorMessage": "واجه Ryujinx خطأ", "DialogRyujinxErrorMessage": "واجه ريوجينكس خطأ",
"DialogInvalidTitleIdErrorMessage": "خطأ في واجهة المستخدم: اللعبة المحددة لم يكن لديها معرف عنوان صالح", "DialogInvalidTitleIdErrorMessage": "خطأ في واجهة المستخدم: اللعبة المحددة لم يكن لديها معرف عنوان صالح",
"DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "لم يتم العثور على فريموير للنظام صالح في {0}.", "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "لم يتم العثور على برنامج ثابت للنظام صالح في {0}.",
"DialogFirmwareInstallerFirmwareInstallTitle": "تثبيت البرنامج الثابت {0}", "DialogFirmwareInstallerFirmwareInstallTitle": "تثبيت البرنامج الثابت {0}",
"DialogFirmwareInstallerFirmwareInstallMessage": "سيتم تثبيت إصدار النظام {0}.", "DialogFirmwareInstallerFirmwareInstallMessage": "سيتم تثبيت إصدار النظام {0}.",
"DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nهذا سيحل محل إصدار النظام الحالي {0}.", "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nهذا سيحل محل إصدار النظام الحالي {0}.",
"DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\nهل تريد المتابعة؟", "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\nهل تريد المتابعة؟",
"DialogFirmwareInstallerFirmwareInstallWaitMessage": "تثبيت البرنامج الثابت...", "DialogFirmwareInstallerFirmwareInstallWaitMessage": "تثبيت البرنامج الثابت...",
"DialogFirmwareInstallerFirmwareInstallSuccessMessage": "تم تثبيت إصدار النظام {0} بنجاح.", "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "تم تثبيت إصدار النظام {0} بنجاح.",
"DialogUserProfileDeletionWarningMessage": "لن تكون هناك ملفات تعريف أخرى لفتحها إذا تم حذف الملف الشخصي المحدد", "DialogUserProfileDeletionWarningMessage": "لن تكون هناك ملفات الشخصية أخرى لفتحها إذا تم حذف الملف الشخصي المحدد",
"DialogUserProfileDeletionConfirmMessage": "هل تريد حذف الملف الشخصي المحدد", "DialogUserProfileDeletionConfirmMessage": "هل تريد حذف الملف الشخصي المحدد",
"DialogUserProfileUnsavedChangesTitle": "تحذير - التغييرات غير المحفوظة", "DialogUserProfileUnsavedChangesTitle": "تحذير - التغييرات غير محفوظة",
"DialogUserProfileUnsavedChangesMessage": "لقد قمت بإجراء تغييرات على ملف تعريف المستخدم هذا ولم يتم حفظها.", "DialogUserProfileUnsavedChangesMessage": "لقد قمت بإجراء تغييرات على الملف الشخصي لهذا المستخدم هذا ولم يتم حفظها.",
"DialogUserProfileUnsavedChangesSubMessage": "هل تريد تجاهل التغييرات؟", "DialogUserProfileUnsavedChangesSubMessage": "هل تريد تجاهل التغييرات؟",
"DialogControllerSettingsModifiedConfirmMessage": "تم تحديث إعدادات وحدة التحكم الحالية.", "DialogControllerSettingsModifiedConfirmMessage": "تم تحديث إعدادات وحدة التحكم الحالية.",
"DialogControllerSettingsModifiedConfirmSubMessage": "هل تريد الحفظ ؟", "DialogControllerSettingsModifiedConfirmSubMessage": "هل تريد الحفظ ؟",
@@ -388,29 +494,29 @@
"DialogModAlreadyExistsMessage": "التعديل موجود بالفعل", "DialogModAlreadyExistsMessage": "التعديل موجود بالفعل",
"DialogModInvalidMessage": "المجلد المحدد لا يحتوي على تعديل!", "DialogModInvalidMessage": "المجلد المحدد لا يحتوي على تعديل!",
"DialogModDeleteNoParentMessage": "فشل الحذف: لم يمكن العثور على المجلد الرئيسي للتعديل\"{0}\"!", "DialogModDeleteNoParentMessage": "فشل الحذف: لم يمكن العثور على المجلد الرئيسي للتعديل\"{0}\"!",
"DialogDlcNoDlcErrorMessage": "الملف المحدد لا يحتوي على DLC للعنوان المحدد!", "DialogDlcNoDlcErrorMessage": "الملف المحدد لا يحتوي على محتوى إضافي للعنوان المحدد!",
"DialogPerformanceCheckLoggingEnabledMessage": "لقد تم تمكين تسجيل التتبع، والذي تم تصميمه ليتم استخدامه من قبل المطورين فقط.", "DialogPerformanceCheckLoggingEnabledMessage": "لقد تم تمكين تسجيل التتبع، والذي تم تصميمه ليتم استخدامه من قبل المطورين فقط.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "للحصول على الأداء الأمثل، يوصى بتعطيل تسجيل التتبع. هل ترغب في تعطيل تسجيل التتبع الآن؟", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "للحصول على الأداء الأمثل، يوصى بتعطيل تسجيل التتبع. هل ترغب في تعطيل تسجيل التتبع الآن؟",
"DialogPerformanceCheckShaderDumpEnabledMessage": "لقد قمت بتمكين تفريغ التظليل، والذي تم تصميمه ليستخدمه المطورون فقط.", "DialogPerformanceCheckShaderDumpEnabledMessage": "لقد قمت بتمكين تفريغ المظللات، والذي تم تصميمه ليستخدمه المطورون فقط.",
"DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?", "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "للحصول على الأداء الأمثل، يوصى بتعطيل تفريغ المظللات. هل ترغب في تعطيل تفريغ المظللات الآن؟",
"DialogLoadAppGameAlreadyLoadedMessage": "تم تحميل لعبة بالفعل", "DialogLoadAppGameAlreadyLoadedMessage": "تم تحميل لعبة بالفعل",
"DialogLoadAppGameAlreadyLoadedSubMessage": "الرجاء إيقاف المحاكاة أو إغلاق المحاكي قبل بدء لعبة أخرى.", "DialogLoadAppGameAlreadyLoadedSubMessage": "الرجاء إيقاف المحاكاة أو إغلاق المحاكي قبل بدء لعبة أخرى.",
"DialogUpdateAddUpdateErrorMessage": "الملف المحدد لا يحتوي على تحديث للملف المحدد!", "DialogUpdateAddUpdateErrorMessage": "الملف المحدد لا يحتوي على تحديث للعنوان المحدد!",
"DialogSettingsBackendThreadingWarningTitle": "Warning - Backend Threading", "DialogSettingsBackendThreadingWarningTitle": "تحذير - خلفية متعددة المسارات",
"DialogSettingsBackendThreadingWarningMessage": "Ryujinx must be restarted after changing this option for it to apply fully. Depending on your platform, you may need to manually disable your driver's own multithreading when using Ryujinx's.", "DialogSettingsBackendThreadingWarningMessage": "يجب إعادة تشغيل ريوجينكس بعد تغيير هذا الخيار حتى يتم تطبيقه بالكامل. اعتمادا على النظام الأساسي الخاص بك، قد تحتاج إلى تعطيل تعدد المسارات الخاص ببرنامج الرسومات التشغيل الخاص بك يدويًا عند استخدام الخاص بريوجينكس.",
"DialogModManagerDeletionWarningMessage": "أنت على وشك حذف التعديل: {0}\n\nهل انت متأكد انك تريد المتابعة؟", "DialogModManagerDeletionWarningMessage": "أنت على وشك حذف التعديل: {0}\n\nهل انت متأكد انك تريد المتابعة؟",
"DialogModManagerDeletionAllWarningMessage": "أنت على وشك حذف كافة التعديلات لهذا العنوان.\n\nهل انت متأكد انك تريد المتابعة؟", "DialogModManagerDeletionAllWarningMessage": "أنت على وشك حذف كافة التعديلات لهذا العنوان.\n\nهل انت متأكد انك تريد المتابعة؟",
"SettingsTabGraphicsFeaturesOptions": "المميزات", "SettingsTabGraphicsFeaturesOptions": "المميزات",
"SettingsTabGraphicsBackendMultithreading": "Graphics Backend Multithreading:", "SettingsTabGraphicsBackendMultithreading": "تعدد المسارات لخلفية الرسومات:",
"CommonAuto": "تلقائي", "CommonAuto": "تلقائي",
"CommonOff": "إيقاف", "CommonOff": "معطل",
"CommonOn": "تشغيل", "CommonOn": "تشغيل",
"InputDialogYes": "نعم", "InputDialogYes": "نعم",
"InputDialogNo": "لا", "InputDialogNo": "لا",
"DialogProfileInvalidProfileNameErrorMessage": "يحتوي اسم الملف على أحرف غير صالحة. يرجى المحاولة مرة أخرى.", "DialogProfileInvalidProfileNameErrorMessage": "يحتوي اسم الملف على أحرف غير صالحة. يرجى المحاولة مرة أخرى.",
"MenuBarOptionsPauseEmulation": "إيقاف مؤقت", "MenuBarOptionsPauseEmulation": "إيقاف مؤقت",
"MenuBarOptionsResumeEmulation": "استئناف", "MenuBarOptionsResumeEmulation": "استئناف",
"AboutUrlTooltipMessage": "انقر لفتح موقع Ryujinx في متصفحك الافتراضي.", "AboutUrlTooltipMessage": "انقر لفتح موقع ريوجينكس في متصفحك الافتراضي.",
"AboutDisclaimerMessage": "ريوجينكس لا ينتمي إلى نينتندو™،\nأو أي من شركائها بأي شكل من الأشكال.", "AboutDisclaimerMessage": "ريوجينكس لا ينتمي إلى نينتندو™،\nأو أي من شركائها بأي شكل من الأشكال.",
"AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) يتم \nاستخدامه في محاكاة أمبيو لدينا.", "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) يتم \nاستخدامه في محاكاة أمبيو لدينا.",
"AboutPatreonUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في باتريون في متصفحك الافتراضي.", "AboutPatreonUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في باتريون في متصفحك الافتراضي.",
@@ -419,18 +525,18 @@
"AboutTwitterUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في تويتر في متصفحك الافتراضي.", "AboutTwitterUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في تويتر في متصفحك الافتراضي.",
"AboutRyujinxAboutTitle": "حول:", "AboutRyujinxAboutTitle": "حول:",
"AboutRyujinxAboutContent": "ريوجينكس هو محاكي لجهاز نينتندو سويتش™.\nمن فضلك ادعمنا على باتريون.\nاحصل على آخر الأخبار على تويتر أو ديسكورد.\nيمكن للمطورين المهتمين بالمساهمة معرفة المزيد على غيت هاب أو ديسكورد.", "AboutRyujinxAboutContent": "ريوجينكس هو محاكي لجهاز نينتندو سويتش™.\nمن فضلك ادعمنا على باتريون.\nاحصل على آخر الأخبار على تويتر أو ديسكورد.\nيمكن للمطورين المهتمين بالمساهمة معرفة المزيد على غيت هاب أو ديسكورد.",
"AboutRyujinxMaintainersTitle": "تم إصلاحها بواسطة:", "AboutRyujinxMaintainersTitle": تم صيانته بواسطة:",
"AboutRyujinxMaintainersContentTooltipMessage": "انقر لفتح صفحة المساهمين في متصفحك الافتراضي.", "AboutRyujinxMaintainersContentTooltipMessage": "انقر لفتح صفحة المساهمين في متصفحك الافتراضي.",
"AboutRyujinxSupprtersTitle": "مدعوم على باتريون بواسطة:", "AboutRyujinxSupprtersTitle": "مدعوم على باتريون بواسطة:",
"AmiiboSeriesLabel": "مجموعة أميبو", "AmiiboSeriesLabel": "مجموعة أميبو",
"AmiiboCharacterLabel": "شخصية", "AmiiboCharacterLabel": "شخصية",
"AmiiboScanButtonLabel": "فحصه", "AmiiboScanButtonLabel": "فحصه",
"AmiiboOptionsShowAllLabel": "إظهار كل أميبو", "AmiiboOptionsShowAllLabel": "إظهار كل أميبو",
"AmiiboOptionsUsRandomTagLabel": "Hack: Use Random tag Uuid", "AmiiboOptionsUsRandomTagLabel": "هاك: استخدم علامة Uuid عشوائية ",
"DlcManagerTableHeadingEnabledLabel": "مفعل", "DlcManagerTableHeadingEnabledLabel": "مفعل",
"DlcManagerTableHeadingTitleIdLabel": "معرف العنوان", "DlcManagerTableHeadingTitleIdLabel": "معرف العنوان",
"DlcManagerTableHeadingContainerPathLabel": "مسار الحاوية", "DlcManagerTableHeadingContainerPathLabel": "مسار الحاوية",
"DlcManagerTableHeadingFullPathLabel": "المسار كاملاً", "DlcManagerTableHeadingFullPathLabel": "المسار كاملا",
"DlcManagerRemoveAllButton": "حذف الكل", "DlcManagerRemoveAllButton": "حذف الكل",
"DlcManagerEnableAllButton": "تشغيل الكل", "DlcManagerEnableAllButton": "تشغيل الكل",
"DlcManagerDisableAllButton": "تعطيل الكل", "DlcManagerDisableAllButton": "تعطيل الكل",
@@ -440,100 +546,100 @@
"CommonSort": "فرز", "CommonSort": "فرز",
"CommonShowNames": "عرض الأسماء", "CommonShowNames": "عرض الأسماء",
"CommonFavorite": "المفضلة", "CommonFavorite": "المفضلة",
"OrderAscending": رتيب تصاعدي", "OrderAscending": "تصاعدي",
"OrderDescending": رتيب تنازلي", "OrderDescending": "تنازلي",
"SettingsTabGraphicsFeatures": "الميزات والتحسينات", "SettingsTabGraphicsFeatures": "الميزات والتحسينات",
"ErrorWindowTitle": "نافذة الخطأ", "ErrorWindowTitle": "نافذة الخطأ",
"ToggleDiscordTooltip": "اختر ما إذا كنت تريد عرض ريوجينكس في نشاط ديسكورد \"يتم تشغيله حاليًا\" أم لا", "ToggleDiscordTooltip": "اختر ما إذا كنت تريد عرض ريوجينكس في نشاط ديسكورد \"يتم تشغيله حاليا\" أم لا",
"AddGameDirBoxTooltip": "أدخل دليل اللعبة لإضافته إلى القائمة", "AddGameDirBoxTooltip": "أدخل مجلد اللعبة لإضافته إلى القائمة",
"AddGameDirTooltip": "إضافة دليل اللعبة إلى القائمة", "AddGameDirTooltip": "إضافة مجلد اللعبة إلى القائمة",
"RemoveGameDirTooltip": "إزالة دليل اللعبة المحدد", "RemoveGameDirTooltip": "إزالة مجلد اللعبة المحدد",
"CustomThemeCheckTooltip": "استخدم سمة أفالونيا المخصصة لواجهة المستخدم الرسومية لتغيير مظهر قوائم المحاكي", "CustomThemeCheckTooltip": "استخدم سمة أفالونيا المخصصة لواجهة المستخدم الرسومية لتغيير مظهر قوائم المحاكي",
"CustomThemePathTooltip": "مسار سمة واجهة المستخدم المخصصة", "CustomThemePathTooltip": "مسار سمة واجهة المستخدم المخصصة",
"CustomThemeBrowseTooltip": "تصفح للحصول على سمة واجهة المستخدم المخصصة", "CustomThemeBrowseTooltip": "تصفح للحصول على سمة واجهة المستخدم المخصصة",
"DockModeToggleTooltip": "يجعل وضع الإرساء النظام الذي تمت محاكاته بمثابة جهاز نينتندو سويتش الذي تم إرساءه. يؤدي هذا إلى تحسين الدقة الرسومية في معظم الألعاب. على العكس من ذلك، سيؤدي تعطيل هذا إلى جعل النظام الذي تمت محاكاته يعمل كجهاز نينتندو سويتش محمول، مما يقلل من جودة الرسومات.\n\nقم بتكوين عناصر تحكم اللاعب 1 إذا كنت تخطط لاستخدام وضع الإرساء؛ قم بتكوين عناصر التحكم المحمولة إذا كنت تخطط لاستخدام الوضع المحمول.\n\nاتركه مشغل إذا لم تكن متأكدًا.", "DockModeToggleTooltip": "يجعل وضع تركيب بالمنصة النظام الذي تمت محاكاته بمثابة جهاز نينتندو سويتش الذي تم تركيبه بالمنصة. يؤدي هذا إلى تحسين الدقة الرسومية في معظم الألعاب. على العكس من ذلك، سيؤدي تعطيل هذا إلى جعل النظام الذي تمت محاكاته يعمل كجهاز نينتندو سويتش محمول، مما يقلل من جودة الرسومات.\n\nقم بتكوين عناصر تحكم اللاعب 1 إذا كنت تخطط لاستخدام وضع تركيب بالمنصة؛ قم بتكوين عناصر التحكم المحمولة إذا كنت تخطط لاستخدام الوضع المحمول.\n\nاتركه مشغل إذا لم تكن متأكدا.",
"DirectKeyboardTooltip": "دعم الوصول المباشر إلى لوحة المفاتيح (HID). يوفر وصول الألعاب إلى لوحة المفاتيح الخاصة بك كجهاز لإدخال النص.\n\nيعمل فقط مع الألعاب التي تدعم استخدام لوحة المفاتيح في الأصل على أجهزة سويتش.\n\nاتركه معطلا إذا كنت غير متأكد.", "DirectKeyboardTooltip": "دعم الوصول المباشر للوحة المفاتيح (HID). يوفر وصول الألعاب إلى لوحة المفاتيح الخاصة بك كجهاز لإدخال النص.\n\nيعمل فقط مع الألعاب التي تدعم استخدام لوحة المفاتيح في الأصل على أجهزة سويتش.\n\nاتركه معطلا إذا كنت غير متأكد.",
"DirectMouseTooltip": "دعم الوصول المباشر إلى لوحة المفاتيح (HID). يوفر وصول الألعاب إلى لوحة المفاتيح الخاصة بك كجهاز لإدخال النص.\n\nيعمل فقط مع الألعاب التي تدعم استخدام لوحة المفاتيح في الأصل على أجهزة سويتش.\n\nاتركه معطلا إذا كنت غير متأكد.", "DirectMouseTooltip": "دعم الوصول المباشر للوحة المفاتيح (HID). يوفر وصول الألعاب إلى لوحة المفاتيح الخاصة بك كجهاز لإدخال النص.\n\nيعمل فقط مع الألعاب التي تدعم استخدام لوحة المفاتيح في الأصل على أجهزة سويتش.\n\nاتركه معطلا إذا كنت غير متأكد.",
"RegionTooltip": "تغيير منطقة النظام", "RegionTooltip": "تغيير منطقة النظام",
"LanguageTooltip": "تغيير لغة النظام", "LanguageTooltip": "تغيير لغة النظام",
"TimezoneTooltip": "تغيير المنطقة الزمنية للنظام", "TimezoneTooltip": "تغيير النطاق الزمني للنظام",
"TimeTooltip": "تغيير وقت النظام", "TimeTooltip": "تغيير وقت النظام",
"VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", "VSyncToggleTooltip": "محاكاة المزامنة العمودية للجهاز. في الأساس محدد الإطار لغالبية الألعاب؛ قد يؤدي تعطيله إلى تشغيل الألعاب بسرعة أعلى أو جعل شاشات التحميل تستغرق وقتا أطول أو تتعطل.\n\nيمكن تبديله داخل اللعبة باستخدام مفتاح التشغيل السريع الذي تفضله (F1 افتراضيا). نوصي بالقيام بذلك إذا كنت تخطط لتعطيله.\n\nاتركه ممكنا إذا لم تكن متأكدا.",
"PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.", "PptcToggleTooltip": "يحفظ وظائف JIT المترجمة بحيث لا تحتاج إلى ترجمتها في كل مرة يتم فيها تحميل اللعبة.\n\nيقلل من التقطيع ويسرع بشكل ملحوظ أوقات التشغيل بعد التشغيل الأول للعبة.\n\nاتركه ممكنا إذا لم تكن متأكدا.",
"FsIntegrityToggleTooltip": "يتحقق من وجود ملفات تالفة عند تشغيل لعبة ما، وإذا تم اكتشاف ملفات تالفة، فسيتم عرض خطأ تجزئة في السجل.\n\nليس له أي تأثير على الأداء ويهدف إلى المساعدة في استكشاف الأخطاء وإصلاحها.\n\nاتركه مفعلا إذا كنت غير متأكد.", "FsIntegrityToggleTooltip": "يتحقق من وجود ملفات تالفة عند تشغيل لعبة ما، وإذا تم اكتشاف ملفات تالفة، فسيتم عرض خطأ تجزئة في السجل.\n\nليس له أي تأثير على الأداء ويهدف إلى المساعدة في استكشاف الأخطاء وإصلاحها.\n\nاتركه مفعلا إذا كنت غير متأكد.",
"AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.", "AudioBackendTooltip": "يغير الواجهة الخلفية المستخدمة لتقديم الصوت.\n\nSDL2 هو الخيار المفضل، بينما يتم استخدام OpenAL وSoundIO كبديلين. زائف لن يكون لها صوت.\n\nاضبط على SDL2 إذا لم تكن متأكدا.",
"MemoryManagerTooltip": "Change how guest memory is mapped and accessed. Greatly affects emulated CPU performance.\n\nSet to HOST UNCHECKED if unsure.", "MemoryManagerTooltip": "تغيير كيفية تعيين ذاكرة الضيف والوصول إليها. يؤثر بشكل كبير على أداء وحدة المعالجة المركزية التي تمت محاكاتها.\n\nاضبط على المضيف غير محدد إذا لم تكن متأكدا.",
"MemoryManagerSoftwareTooltip": "استخدام جدول الصفحات البرمجي لترجمة العناوين. أعلى دقة ولكن أبطأ أداء.", "MemoryManagerSoftwareTooltip": "استخدام جدول الصفحات البرمجي لترجمة العناوين. أعلى دقة ولكن أبطأ أداء.",
"MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.", "MemoryManagerHostTooltip": "تعيين الذاكرة مباشرة في مساحة عنوان المضيف. تجميع وتنفيذ JIT أسرع بكثير.",
"MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.", "MemoryManagerUnsafeTooltip": "تعيين الذاكرة مباشرة، ولكن لا تخفي العنوان داخل مساحة عنوان الضيف قبل الوصول. أسرع، ولكن على حساب السلامة. يمكن لتطبيق الضيف الوصول إلى الذاكرة من أي مكان في ريوجينكس، لذا قم بتشغيل البرامج التي تثق بها فقط مع هذا الوضع.",
"UseHypervisorTooltip": "Use Hypervisor instead of JIT. Greatly improves performance when available, but can be unstable in its current state.", "UseHypervisorTooltip": "استخدم هايبرڤايزور بدلا من JIT. يعمل على تحسين الأداء بشكل كبير عند توفره، ولكنه قد يكون غير مستقر في حالته الحالية.",
"DRamTooltip": "Utilizes an alternative MemoryMode layout to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", "DRamTooltip": "يستخدم تخطيط وضع الذاكرة البديل لتقليد نموذج سويتش المطورين.\n\nيعد هذا مفيدا فقط لحزم النسيج عالية الدقة أو تعديلات دقة 4K. لا يحسن الأداء.\n\nاتركه معطلا إذا لم تكن متأكدا.",
"IgnoreMissingServicesTooltip": "يتجاهل خدمات نظام هوريزون غير المنفذة. قد يساعد هذا في تجاوز الأعطال عند تشغيل ألعاب معينة.\n\nاتركه معطلا إذا كنت غير متأكد.", "IgnoreMissingServicesTooltip": "يتجاهل خدمات نظام هوريزون غير المنفذة. قد يساعد هذا في تجاوز الأعطال عند تشغيل ألعاب معينة.\n\nاتركه معطلا إذا كنت غير متأكد.",
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", "GraphicsBackendThreadingTooltip": "ينفذ أوامر الواجهة الخلفية للرسومات على مسار ثاني.\n\nيعمل على تسريع عملية تجميع المظللات وتقليل التقطيع وتحسين الأداء على برامج تشغيل وحدة الرسوميات دون دعم المسارات المتعددة الخاصة بهم. أداء أفضل قليلا على برامج التشغيل ذات المسارات المتعددة.\n\nاضبط على تلقائي إذا لم تكن متأكدا.",
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", "GalThreadingTooltip": "ينفذ أوامر الواجهة الخلفية للرسومات على مسار ثاني.\n\nيعمل على تسريع عملية تجميع المظللات وتقليل التقطيع وتحسين الأداء على برامج تشغيل وحدة الرسوميات دون دعم المسارات المتعددة الخاصة بهم. أداء أفضل قليلا على برامج التشغيل ذات المسارات المتعددة.\n\nاضبط على تلقائي إذا لم تكن متأكدا.",
"ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.", "ShaderCacheToggleTooltip": "يحفظ ذاكرة المظللات المؤقتة على القرص مما يقلل من التقطيع في عمليات التشغيل اللاحقة.\n\nاتركه مفعلا إذا لم تكن متأكدا.",
"ResolutionScaleTooltip": "يضاعف دقة عرض اللعبة.\n\nقد لا تعمل بعض الألعاب مع هذا وتبدو منقطة حتى عند زيادة الدقة؛ بالنسبة لهذه الألعاب، قد تحتاج إلى العثور على تعديلات تزيل الصقل أو تزيد من دقة العرض الداخلي. لاستخدام الأخير، من المحتمل أن ترغب في تحديد أصلي.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبًا والتجربة حتى تجد المظهر المفضل للعبة.\n\nضع في اعتبارك أن 4x مبالغة في أي إعداد تقريبًا.", "ResolutionScaleTooltip": "يضاعف دقة عرض اللعبة.\n\nقد لا تعمل بعض الألعاب مع هذا وتبدو منقطة حتى عند زيادة الدقة؛ بالنسبة لهذه الألعاب، قد تحتاج إلى العثور على تعديلات تزيل تنعيم الحواف أو تزيد من دقة العرض الداخلي. لاستخدام الأخير، من المحتمل أن ترغب في تحديد أصلي.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبًا والتجربة حتى تجد المظهر المفضل للعبة.\n\nضع في اعتبارك أن 4x مبالغة في أي إعداد تقريبًا.",
"ResolutionScaleEntryTooltip": "مقياس دقة النقطة العائمة، مثل 1.5. من المرجح أن تتسبب المقاييس غير المتكاملة في حدوث مشكلات أو تعطل.", "ResolutionScaleEntryTooltip": "مقياس دقة النقطة العائمة، مثل 1.5. من المرجح أن تتسبب المقاييس غير المتكاملة في حدوث مشكلات أو تعطل.",
"AnisotropyTooltip": "مستوى تصفية متباين الخواص. اضبط على تلقائي لاستخدام القيمة التي تطلبها اللعبة.", "AnisotropyTooltip": "مستوى تصفية. اضبط على تلقائي لاستخدام القيمة التي تطلبها اللعبة.",
"AspectRatioTooltip": "يتم تطبيق نسبة العرض إلى الارتفاع على نافذة العارض.\n\nقم بتغيير هذا فقط إذا كنت تستخدم تعديل نسبة العرض إلى الارتفاع للعبتك، وإلا سيتم تمديد الرسومات.\n\nاتركه على 16:9 إذا لم تكن متأكدًا.", "AspectRatioTooltip": "يتم تطبيق نسبة العرض إلى الارتفاع على نافذة العارض.\n\nقم بتغيير هذا فقط إذا كنت تستخدم تعديل نسبة العرض إلى الارتفاع للعبتك، وإلا سيتم تمديد الرسومات.\n\nاتركه16:9 إذا لم تكن متأكدا.",
"ShaderDumpPathTooltip": "Graphics Shaders Dump Path", "ShaderDumpPathTooltip": "مسار تفريغ المظللات",
"FileLogTooltip": "حفظ تسجيل وحدة التحكم إلى ملف سجل على القرص. لا يؤثر على الأداء.", "FileLogTooltip": "حفظ تسجيل وحدة التحكم إلى ملف سجل على القرص. لا يؤثر على الأداء.",
"StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.", "StubLogTooltip": "طباعة رسائل سجل stub في وحدة التحكم. لا يؤثر على الأداء.",
"InfoLogTooltip": "Prints info log messages in the console. Does not affect performance.", "InfoLogTooltip": "طباعة رسائل سجل المعلومات في وحدة التحكم. لا يؤثر على الأداء.",
"WarnLogTooltip": "طباعة رسائل سجل التحذير في وحدة التحكم. لا يؤثر على الأداء.", "WarnLogTooltip": "طباعة رسائل سجل التحذير في وحدة التحكم. لا يؤثر على الأداء.",
"ErrorLogTooltip": "طباعة رسائل سجل الأخطاء في وحدة التحكم. لا يؤثر على الأداء.", "ErrorLogTooltip": "طباعة رسائل سجل الأخطاء في وحدة التحكم. لا يؤثر على الأداء.",
"TraceLogTooltip": "طباعة رسائل سجل التتبع في وحدة التحكم. لا يؤثر على الأداء.", "TraceLogTooltip": "طباعة رسائل سجل التتبع في وحدة التحكم. لا يؤثر على الأداء.",
"GuestLogTooltip": "طباعة رسائل سجل الضيف في وحدة التحكم. لا يؤثر على الأداء.", "GuestLogTooltip": "طباعة رسائل سجل الضيف في وحدة التحكم. لا يؤثر على الأداء.",
"FileAccessLogTooltip": "طباعة رسائل سجل الوصول إلى الملفات في وحدة التحكم.", "FileAccessLogTooltip": "طباعة رسائل سجل الوصول إلى الملفات في وحدة التحكم.",
"FSAccessLogModeTooltip": "Enables FS access log output to the console. Possible modes are 0-3", "FSAccessLogModeTooltip": "تمكين إخراج سجل الوصول إلى نظام الملفات إلى وحدة التحكم. الأوضاع الممكنة هي 0-3",
"DeveloperOptionTooltip": "استخدمه بعناية", "DeveloperOptionTooltip": "استخدمه بعناية",
"OpenGlLogLevel": "يتطلب تمكين مستويات السجل المناسبة", "OpenGlLogLevel": "يتطلب تمكين مستويات السجل المناسبة",
"DebugLogTooltip": "طباعة رسائل سجل التصحيح في وحدة التحكم.\n\nاستخدم هذا فقط إذا طلب منك أحد الموظفين تحديدًا ذلك، لأنه سيجعل من الصعب قراءة السجلات وسيؤدي إلى تدهور أداء المحاكي.", "DebugLogTooltip": "طباعة رسائل سجل التصحيح في وحدة التحكم.\n\nاستخدم هذا فقط إذا طلب منك أحد الموظفين تحديدًا ذلك، لأنه سيجعل من الصعب قراءة السجلات وسيؤدي إلى تدهور أداء المحاكي.",
"LoadApplicationFileTooltip": "افتح مستكشف الملفات لاختيار ملف متوافق مع Switch لتحميله", "LoadApplicationFileTooltip": "افتح مستكشف الملفات لاختيار ملف متوافق مع سويتش لتحميله",
"LoadApplicationFolderTooltip": "افتح مستكشف الملفات لاختيار تطبيق متوافق مع Switch للتحميل", "LoadApplicationFolderTooltip": "افتح مستكشف الملفات لاختيار تطبيق متوافق مع سويتش للتحميل",
"OpenRyujinxFolderTooltip": "فتح مجلد نظام ملفات Ryujinx", "OpenRyujinxFolderTooltip": "فتح مجلد نظام ملفات ريوجينكس",
"OpenRyujinxLogsTooltip": "يفتح المجلد الذي تتم كتابة السجلات إليه", "OpenRyujinxLogsTooltip": "يفتح المجلد الذي تتم كتابة السجلات إليه",
"ExitTooltip": "الخروج من Ryujinx", "ExitTooltip": "الخروج من ريوجينكس",
"OpenSettingsTooltip": "فتح نافذة الإعدادات", "OpenSettingsTooltip": "فتح نافذة الإعدادات",
"OpenProfileManagerTooltip": "فتح نافذة إدارة ملفات تعريف المستخدمين", "OpenProfileManagerTooltip": "فتح نافذة إدارة الملفات الشخصية للمستخدمين",
"StopEmulationTooltip": "إيقاف محاكاة اللعبة الحالية والعودة إلى اختيار اللعبة", "StopEmulationTooltip": "إيقاف محاكاة اللعبة الحالية والعودة إلى اختيار اللعبة",
"CheckUpdatesTooltip": "التحقق من وجود تحديثات لـ Ryujinx", "CheckUpdatesTooltip": "التحقق من وجود تحديثات لريوجينكس",
"OpenAboutTooltip": "فتح حول النافذة", "OpenAboutTooltip": "فتح حول النافذة",
"GridSize": "حجم الشبكة", "GridSize": "حجم الشبكة",
"GridSizeTooltip": "تغيير حجم عناصر الشبكة", "GridSizeTooltip": "تغيير حجم عناصر الشبكة",
"SettingsTabSystemSystemLanguageBrazilianPortuguese": "البرتغالية البرازيلية", "SettingsTabSystemSystemLanguageBrazilianPortuguese": "البرتغالية البرازيلية",
"AboutRyujinxContributorsButtonHeader": "رؤية جميع المساهمين", "AboutRyujinxContributorsButtonHeader": "رؤية جميع المساهمين",
"SettingsTabSystemAudioVolume": "الحجم:", "SettingsTabSystemAudioVolume": "مستوى الصوت:",
"AudioVolumeTooltip": "تغيير مستوى الصوت", "AudioVolumeTooltip": "تغيير مستوى الصوت",
"SettingsTabSystemEnableInternetAccess": "الوصول إلى إنترنت كضيف/وضع LAN", "SettingsTabSystemEnableInternetAccess": "الوصول إلى إنترنت كضيف/وضع LAN",
"EnableInternetAccessTooltip": "للسماح للتطبيق المحاكي بالاتصال بالإنترنت.\n\nالألعاب ذات وضع الشبكة المحلية يمكن الاتصال ببعضها البعض عندما يتم تمكين هذا النظام وتكون الأنظمة متصلة بنفس نقطة الوصول. هذا يشمل وحدات التحكم الحقيقية أيضًا.\n\nلا يسمح بالاتصال بخوادم Nintendo. قد يسبب الانهيار في بعض الألعاب التي تحاول الاتصال بالإنترنت.\n\nاتركه متوقفا إن لم يكن مؤكدا.", "EnableInternetAccessTooltip": "للسماح للتطبيق الذي تمت محاكاته بالاتصال بالإنترنت.\n\nيمكن للألعاب التي تحتوي على وضع LAN الاتصال ببعضها البعض عند تمكين ذلك وتوصيل الأنظمة بنفس نقطة الوصول. وهذا يشمل الأجهزة الحقيقية أيضا.\n\nلا يسمح بالاتصال بخوادم نينتندو. قد يتسبب في حدوث عطل في بعض الألعاب التي تحاول الاتصال بالإنترنت.\n\nاتركه معطلا إذا لم تكن متأكدا.",
"GameListContextMenuManageCheatToolTip": "إدارة الغش", "GameListContextMenuManageCheatToolTip": "إدارة الغش",
"GameListContextMenuManageCheat": "إدارة الغش", "GameListContextMenuManageCheat": "إدارة الغش",
"GameListContextMenuManageModToolTip": "إدارة التعديلات", "GameListContextMenuManageModToolTip": "إدارة التعديلات",
"GameListContextMenuManageMod": "إدارة التعديلات", "GameListContextMenuManageMod": "إدارة التعديلات",
"ControllerSettingsStickRange": "نطاق:", "ControllerSettingsStickRange": "نطاق:",
"DialogStopEmulationTitle": "Ryujinx - إيقاف المحاكاة", "DialogStopEmulationTitle": "ريوجينكس - إيقاف المحاكاة",
"DialogStopEmulationMessage": "هل أنت متأكد أنك تريد إيقاف المحاكاة؟", "DialogStopEmulationMessage": "هل أنت متأكد أنك تريد إيقاف المحاكاة؟",
"SettingsTabCpu": "CPU", "SettingsTabCpu": "المعالج",
"SettingsTabAudio": "الصوت", "SettingsTabAudio": "الصوت",
"SettingsTabNetwork": "الشبكة", "SettingsTabNetwork": "الشبكة",
"SettingsTabNetworkConnection": "اتصال الشبكة", "SettingsTabNetworkConnection": "اتصال الشبكة",
"SettingsTabCpuCache": "ذاكرة المعالج المؤقت", "SettingsTabCpuCache": "ذاكرة المعالج المؤقت",
"SettingsTabCpuMemory": "وضع المعالج", "SettingsTabCpuMemory": "وضع المعالج",
"DialogUpdaterFlatpakNotSupportedMessage": "الرجاء تحديث Ryujinx عبر FlatHub.", "DialogUpdaterFlatpakNotSupportedMessage": "الرجاء تحديث ريوجينكس عبر فلات هاب.",
"UpdaterDisabledWarningTitle": "التحديث معطل!", "UpdaterDisabledWarningTitle": "المحدث معطل!",
"ControllerSettingsRotate90": "تدوير 90 درجة في اتجاه عقارب الساعة", "ControllerSettingsRotate90": "تدوير 90 درجة في اتجاه عقارب الساعة",
"IconSize": "حجم الأيقونة", "IconSize": "حجم الأيقونة",
"IconSizeTooltip": "تغيير حجم أيقونات اللعبة", "IconSizeTooltip": "تغيير حجم أيقونات اللعبة",
"MenuBarOptionsShowConsole": "عرض وحدة التحكم", "MenuBarOptionsShowConsole": "عرض وحدة التحكم",
"ShaderCachePurgeError": "Error purging shader cache at {0}: {1}", "ShaderCachePurgeError": "حدث خطأ أثناء تنظيف ذاكرة المظللات المؤقتة في {0}: {1}",
"UserErrorNoKeys": "المفاتيح غير موجودة", "UserErrorNoKeys": "المفاتيح غير موجودة",
"UserErrorNoFirmware": "لم يتم العثور على البرنامج الثابت", "UserErrorNoFirmware": "لم يتم العثور على البرنامج الثابت",
"UserErrorFirmwareParsingFailed": "خطأ في تحليل البرنامج الثابت", "UserErrorFirmwareParsingFailed": "خطأ في تحليل البرنامج الثابت",
"UserErrorApplicationNotFound": "التطبيق غير موجود", "UserErrorApplicationNotFound": "التطبيق غير موجود",
"UserErrorUnknown": "خطأ غير معروف", "UserErrorUnknown": "خطأ غير معروف",
"UserErrorUndefined": "خطأ غير محدد", "UserErrorUndefined": "خطأ غير محدد",
"UserErrorNoKeysDescription": "لم يتمكن Ryujinx من العثور على ملف 'prod.keys' الخاص بك", "UserErrorNoKeysDescription": "لم يتمكن ريوجينكس من العثور على ملف 'prod.keys' الخاص بك",
"UserErrorNoFirmwareDescription": "لم يتمكن ريوجينكس من العثور على أية برامج ثابتة مثبتة", "UserErrorNoFirmwareDescription": "لم يتمكن ريوجينكس من العثور على أية برامج ثابتة مثبتة",
"UserErrorFirmwareParsingFailedDescription": "لم يتمكن ريوجينكس من تحليل البرامج الثابتة المتوفرة. يحدث هذا عادة بسبب المفاتيح القديمة.", "UserErrorFirmwareParsingFailedDescription": "لم يتمكن ريوجينكس من تحليل البرامج الثابتة المتوفرة. يحدث هذا عادة بسبب المفاتيح القديمة.",
"UserErrorApplicationNotFoundDescription": "تعذر على ريوجينكس العثور على تطبيق صالح في المسار المحدد.", "UserErrorApplicationNotFoundDescription": "تعذر على ريوجينكس العثور على تطبيق صالح في المسار المحدد.",
@@ -542,72 +648,73 @@
"OpenSetupGuideMessage": "فتح دليل الإعداد", "OpenSetupGuideMessage": "فتح دليل الإعداد",
"NoUpdate": "لا يوجد تحديث", "NoUpdate": "لا يوجد تحديث",
"TitleUpdateVersionLabel": "الإصدار: {0}", "TitleUpdateVersionLabel": "الإصدار: {0}",
"RyujinxInfo": "Ryujinx - معلومات", "RyujinxInfo": "ريوجينكس - معلومات",
"RyujinxConfirm": "Ryujinx - تأكيد", "RyujinxConfirm": "ريوجينكس - تأكيد",
"FileDialogAllTypes": "كل الأنواع", "FileDialogAllTypes": "كل الأنواع",
"Never": "مطلقاً", "Never": "مطلقا",
"SwkbdMinCharacters": "يجب أن يبلغ طوله {0} حرفًا على الأقل", "SwkbdMinCharacters": "يجب أن يبلغ طوله {0} حرفا على الأقل",
"SwkbdMinRangeCharacters": "يجب أن يتكون من {0}-{1} حرفًا", "SwkbdMinRangeCharacters": "يجب أن يتكون من {0}-{1} حرفا",
"SoftwareKeyboard": "لوحة المفاتيح البرمجية", "SoftwareKeyboard": "لوحة المفاتيح البرمجية",
"SoftwareKeyboardModeNumeric": "يجب أن يكون 0-9 أو '.' فقط", "SoftwareKeyboardModeNumeric": "يجب أن يكون 0-9 أو '.' فقط",
"SoftwareKeyboardModeAlphabet": "يجب أن تكون الأحرف غير CJK فقط", "SoftwareKeyboardModeAlphabet": "يجب أن تكون الأحرف غير CJK فقط",
"SoftwareKeyboardModeASCII": "يجب أن يكون نص ASCII فقط", "SoftwareKeyboardModeASCII": "يجب أن يكون نص ASCII فقط",
"ControllerAppletControllers": "ذراع التحكم المدعومة:", "ControllerAppletControllers": "وحدات التحكم المدعومة:",
"ControllerAppletPlayers": "اللاعبين:", "ControllerAppletPlayers": "اللاعبين:",
"ControllerAppletDescription": "الإعدادات الحالية غير صالحة. افتح الإعدادات وأعد تكوين المدخلات الخاصة بك.", "ControllerAppletDescription": "الإعدادات الحالية غير صالحة. افتح الإعدادات وأعد تكوين المدخلات الخاصة بك.",
"ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.", "ControllerAppletDocked": "تم ضبط وضع تركيب بالمنصة. يجب تعطيل التحكم المحمول.",
"UpdaterRenaming": "إعادة تسمية الملفات القديمة...", "UpdaterRenaming": "إعادة تسمية الملفات القديمة...",
"UpdaterRenameFailed": "التحديث غير قادر على إعادة تسمية الملف: {0}", "UpdaterRenameFailed": "المحدث غير قادر على إعادة تسمية الملف: {0}",
"UpdaterAddingFiles": "إضافة ملفات جديدة...", "UpdaterAddingFiles": "إضافة ملفات جديدة...",
"UpdaterExtracting": "استخراج التحديث...", "UpdaterExtracting": "استخراج التحديث...",
"UpdaterDownloading": "تحميل التحديث...", "UpdaterDownloading": "تحميل التحديث...",
"Game": "لعبة", "Game": "لعبة",
"Docked": "مركب بالمنصة", "Docked": "تركيب بالمنصة",
"Handheld": "محمول", "Handheld": "محمول",
"ConnectionError": "خطأ في الاتصال", "ConnectionError": "خطأ في الاتصال",
"AboutPageDeveloperListMore": "{0} والمزيد...", "AboutPageDeveloperListMore": "{0} والمزيد...",
"ApiError": "خطأ في API.", "ApiError": "خطأ في API.",
"LoadingHeading": "جارٍ تحميل {0}", "LoadingHeading": "جاري تحميل {0}",
"CompilingPPTC": "تجميع الـ PTC", "CompilingPPTC": "تجميع الـ(PPTC)",
"CompilingShaders": "تجميع الظلال", "CompilingShaders": "تجميع المظللات",
"AllKeyboards": "كل لوحات المفاتيح", "AllKeyboards": "كل لوحات المفاتيح",
"OpenFileDialogTitle": "حدد ملف مدعوم لفتحه", "OpenFileDialogTitle": "حدد ملف مدعوم لفتحه",
"OpenFolderDialogTitle": "حدد مجلدًا يحتوي على لعبة غير مضغوطة", "OpenFolderDialogTitle": "حدد مجلدا يحتوي على لعبة غير مضغوطة",
"AllSupportedFormats": "كل التنسيقات المدعومة", "AllSupportedFormats": "كل التنسيقات المدعومة",
"RyujinxUpdater": "تحديث Ryujinx", "RyujinxUpdater": "محدث ريوجينكس",
"SettingsTabHotkeys": "مفاتيح الاختصار في لوحة المفاتيح", "SettingsTabHotkeys": "مفاتيح الاختصار في لوحة المفاتيح",
"SettingsTabHotkeysHotkeys": "مفاتيح الاختصار في لوحة المفاتيح", "SettingsTabHotkeysHotkeys": "مفاتيح الاختصار في لوحة المفاتيح",
"SettingsTabHotkeysToggleVsyncHotkey": "تبديل VSync:", "SettingsTabHotkeysToggleVsyncHotkey": "تبديل المزامنة العمودية:",
"SettingsTabHotkeysScreenshotHotkey": "لقطة الشاشة:", "SettingsTabHotkeysScreenshotHotkey": "لقطة الشاشة:",
"SettingsTabHotkeysShowUiHotkey": "عرض واجهة المستخدم:", "SettingsTabHotkeysShowUiHotkey": "عرض واجهة المستخدم:",
"SettingsTabHotkeysPauseHotkey": "إيقاف مؤقت:", "SettingsTabHotkeysPauseHotkey": "إيقاف مؤقت:",
"SettingsTabHotkeysToggleMuteHotkey": "كتم الصوت:", "SettingsTabHotkeysToggleMuteHotkey": "كتم:",
"ControllerMotionTitle": "إعدادات التحكم بالحركة", "ControllerMotionTitle": "إعدادات التحكم بالحركة",
"ControllerRumbleTitle": "إعدادات الهزاز", "ControllerRumbleTitle": "إعدادات الهزاز",
"SettingsSelectThemeFileDialogTitle": "حدد ملف السمة", "SettingsSelectThemeFileDialogTitle": "حدد ملف السمة",
"SettingsXamlThemeFile": "Xaml Theme File", "SettingsXamlThemeFile": "ملف سمة Xaml",
"AvatarWindowTitle": "إدارة الحسابات - الصورة الرمزية", "AvatarWindowTitle": "إدارة الحسابات - الصورة الرمزية",
"Amiibo": "أميبو", "Amiibo": "أميبو",
"Unknown": "غير معروف", "Unknown": "غير معروف",
"Usage": "الاستخدام", "Usage": "الاستخدام",
"Writable": "قابل للكتابة", "Writable": "قابل للكتابة",
"SelectDlcDialogTitle": "حدد ملفات DLC", "SelectDlcDialogTitle": "حدد ملفات المحتوي الإضافي",
"SelectUpdateDialogTitle": "حدد ملفات التحديث", "SelectUpdateDialogTitle": "حدد ملفات التحديث",
"SelectModDialogTitle": "حدد مجلد التعديل", "SelectModDialogTitle": "حدد مجلد التعديل",
"UserProfileWindowTitle": "مدير ملفات تعريف المستخدمين", "UserProfileWindowTitle": "مدير الملفات الشخصية للمستخدمين",
"CheatWindowTitle": "مدير الغش", "CheatWindowTitle": "مدير الغش",
"DlcWindowTitle": "إدارة المحتوى القابل للتنزيل لـ {0} ({1})", "DlcWindowTitle": "إدارة المحتوى القابل للتنزيل لـ {0} ({1})",
"ModWindowTitle": "إدارة التعديلات لـ {0} ({1})",
"UpdateWindowTitle": "مدير تحديث العنوان", "UpdateWindowTitle": "مدير تحديث العنوان",
"CheatWindowHeading": "الغش متوفر لـ {0} [{1}]", "CheatWindowHeading": "الغش متوفر لـ {0} [{1}]",
"BuildId": "معرف البناء:", "BuildId": "معرف البناء:",
"DlcWindowHeading": "المحتويات القابلة للتنزيل {0}", "DlcWindowHeading": "المحتويات القابلة للتنزيل {0}",
"ModWindowHeading": "{0} تعديل", "ModWindowHeading": "{0} تعديل",
"UserProfilesEditProfile": "تعديل المحددة", "UserProfilesEditProfile": "تعديل المحدد",
"Cancel": "إلغاء", "Cancel": "إلغاء",
"Save": "حفظ", "Save": "حفظ",
"Discard": "تجاهل", "Discard": "تجاهل",
"Paused": "متوقف مؤقتا", "Paused": "متوقف مؤقتا",
"UserProfilesSetProfileImage": "تعيين صورة ملف التعريف", "UserProfilesSetProfileImage": "تعيين صورة الملف الشخصي",
"UserProfileEmptyNameError": "الاسم مطلوب", "UserProfileEmptyNameError": "الاسم مطلوب",
"UserProfileNoImageError": "يجب تعيين صورة الملف الشخصي", "UserProfileNoImageError": "يجب تعيين صورة الملف الشخصي",
"GameUpdateWindowHeading": "إدارة التحديثات لـ {0} ({1})", "GameUpdateWindowHeading": "إدارة التحديثات لـ {0} ({1})",
@@ -616,22 +723,22 @@
"UserProfilesName": "الاسم:", "UserProfilesName": "الاسم:",
"UserProfilesUserId": "معرف المستخدم:", "UserProfilesUserId": "معرف المستخدم:",
"SettingsTabGraphicsBackend": "خلفية الرسومات", "SettingsTabGraphicsBackend": "خلفية الرسومات",
"SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", "SettingsTabGraphicsBackendTooltip": "حدد الواجهة الخلفية للرسومات التي سيتم استخدامها في المحاكي.\n\nيعد برنامج فولكان أفضل بشكل عام لجميع بطاقات الرسومات الحديثة، طالما أن برامج التشغيل الخاصة بها محدثة. يتميز فولكان أيضا بتجميع مظللات أسرع (أقل تقطيعا) على جميع بائعي وحدات معالجة الرسومات.\n\nقد يحقق أوبن جي أل نتائج أفضل على وحدات معالجة الرسومات إنفيديا القديمة، أو على وحدات معالجة الرسومات إي إم دي القديمة على لينكس، أو على وحدات معالجة الرسومات ذات ذاكرة الوصول العشوائي للفيديوالأقل، على الرغم من أن تعثرات تجميع المظللات ستكون أكبر.\n\nاضبط على فولكان إذا لم تكن متأكدا. اضبط على أوبن جي أل إذا كانت وحدة معالجة الرسومات الخاصة بك لا تدعم فولكان حتى مع أحدث برامج تشغيل الرسومات.",
"SettingsEnableTextureRecompression": "تمكين إعادة ضغط التكستر", "SettingsEnableTextureRecompression": "تمكين إعادة ضغط التكستر",
"SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", "SettingsEnableTextureRecompressionTooltip": "يضغط تكستر ASTC من أجل تقليل استخدام ذاكرة الوصول العشوائي للفيديو.\n\nتتضمن الألعاب التي تستخدم تنسيق النسيج هذا Astral Chain وBayonetta 3 وFire Emblem Engage وMetroid Prime Remastered وSuper Mario Bros. Wonder وThe Legend of Zelda: Tears of the Kingdom.\n\nمن المحتمل أن تتعطل بطاقات الرسومات التي تحتوي على 4 جيجا بايت من ذاكرة الوصول العشوائي للفيديو أو أقل في مرحلة ما أثناء تشغيل هذه الألعاب.\n\nقم بالتمكين فقط في حالة نفاد ذاكرة الوصول العشوائي للفيديو في الألعاب المذكورة أعلاه. اتركه معطلا إذا لم تكن متأكدا.",
"SettingsTabGraphicsPreferredGpu": "GPU المفضل", "SettingsTabGraphicsPreferredGpu": "وحدة معالجة الرسوميات المفضلة",
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.", "SettingsTabGraphicsPreferredGpuTooltip": "حدد بطاقة الرسومات التي سيتم استخدامها مع الواجهة الخلفية لرسومات فولكان.\n\nلا يؤثر على وحدة معالجة الرسومات التي سيستخدمها أوبن جي أل.\n\nاضبط على وحدة معالجة الرسومات التي تم وضع علامة عليها كـ \"dGPU\" إذا لم تكن متأكدًا. إذا لم يكن هناك واحد، اتركه.",
"SettingsAppRequiredRestartMessage": "مطلوب إعادة تشغيل Ryujinx", "SettingsAppRequiredRestartMessage": "مطلوب إعادة تشغيل ريوجينكس",
"SettingsGpuBackendRestartMessage": "Graphics Backend or GPU settings have been modified. This will require a restart to be applied", "SettingsGpuBackendRestartMessage": "تم تعديل إعدادات الواجهة الخلفية للرسومات أو وحدة معالجة الرسومات. سيتطلب هذا إعادة التشغيل ليتم تطبيقه",
"SettingsGpuBackendRestartSubMessage": "\n\nهل تريد إعادة التشغيل الآن؟", "SettingsGpuBackendRestartSubMessage": "\n\nهل تريد إعادة التشغيل الآن؟",
"RyujinxUpdaterMessage": "هل تريد تحديث Ryujinx إلى أحدث إصدار؟", "RyujinxUpdaterMessage": "هل تريد تحديث ريوجينكس إلى أحدث إصدار؟",
"SettingsTabHotkeysVolumeUpHotkey": "زيادة مستوى الصوت:", "SettingsTabHotkeysVolumeUpHotkey": "زيادة مستوى الصوت:",
"SettingsTabHotkeysVolumeDownHotkey": "خفض مستوى الصوت:", "SettingsTabHotkeysVolumeDownHotkey": "خفض مستوى الصوت:",
"SettingsEnableMacroHLE": "Enable Macro HLE", "SettingsEnableMacroHLE": "تمكين Maro HLE",
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.", "SettingsEnableMacroHLETooltip": "محاكاة عالية المستوى لكود مايكرو وحدة معالجة الرسوميات.\n\nيعمل على تحسين الأداء، ولكنه قد يسبب خللا رسوميا في بعض الألعاب.\n\nاتركه مفعلا إذا لم تكن متأكدا.",
"SettingsEnableColorSpacePassthrough": "Color Space Passthrough", "SettingsEnableColorSpacePassthrough": "عبور مساحة اللون",
"SettingsEnableColorSpacePassthroughTooltip": "Directs the Vulkan backend to pass through color information without specifying a color space. For users with wide gamut displays, this may result in more vibrant colors, at the cost of color correctness.", "SettingsEnableColorSpacePassthroughTooltip": "يوجه واجهة فولكان الخلفية لتمرير معلومات الألوان دون تحديد مساحة اللون. بالنسبة للمستخدمين الذين لديهم شاشات ذات نطاق واسع، قد يؤدي ذلك إلى الحصول على ألوان أكثر حيوية، على حساب صحة الألوان.",
"VolumeShort": "الحجم", "VolumeShort": "مستوى",
"UserProfilesManageSaves": "إدارة الحفظ", "UserProfilesManageSaves": "إدارة الحفظ",
"DeleteUserSave": "هل تريد حذف حفظ المستخدم لهذه اللعبة؟", "DeleteUserSave": "هل تريد حذف حفظ المستخدم لهذه اللعبة؟",
"IrreversibleActionNote": "هذا الإجراء لا يمكن التراجع عنه.", "IrreversibleActionNote": "هذا الإجراء لا يمكن التراجع عنه.",
@@ -642,12 +749,12 @@
"Search": "بحث", "Search": "بحث",
"UserProfilesRecoverLostAccounts": "استعادة الحسابات المفقودة", "UserProfilesRecoverLostAccounts": "استعادة الحسابات المفقودة",
"Recover": "استعادة", "Recover": "استعادة",
"UserProfilesRecoverHeading": "تم العثور على الحفظ للحسابات التالية", "UserProfilesRecoverHeading": "تم العثور على حفظ للحسابات التالية",
"UserProfilesRecoverEmptyList": "لا توجد ملفات تعريف لاستردادها", "UserProfilesRecoverEmptyList": "لا توجد ملفات شخصية لاستردادها",
"GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", "GraphicsAATooltip": "يتم تطبيق تنعيم الحواف على عرض اللعبة.\n\nسوف يقوم FXAA بتعتيم معظم الصورة، بينما سيحاول SMAA العثور على حواف خشنة وتنعيمها.\n\nلا ينصح باستخدامه مع فلتر FSR لتكبير.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على لا شيء إذا لم تكن متأكدا.",
"GraphicsAALabel": "تنعيم الحواف:", "GraphicsAALabel": "تنعيم الحواف:",
"GraphicsScalingFilterLabel": "فلتر التكبير:", "GraphicsScalingFilterLabel": "فلتر التكبير:",
"GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterTooltip": "اختر فلتر التكبير الذي سيتم تطبيقه عند استخدام مقياس الدقة.\n\nيعمل Bilinear بشكل جيد مع الألعاب ثلاثية الأبعاد وهو خيار افتراضي آمن.\n\nيوصى باستخدام Nearest لألعاب البكسل الفنية.\n\nFSR 1.0 هو مجرد مرشح توضيحي، ولا ينصح باستخدامه مع FXAA أو SMAA.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على Bilinear إذا لم تكن متأكدا.",
"GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterBilinear": "Bilinear",
"GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterNearest": "Nearest",
"GraphicsScalingFilterFsr": "FSR", "GraphicsScalingFilterFsr": "FSR",
@@ -660,14 +767,14 @@
"UserEditorTitle": "تعديل المستخدم", "UserEditorTitle": "تعديل المستخدم",
"UserEditorTitleCreate": "إنشاء مستخدم", "UserEditorTitleCreate": "إنشاء مستخدم",
"SettingsTabNetworkInterface": "واجهة الشبكة:", "SettingsTabNetworkInterface": "واجهة الشبكة:",
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", "NetworkInterfaceTooltip": "واجهة الشبكة مستخدمة لميزات LAN/LDN.\n\nبالاشتراك مع VPN أو XLink Kai ولعبة تدعم LAN، يمكن استخدامها لتزييف اتصال الشبكة نفسها عبر الإنترنت.\n\nاتركه على الافتراضي إذا لم تكن متأكدا.",
"NetworkInterfaceDefault": "افتراضي", "NetworkInterfaceDefault": "افتراضي",
"PackagingShaders": "Packaging Shaders", "PackagingShaders": "تعبئة المظللات",
"AboutChangelogButton": "عرض سجل التغييرات على GitHub", "AboutChangelogButton": "عرض سجل التغييرات على غيت هاب",
"AboutChangelogButtonTooltipMessage": "انقر لفتح سجل التغيير لهذا الإصدار في متصفحك الافتراضي.", "AboutChangelogButtonTooltipMessage": "انقر لفتح سجل التغيير لهذا الإصدار في متصفحك الافتراضي.",
"SettingsTabNetworkMultiplayer": "لعب جماعي", "SettingsTabNetworkMultiplayer": "لعب جماعي",
"MultiplayerMode": "النمط:", "MultiplayerMode": "الوضع:",
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", "MultiplayerModeTooltip": "تغيير وضع LDN متعدد اللاعبين.\n\nسوف يقوم LdnMitm بتعديل وظيفة اللعب المحلية/اللاسلكية المحلية في الألعاب لتعمل كما لو كانت شبكة LAN، مما يسمح باتصالات الشبكة المحلية نفسها مع محاكيات ريوجينكس الأخرى وأجهزة نينتندو سويتش المخترقة التي تم تثبيت وحدة ldn_mitm عليها.\n\nيتطلب وضع اللاعبين المتعددين أن يكون جميع اللاعبين على نفس إصدار اللعبة (على سبيل المثال، يتعذر على الإصدار 13.0.1 من سوبر سماش برذرز ألتميت الاتصال بالإصدار 13.0.0).\n\nاتركه معطلا إذا لم تكن متأكدا.",
"MultiplayerModeDisabled": "معطل", "MultiplayerModeDisabled": "معطل",
"MultiplayerModeLdnMitm": "ldn_mitm" "MultiplayerModeLdnMitm": "ldn_mitm"
} }

View File

@@ -30,6 +30,10 @@
"MenuBarToolsManageFileTypes": "Dateitypen verwalten", "MenuBarToolsManageFileTypes": "Dateitypen verwalten",
"MenuBarToolsInstallFileTypes": "Dateitypen installieren", "MenuBarToolsInstallFileTypes": "Dateitypen installieren",
"MenuBarToolsUninstallFileTypes": "Dateitypen deinstallieren", "MenuBarToolsUninstallFileTypes": "Dateitypen deinstallieren",
"MenuBarView": "_Ansicht",
"MenuBarViewWindow": "Fenstergröße",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_Hilfe", "MenuBarHelp": "_Hilfe",
"MenuBarHelpCheckForUpdates": "Nach Updates suchen", "MenuBarHelpCheckForUpdates": "Nach Updates suchen",
"MenuBarHelpAbout": "Über Ryujinx", "MenuBarHelpAbout": "Über Ryujinx",
@@ -92,6 +96,7 @@
"SettingsTabGeneralEnableDiscordRichPresence": "Aktiviere die Statusanzeige für Discord", "SettingsTabGeneralEnableDiscordRichPresence": "Aktiviere die Statusanzeige für Discord",
"SettingsTabGeneralCheckUpdatesOnLaunch": "Beim Start nach Updates suchen", "SettingsTabGeneralCheckUpdatesOnLaunch": "Beim Start nach Updates suchen",
"SettingsTabGeneralShowConfirmExitDialog": "Zeige den \"Beenden bestätigen\"-Dialog", "SettingsTabGeneralShowConfirmExitDialog": "Zeige den \"Beenden bestätigen\"-Dialog",
"SettingsTabGeneralRememberWindowState": "Fenstergröße/-position merken",
"SettingsTabGeneralHideCursor": "Mauszeiger ausblenden", "SettingsTabGeneralHideCursor": "Mauszeiger ausblenden",
"SettingsTabGeneralHideCursorNever": "Niemals", "SettingsTabGeneralHideCursorNever": "Niemals",
"SettingsTabGeneralHideCursorOnIdle": "Mauszeiger bei Inaktivität ausblenden", "SettingsTabGeneralHideCursorOnIdle": "Mauszeiger bei Inaktivität ausblenden",
@@ -266,6 +271,107 @@
"ControllerSettingsMotionGyroDeadzone": "Gyro-Deadzone:", "ControllerSettingsMotionGyroDeadzone": "Gyro-Deadzone:",
"ControllerSettingsSave": "Speichern", "ControllerSettingsSave": "Speichern",
"ControllerSettingsClose": "Schließen", "ControllerSettingsClose": "Schließen",
"KeyUnknown": "Unbekannt",
"KeyShiftLeft": "Shift Left",
"KeyShiftRight": "Shift Right",
"KeyControlLeft": "Ctrl Left",
"KeyMacControlLeft": "⌃ Left",
"KeyControlRight": "Ctrl Right",
"KeyMacControlRight": "⌃ Right",
"KeyAltLeft": "Alt Left",
"KeyMacAltLeft": "⌥ Left",
"KeyAltRight": "Alt Right",
"KeyMacAltRight": "⌥ Right",
"KeyWinLeft": "⊞ Left",
"KeyMacWinLeft": "⌘ Left",
"KeyWinRight": "⊞ Right",
"KeyMacWinRight": "⌘ Right",
"KeyMenu": "Menu",
"KeyUp": "Up",
"KeyDown": "Down",
"KeyLeft": "Left",
"KeyRight": "Right",
"KeyEnter": "Enter",
"KeyEscape": "Escape",
"KeySpace": "Space",
"KeyTab": "Tab",
"KeyBackSpace": "Backspace",
"KeyInsert": "Insert",
"KeyDelete": "Delete",
"KeyPageUp": "Page Up",
"KeyPageDown": "Page Down",
"KeyHome": "Home",
"KeyEnd": "End",
"KeyCapsLock": "Caps Lock",
"KeyScrollLock": "Scroll Lock",
"KeyPrintScreen": "Print Screen",
"KeyPause": "Pause",
"KeyNumLock": "Num Lock",
"KeyClear": "Clear",
"KeyKeypad0": "Keypad 0",
"KeyKeypad1": "Keypad 1",
"KeyKeypad2": "Keypad 2",
"KeyKeypad3": "Keypad 3",
"KeyKeypad4": "Keypad 4",
"KeyKeypad5": "Keypad 5",
"KeyKeypad6": "Keypad 6",
"KeyKeypad7": "Keypad 7",
"KeyKeypad8": "Keypad 8",
"KeyKeypad9": "Keypad 9",
"KeyKeypadDivide": "Keypad Divide",
"KeyKeypadMultiply": "Keypad Multiply",
"KeyKeypadSubtract": "Keypad Subtract",
"KeyKeypadAdd": "Keypad Add",
"KeyKeypadDecimal": "Keypad Decimal",
"KeyKeypadEnter": "Keypad Enter",
"KeyNumber0": "0",
"KeyNumber1": "1",
"KeyNumber2": "2",
"KeyNumber3": "3",
"KeyNumber4": "4",
"KeyNumber5": "5",
"KeyNumber6": "6",
"KeyNumber7": "7",
"KeyNumber8": "8",
"KeyNumber9": "9",
"KeyTilde": "~",
"KeyGrave": "`",
"KeyMinus": "-",
"KeyPlus": "+",
"KeyBracketLeft": "[",
"KeyBracketRight": "]",
"KeySemicolon": ";",
"KeyQuote": "\"",
"KeyComma": ",",
"KeyPeriod": ".",
"KeySlash": "/",
"KeyBackSlash": "\\",
"KeyUnbound": "Unbound",
"GamepadLeftStick": "L Stick Button",
"GamepadRightStick": "R Stick Button",
"GamepadLeftShoulder": "Left Shoulder",
"GamepadRightShoulder": "Right Shoulder",
"GamepadLeftTrigger": "Left Trigger",
"GamepadRightTrigger": "Right Trigger",
"GamepadDpadUp": "Up",
"GamepadDpadDown": "Down",
"GamepadDpadLeft": "Left",
"GamepadDpadRight": "Right",
"GamepadMinus": "-",
"GamepadPlus": "+",
"GamepadGuide": "Guide",
"GamepadMisc1": "Misc",
"GamepadPaddle1": "Paddle 1",
"GamepadPaddle2": "Paddle 2",
"GamepadPaddle3": "Paddle 3",
"GamepadPaddle4": "Paddle 4",
"GamepadTouchpad": "Touchpad",
"GamepadSingleLeftTrigger0": "Left Trigger 0",
"GamepadSingleRightTrigger0": "Right Trigger 0",
"GamepadSingleLeftTrigger1": "Left Trigger 1",
"GamepadSingleRightTrigger1": "Right Trigger 1",
"StickLeft": "Left Stick",
"StickRight": "Right Stick",
"UserProfilesSelectedUserProfile": "Ausgewähltes Profil:", "UserProfilesSelectedUserProfile": "Ausgewähltes Profil:",
"UserProfilesSaveProfileName": "Profilname speichern", "UserProfilesSaveProfileName": "Profilname speichern",
"UserProfilesChangeProfileImage": "Profilbild ändern", "UserProfilesChangeProfileImage": "Profilbild ändern",
@@ -597,6 +703,7 @@
"UserProfileWindowTitle": "Benutzerprofile verwalten", "UserProfileWindowTitle": "Benutzerprofile verwalten",
"CheatWindowTitle": "Spiel-Cheats verwalten", "CheatWindowTitle": "Spiel-Cheats verwalten",
"DlcWindowTitle": "Spiel-DLC verwalten", "DlcWindowTitle": "Spiel-DLC verwalten",
"ModWindowTitle": "Manage Mods for {0} ({1})",
"UpdateWindowTitle": "Spiel-Updates verwalten", "UpdateWindowTitle": "Spiel-Updates verwalten",
"CheatWindowHeading": "Cheats verfügbar für {0} [{1}]", "CheatWindowHeading": "Cheats verfügbar für {0} [{1}]",
"BuildId": "BuildId:", "BuildId": "BuildId:",

View File

@@ -30,6 +30,10 @@
"MenuBarToolsManageFileTypes": "Διαχείριση τύπων αρχείων", "MenuBarToolsManageFileTypes": "Διαχείριση τύπων αρχείων",
"MenuBarToolsInstallFileTypes": "Εγκαταστήσετε τύπους αρχείων.", "MenuBarToolsInstallFileTypes": "Εγκαταστήσετε τύπους αρχείων.",
"MenuBarToolsUninstallFileTypes": "Απεγκαταστήσετε τύπους αρχείων", "MenuBarToolsUninstallFileTypes": "Απεγκαταστήσετε τύπους αρχείων",
"MenuBarView": "_View",
"MenuBarViewWindow": "Window Size",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_Βοήθεια", "MenuBarHelp": "_Βοήθεια",
"MenuBarHelpCheckForUpdates": "Έλεγχος για Ενημερώσεις", "MenuBarHelpCheckForUpdates": "Έλεγχος για Ενημερώσεις",
"MenuBarHelpAbout": "Σχετικά με", "MenuBarHelpAbout": "Σχετικά με",
@@ -92,6 +96,7 @@
"SettingsTabGeneralEnableDiscordRichPresence": "Ενεργοποίηση Εμπλουτισμένης Παρουσίας Discord", "SettingsTabGeneralEnableDiscordRichPresence": "Ενεργοποίηση Εμπλουτισμένης Παρουσίας Discord",
"SettingsTabGeneralCheckUpdatesOnLaunch": "Έλεγχος για Ενημερώσεις στην Εκκίνηση", "SettingsTabGeneralCheckUpdatesOnLaunch": "Έλεγχος για Ενημερώσεις στην Εκκίνηση",
"SettingsTabGeneralShowConfirmExitDialog": "Εμφάνιση διαλόγου \"Επιβεβαίωση Εξόδου\".", "SettingsTabGeneralShowConfirmExitDialog": "Εμφάνιση διαλόγου \"Επιβεβαίωση Εξόδου\".",
"SettingsTabGeneralRememberWindowState": "Remember Window Size/Position",
"SettingsTabGeneralHideCursor": "Απόκρυψη Κέρσορα:", "SettingsTabGeneralHideCursor": "Απόκρυψη Κέρσορα:",
"SettingsTabGeneralHideCursorNever": "Ποτέ", "SettingsTabGeneralHideCursorNever": "Ποτέ",
"SettingsTabGeneralHideCursorOnIdle": "Απόκρυψη Δρομέα στην Αδράνεια", "SettingsTabGeneralHideCursorOnIdle": "Απόκρυψη Δρομέα στην Αδράνεια",
@@ -266,6 +271,107 @@
"ControllerSettingsMotionGyroDeadzone": "Νεκρή Ζώνη Γυροσκοπίου:", "ControllerSettingsMotionGyroDeadzone": "Νεκρή Ζώνη Γυροσκοπίου:",
"ControllerSettingsSave": "Αποθήκευση", "ControllerSettingsSave": "Αποθήκευση",
"ControllerSettingsClose": "Κλείσιμο", "ControllerSettingsClose": "Κλείσιμο",
"KeyUnknown": "Unknown",
"KeyShiftLeft": "Shift Left",
"KeyShiftRight": "Shift Right",
"KeyControlLeft": "Ctrl Left",
"KeyMacControlLeft": "⌃ Left",
"KeyControlRight": "Ctrl Right",
"KeyMacControlRight": "⌃ Right",
"KeyAltLeft": "Alt Left",
"KeyMacAltLeft": "⌥ Left",
"KeyAltRight": "Alt Right",
"KeyMacAltRight": "⌥ Right",
"KeyWinLeft": "⊞ Left",
"KeyMacWinLeft": "⌘ Left",
"KeyWinRight": "⊞ Right",
"KeyMacWinRight": "⌘ Right",
"KeyMenu": "Menu",
"KeyUp": "Up",
"KeyDown": "Down",
"KeyLeft": "Left",
"KeyRight": "Right",
"KeyEnter": "Enter",
"KeyEscape": "Escape",
"KeySpace": "Space",
"KeyTab": "Tab",
"KeyBackSpace": "Backspace",
"KeyInsert": "Insert",
"KeyDelete": "Delete",
"KeyPageUp": "Page Up",
"KeyPageDown": "Page Down",
"KeyHome": "Home",
"KeyEnd": "End",
"KeyCapsLock": "Caps Lock",
"KeyScrollLock": "Scroll Lock",
"KeyPrintScreen": "Print Screen",
"KeyPause": "Pause",
"KeyNumLock": "Num Lock",
"KeyClear": "Clear",
"KeyKeypad0": "Keypad 0",
"KeyKeypad1": "Keypad 1",
"KeyKeypad2": "Keypad 2",
"KeyKeypad3": "Keypad 3",
"KeyKeypad4": "Keypad 4",
"KeyKeypad5": "Keypad 5",
"KeyKeypad6": "Keypad 6",
"KeyKeypad7": "Keypad 7",
"KeyKeypad8": "Keypad 8",
"KeyKeypad9": "Keypad 9",
"KeyKeypadDivide": "Keypad Divide",
"KeyKeypadMultiply": "Keypad Multiply",
"KeyKeypadSubtract": "Keypad Subtract",
"KeyKeypadAdd": "Keypad Add",
"KeyKeypadDecimal": "Keypad Decimal",
"KeyKeypadEnter": "Keypad Enter",
"KeyNumber0": "0",
"KeyNumber1": "1",
"KeyNumber2": "2",
"KeyNumber3": "3",
"KeyNumber4": "4",
"KeyNumber5": "5",
"KeyNumber6": "6",
"KeyNumber7": "7",
"KeyNumber8": "8",
"KeyNumber9": "9",
"KeyTilde": "~",
"KeyGrave": "`",
"KeyMinus": "-",
"KeyPlus": "+",
"KeyBracketLeft": "[",
"KeyBracketRight": "]",
"KeySemicolon": ";",
"KeyQuote": "\"",
"KeyComma": ",",
"KeyPeriod": ".",
"KeySlash": "/",
"KeyBackSlash": "\\",
"KeyUnbound": "Unbound",
"GamepadLeftStick": "L Stick Button",
"GamepadRightStick": "R Stick Button",
"GamepadLeftShoulder": "Left Shoulder",
"GamepadRightShoulder": "Right Shoulder",
"GamepadLeftTrigger": "Left Trigger",
"GamepadRightTrigger": "Right Trigger",
"GamepadDpadUp": "Up",
"GamepadDpadDown": "Down",
"GamepadDpadLeft": "Left",
"GamepadDpadRight": "Right",
"GamepadMinus": "-",
"GamepadPlus": "+",
"GamepadGuide": "Guide",
"GamepadMisc1": "Misc",
"GamepadPaddle1": "Paddle 1",
"GamepadPaddle2": "Paddle 2",
"GamepadPaddle3": "Paddle 3",
"GamepadPaddle4": "Paddle 4",
"GamepadTouchpad": "Touchpad",
"GamepadSingleLeftTrigger0": "Left Trigger 0",
"GamepadSingleRightTrigger0": "Right Trigger 0",
"GamepadSingleLeftTrigger1": "Left Trigger 1",
"GamepadSingleRightTrigger1": "Right Trigger 1",
"StickLeft": "Left Stick",
"StickRight": "Right Stick",
"UserProfilesSelectedUserProfile": "Επιλεγμένο Προφίλ Χρήστη:", "UserProfilesSelectedUserProfile": "Επιλεγμένο Προφίλ Χρήστη:",
"UserProfilesSaveProfileName": "Αποθήκευση Ονόματος Προφίλ", "UserProfilesSaveProfileName": "Αποθήκευση Ονόματος Προφίλ",
"UserProfilesChangeProfileImage": "Αλλαγή Εικόνας Προφίλ", "UserProfilesChangeProfileImage": "Αλλαγή Εικόνας Προφίλ",
@@ -597,6 +703,7 @@
"UserProfileWindowTitle": "Διαχειριστής Προφίλ Χρήστη", "UserProfileWindowTitle": "Διαχειριστής Προφίλ Χρήστη",
"CheatWindowTitle": "Διαχειριστής των Cheats", "CheatWindowTitle": "Διαχειριστής των Cheats",
"DlcWindowTitle": "Downloadable Content Manager", "DlcWindowTitle": "Downloadable Content Manager",
"ModWindowTitle": "Manage Mods for {0} ({1})",
"UpdateWindowTitle": "Διαχειριστής Ενημερώσεων Τίτλου", "UpdateWindowTitle": "Διαχειριστής Ενημερώσεων Τίτλου",
"CheatWindowHeading": "Διαθέσιμα Cheats για {0} [{1}]", "CheatWindowHeading": "Διαθέσιμα Cheats για {0} [{1}]",
"BuildId": "BuildId:", "BuildId": "BuildId:",

View File

@@ -30,6 +30,10 @@
"MenuBarToolsManageFileTypes": "Manage file types", "MenuBarToolsManageFileTypes": "Manage file types",
"MenuBarToolsInstallFileTypes": "Install file types", "MenuBarToolsInstallFileTypes": "Install file types",
"MenuBarToolsUninstallFileTypes": "Uninstall file types", "MenuBarToolsUninstallFileTypes": "Uninstall file types",
"MenuBarView": "_View",
"MenuBarViewWindow": "Window Size",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_Help", "MenuBarHelp": "_Help",
"MenuBarHelpCheckForUpdates": "Check for Updates", "MenuBarHelpCheckForUpdates": "Check for Updates",
"MenuBarHelpAbout": "About", "MenuBarHelpAbout": "About",
@@ -92,6 +96,7 @@
"SettingsTabGeneralEnableDiscordRichPresence": "Enable Discord Rich Presence", "SettingsTabGeneralEnableDiscordRichPresence": "Enable Discord Rich Presence",
"SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch", "SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch",
"SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog", "SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog",
"SettingsTabGeneralRememberWindowState": "Remember Window Size/Position",
"SettingsTabGeneralHideCursor": "Hide Cursor:", "SettingsTabGeneralHideCursor": "Hide Cursor:",
"SettingsTabGeneralHideCursorNever": "Never", "SettingsTabGeneralHideCursorNever": "Never",
"SettingsTabGeneralHideCursorOnIdle": "On Idle", "SettingsTabGeneralHideCursorOnIdle": "On Idle",
@@ -266,6 +271,107 @@
"ControllerSettingsMotionGyroDeadzone": "Gyro Deadzone:", "ControllerSettingsMotionGyroDeadzone": "Gyro Deadzone:",
"ControllerSettingsSave": "Save", "ControllerSettingsSave": "Save",
"ControllerSettingsClose": "Close", "ControllerSettingsClose": "Close",
"KeyUnknown": "Unknown",
"KeyShiftLeft": "Shift Left",
"KeyShiftRight": "Shift Right",
"KeyControlLeft": "Ctrl Left",
"KeyMacControlLeft": "⌃ Left",
"KeyControlRight": "Ctrl Right",
"KeyMacControlRight": "⌃ Right",
"KeyAltLeft": "Alt Left",
"KeyMacAltLeft": "⌥ Left",
"KeyAltRight": "Alt Right",
"KeyMacAltRight": "⌥ Right",
"KeyWinLeft": "⊞ Left",
"KeyMacWinLeft": "⌘ Left",
"KeyWinRight": "⊞ Right",
"KeyMacWinRight": "⌘ Right",
"KeyMenu": "Menu",
"KeyUp": "Up",
"KeyDown": "Down",
"KeyLeft": "Left",
"KeyRight": "Right",
"KeyEnter": "Enter",
"KeyEscape": "Escape",
"KeySpace": "Space",
"KeyTab": "Tab",
"KeyBackSpace": "Backspace",
"KeyInsert": "Insert",
"KeyDelete": "Delete",
"KeyPageUp": "Page Up",
"KeyPageDown": "Page Down",
"KeyHome": "Home",
"KeyEnd": "End",
"KeyCapsLock": "Caps Lock",
"KeyScrollLock": "Scroll Lock",
"KeyPrintScreen": "Print Screen",
"KeyPause": "Pause",
"KeyNumLock": "Num Lock",
"KeyClear": "Clear",
"KeyKeypad0": "Keypad 0",
"KeyKeypad1": "Keypad 1",
"KeyKeypad2": "Keypad 2",
"KeyKeypad3": "Keypad 3",
"KeyKeypad4": "Keypad 4",
"KeyKeypad5": "Keypad 5",
"KeyKeypad6": "Keypad 6",
"KeyKeypad7": "Keypad 7",
"KeyKeypad8": "Keypad 8",
"KeyKeypad9": "Keypad 9",
"KeyKeypadDivide": "Keypad Divide",
"KeyKeypadMultiply": "Keypad Multiply",
"KeyKeypadSubtract": "Keypad Subtract",
"KeyKeypadAdd": "Keypad Add",
"KeyKeypadDecimal": "Keypad Decimal",
"KeyKeypadEnter": "Keypad Enter",
"KeyNumber0": "0",
"KeyNumber1": "1",
"KeyNumber2": "2",
"KeyNumber3": "3",
"KeyNumber4": "4",
"KeyNumber5": "5",
"KeyNumber6": "6",
"KeyNumber7": "7",
"KeyNumber8": "8",
"KeyNumber9": "9",
"KeyTilde": "~",
"KeyGrave": "`",
"KeyMinus": "-",
"KeyPlus": "+",
"KeyBracketLeft": "[",
"KeyBracketRight": "]",
"KeySemicolon": ";",
"KeyQuote": "\"",
"KeyComma": ",",
"KeyPeriod": ".",
"KeySlash": "/",
"KeyBackSlash": "\\",
"KeyUnbound": "Unbound",
"GamepadLeftStick": "L Stick Button",
"GamepadRightStick": "R Stick Button",
"GamepadLeftShoulder": "Left Shoulder",
"GamepadRightShoulder": "Right Shoulder",
"GamepadLeftTrigger": "Left Trigger",
"GamepadRightTrigger": "Right Trigger",
"GamepadDpadUp": "Up",
"GamepadDpadDown": "Down",
"GamepadDpadLeft": "Left",
"GamepadDpadRight": "Right",
"GamepadMinus": "-",
"GamepadPlus": "+",
"GamepadGuide": "Guide",
"GamepadMisc1": "Misc",
"GamepadPaddle1": "Paddle 1",
"GamepadPaddle2": "Paddle 2",
"GamepadPaddle3": "Paddle 3",
"GamepadPaddle4": "Paddle 4",
"GamepadTouchpad": "Touchpad",
"GamepadSingleLeftTrigger0": "Left Trigger 0",
"GamepadSingleRightTrigger0": "Right Trigger 0",
"GamepadSingleLeftTrigger1": "Left Trigger 1",
"GamepadSingleRightTrigger1": "Right Trigger 1",
"StickLeft": "Left Stick",
"StickRight": "Right Stick",
"UserProfilesSelectedUserProfile": "Selected User Profile:", "UserProfilesSelectedUserProfile": "Selected User Profile:",
"UserProfilesSaveProfileName": "Save Profile Name", "UserProfilesSaveProfileName": "Save Profile Name",
"UserProfilesChangeProfileImage": "Change Profile Image", "UserProfilesChangeProfileImage": "Change Profile Image",
@@ -298,6 +404,7 @@
"GameListContextMenuToggleFavorite": "Toggle Favorite", "GameListContextMenuToggleFavorite": "Toggle Favorite",
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game", "GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
"SettingsTabGeneralTheme": "Theme:", "SettingsTabGeneralTheme": "Theme:",
"SettingsTabGeneralThemeAuto": "Auto",
"SettingsTabGeneralThemeDark": "Dark", "SettingsTabGeneralThemeDark": "Dark",
"SettingsTabGeneralThemeLight": "Light", "SettingsTabGeneralThemeLight": "Light",
"ControllerSettingsConfigureGeneral": "Configure", "ControllerSettingsConfigureGeneral": "Configure",

View File

@@ -30,6 +30,10 @@
"MenuBarToolsManageFileTypes": "Administrar tipos de archivo", "MenuBarToolsManageFileTypes": "Administrar tipos de archivo",
"MenuBarToolsInstallFileTypes": "Instalar tipos de archivo", "MenuBarToolsInstallFileTypes": "Instalar tipos de archivo",
"MenuBarToolsUninstallFileTypes": "Desinstalar tipos de archivo", "MenuBarToolsUninstallFileTypes": "Desinstalar tipos de archivo",
"MenuBarView": "_View",
"MenuBarViewWindow": "Window Size",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_Ayuda", "MenuBarHelp": "_Ayuda",
"MenuBarHelpCheckForUpdates": "Buscar actualizaciones", "MenuBarHelpCheckForUpdates": "Buscar actualizaciones",
"MenuBarHelpAbout": "Acerca de", "MenuBarHelpAbout": "Acerca de",
@@ -92,6 +96,7 @@
"SettingsTabGeneralEnableDiscordRichPresence": "Habilitar estado en Discord", "SettingsTabGeneralEnableDiscordRichPresence": "Habilitar estado en Discord",
"SettingsTabGeneralCheckUpdatesOnLaunch": "Buscar actualizaciones al iniciar", "SettingsTabGeneralCheckUpdatesOnLaunch": "Buscar actualizaciones al iniciar",
"SettingsTabGeneralShowConfirmExitDialog": "Mostrar diálogo de confirmación al cerrar", "SettingsTabGeneralShowConfirmExitDialog": "Mostrar diálogo de confirmación al cerrar",
"SettingsTabGeneralRememberWindowState": "Remember Window Size/Position",
"SettingsTabGeneralHideCursor": "Esconder el cursor:", "SettingsTabGeneralHideCursor": "Esconder el cursor:",
"SettingsTabGeneralHideCursorNever": "Nunca", "SettingsTabGeneralHideCursorNever": "Nunca",
"SettingsTabGeneralHideCursorOnIdle": "Ocultar cursor cuando esté inactivo", "SettingsTabGeneralHideCursorOnIdle": "Ocultar cursor cuando esté inactivo",
@@ -155,7 +160,7 @@
"SettingsTabGraphicsResolutionScaleNative": "Nativa (720p/1080p)", "SettingsTabGraphicsResolutionScaleNative": "Nativa (720p/1080p)",
"SettingsTabGraphicsResolutionScale2x": "x2 (1440p/2160p)", "SettingsTabGraphicsResolutionScale2x": "x2 (1440p/2160p)",
"SettingsTabGraphicsResolutionScale3x": "x3 (2160p/3240p)", "SettingsTabGraphicsResolutionScale3x": "x3 (2160p/3240p)",
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)", "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (no recomendado)",
"SettingsTabGraphicsAspectRatio": "Relación de aspecto:", "SettingsTabGraphicsAspectRatio": "Relación de aspecto:",
"SettingsTabGraphicsAspectRatio4x3": "4:3", "SettingsTabGraphicsAspectRatio4x3": "4:3",
"SettingsTabGraphicsAspectRatio16x9": "16:9", "SettingsTabGraphicsAspectRatio16x9": "16:9",
@@ -266,6 +271,107 @@
"ControllerSettingsMotionGyroDeadzone": "Zona muerta de Gyro:", "ControllerSettingsMotionGyroDeadzone": "Zona muerta de Gyro:",
"ControllerSettingsSave": "Guardar", "ControllerSettingsSave": "Guardar",
"ControllerSettingsClose": "Cerrar", "ControllerSettingsClose": "Cerrar",
"KeyUnknown": "Desconocido",
"KeyShiftLeft": "Shift Left",
"KeyShiftRight": "Shift Right",
"KeyControlLeft": "Ctrl Left",
"KeyMacControlLeft": "⌃ Left",
"KeyControlRight": "Ctrl Right",
"KeyMacControlRight": "⌃ Right",
"KeyAltLeft": "Alt Left",
"KeyMacAltLeft": "⌥ Left",
"KeyAltRight": "Alt Right",
"KeyMacAltRight": "⌥ Right",
"KeyWinLeft": "⊞ Left",
"KeyMacWinLeft": "⌘ Left",
"KeyWinRight": "⊞ Right",
"KeyMacWinRight": "⌘ Right",
"KeyMenu": "Menu",
"KeyUp": "Up",
"KeyDown": "Down",
"KeyLeft": "Left",
"KeyRight": "Right",
"KeyEnter": "Enter",
"KeyEscape": "Escape",
"KeySpace": "Space",
"KeyTab": "Tab",
"KeyBackSpace": "Backspace",
"KeyInsert": "Insert",
"KeyDelete": "Delete",
"KeyPageUp": "Page Up",
"KeyPageDown": "Page Down",
"KeyHome": "Home",
"KeyEnd": "End",
"KeyCapsLock": "Caps Lock",
"KeyScrollLock": "Scroll Lock",
"KeyPrintScreen": "Print Screen",
"KeyPause": "Pause",
"KeyNumLock": "Num Lock",
"KeyClear": "Clear",
"KeyKeypad0": "Keypad 0",
"KeyKeypad1": "Keypad 1",
"KeyKeypad2": "Keypad 2",
"KeyKeypad3": "Keypad 3",
"KeyKeypad4": "Keypad 4",
"KeyKeypad5": "Keypad 5",
"KeyKeypad6": "Keypad 6",
"KeyKeypad7": "Keypad 7",
"KeyKeypad8": "Keypad 8",
"KeyKeypad9": "Keypad 9",
"KeyKeypadDivide": "Keypad Divide",
"KeyKeypadMultiply": "Keypad Multiply",
"KeyKeypadSubtract": "Keypad Subtract",
"KeyKeypadAdd": "Keypad Add",
"KeyKeypadDecimal": "Keypad Decimal",
"KeyKeypadEnter": "Keypad Enter",
"KeyNumber0": "0",
"KeyNumber1": "1",
"KeyNumber2": "2",
"KeyNumber3": "3",
"KeyNumber4": "4",
"KeyNumber5": "5",
"KeyNumber6": "6",
"KeyNumber7": "7",
"KeyNumber8": "8",
"KeyNumber9": "9",
"KeyTilde": "~",
"KeyGrave": "`",
"KeyMinus": "-",
"KeyPlus": "+",
"KeyBracketLeft": "[",
"KeyBracketRight": "]",
"KeySemicolon": ";",
"KeyQuote": "\"",
"KeyComma": ",",
"KeyPeriod": ".",
"KeySlash": "/",
"KeyBackSlash": "\\",
"KeyUnbound": "Unbound",
"GamepadLeftStick": "L Stick Button",
"GamepadRightStick": "R Stick Button",
"GamepadLeftShoulder": "Left Shoulder",
"GamepadRightShoulder": "Right Shoulder",
"GamepadLeftTrigger": "Left Trigger",
"GamepadRightTrigger": "Right Trigger",
"GamepadDpadUp": "Up",
"GamepadDpadDown": "Down",
"GamepadDpadLeft": "Left",
"GamepadDpadRight": "Right",
"GamepadMinus": "-",
"GamepadPlus": "+",
"GamepadGuide": "Guide",
"GamepadMisc1": "Misc",
"GamepadPaddle1": "Paddle 1",
"GamepadPaddle2": "Paddle 2",
"GamepadPaddle3": "Paddle 3",
"GamepadPaddle4": "Paddle 4",
"GamepadTouchpad": "Touchpad",
"GamepadSingleLeftTrigger0": "Left Trigger 0",
"GamepadSingleRightTrigger0": "Right Trigger 0",
"GamepadSingleLeftTrigger1": "Left Trigger 1",
"GamepadSingleRightTrigger1": "Right Trigger 1",
"StickLeft": "Left Stick",
"StickRight": "Right Stick",
"UserProfilesSelectedUserProfile": "Perfil de usuario seleccionado:", "UserProfilesSelectedUserProfile": "Perfil de usuario seleccionado:",
"UserProfilesSaveProfileName": "Guardar nombre de perfil", "UserProfilesSaveProfileName": "Guardar nombre de perfil",
"UserProfilesChangeProfileImage": "Cambiar imagen de perfil", "UserProfilesChangeProfileImage": "Cambiar imagen de perfil",
@@ -597,6 +703,7 @@
"UserProfileWindowTitle": "Administrar perfiles de usuario", "UserProfileWindowTitle": "Administrar perfiles de usuario",
"CheatWindowTitle": "Administrar cheats", "CheatWindowTitle": "Administrar cheats",
"DlcWindowTitle": "Administrar contenido descargable", "DlcWindowTitle": "Administrar contenido descargable",
"ModWindowTitle": "Manage Mods for {0} ({1})",
"UpdateWindowTitle": "Administrar actualizaciones", "UpdateWindowTitle": "Administrar actualizaciones",
"CheatWindowHeading": "Cheats disponibles para {0} [{1}]", "CheatWindowHeading": "Cheats disponibles para {0} [{1}]",
"BuildId": "Id de compilación:", "BuildId": "Id de compilación:",
@@ -647,12 +754,12 @@
"GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
"GraphicsAALabel": "Suavizado de bordes:", "GraphicsAALabel": "Suavizado de bordes:",
"GraphicsScalingFilterLabel": "Filtro de escalado:", "GraphicsScalingFilterLabel": "Filtro de escalado:",
"GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "GraphicsScalingFilterTooltip": "Elija el filtro de escala que se aplicará al utilizar la escala de resolución.\n\nBilinear funciona bien para juegos 3D y es una opción predeterminada segura.\n\nSe recomienda el bilinear para juegos de pixel art.\n\nFSR 1.0 es simplemente un filtro de afilado, no se recomienda su uso con FXAA o SMAA.\n\nEsta opción se puede cambiar mientras se ejecuta un juego haciendo clic en \"Aplicar\" a continuación; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDéjelo en BILINEAR si no está seguro.",
"GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterBilinear": "Bilinear\n",
"GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterNearest": "Cercano",
"GraphicsScalingFilterFsr": "FSR", "GraphicsScalingFilterFsr": "FSR",
"GraphicsScalingFilterLevelLabel": "Nivel", "GraphicsScalingFilterLevelLabel": "Nivel",
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", "GraphicsScalingFilterLevelTooltip": "Ajuste el nivel de nitidez FSR 1.0. Mayor es más nítido.",
"SmaaLow": "SMAA Bajo", "SmaaLow": "SMAA Bajo",
"SmaaMedium": "SMAA Medio", "SmaaMedium": "SMAA Medio",
"SmaaHigh": "SMAA Alto", "SmaaHigh": "SMAA Alto",
@@ -660,14 +767,14 @@
"UserEditorTitle": "Editar usuario", "UserEditorTitle": "Editar usuario",
"UserEditorTitleCreate": "Crear Usuario", "UserEditorTitleCreate": "Crear Usuario",
"SettingsTabNetworkInterface": "Interfaz de Red", "SettingsTabNetworkInterface": "Interfaz de Red",
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", "NetworkInterfaceTooltip": "Interfaz de red usada para características LAN/LDN.\n\njunto con una VPN o XLink Kai y un juego con soporte LAN, puede usarse para suplantar una conexión de la misma red a través de Internet.\n\nDeje en DEFAULT si no está seguro.",
"NetworkInterfaceDefault": "Predeterminado", "NetworkInterfaceDefault": "Predeterminado",
"PackagingShaders": "Empaquetando sombreadores", "PackagingShaders": "Empaquetando sombreadores",
"AboutChangelogButton": "Ver registro de cambios en GitHub", "AboutChangelogButton": "Ver registro de cambios en GitHub",
"AboutChangelogButtonTooltipMessage": "Haga clic para abrir el registro de cambios para esta versión en su navegador predeterminado.", "AboutChangelogButtonTooltipMessage": "Haga clic para abrir el registro de cambios para esta versión en su navegador predeterminado.",
"SettingsTabNetworkMultiplayer": "Multijugador", "SettingsTabNetworkMultiplayer": "Multijugador",
"MultiplayerMode": "Modo:", "MultiplayerMode": "Modo:",
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", "MultiplayerModeTooltip": "Cambiar modo LDN multijugador.\n\nLdnMitm modificará la funcionalidad local de juego inalámbrico para funcionar como si fuera LAN, permitiendo locales conexiones de la misma red con otras instancias de Ryujinx y consolas hackeadas de Nintendo Switch que tienen instalado el módulo ldn_mitm.\n\nMultijugador requiere que todos los jugadores estén en la misma versión del juego (por ejemplo, Super Smash Bros. Ultimate v13.0.1 no se puede conectar a v13.0.0).\n\nDejar DESACTIVADO si no está seguro.",
"MultiplayerModeDisabled": "Disabled", "MultiplayerModeDisabled": "Deshabilitar",
"MultiplayerModeLdnMitm": "ldn_mitm" "MultiplayerModeLdnMitm": "ldn_mitm"
} }

View File

@@ -30,6 +30,10 @@
"MenuBarToolsManageFileTypes": "Gérer les types de fichiers", "MenuBarToolsManageFileTypes": "Gérer les types de fichiers",
"MenuBarToolsInstallFileTypes": "Installer les types de fichiers", "MenuBarToolsInstallFileTypes": "Installer les types de fichiers",
"MenuBarToolsUninstallFileTypes": "Désinstaller les types de fichiers", "MenuBarToolsUninstallFileTypes": "Désinstaller les types de fichiers",
"MenuBarView": "_View",
"MenuBarViewWindow": "Window Size",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_Aide", "MenuBarHelp": "_Aide",
"MenuBarHelpCheckForUpdates": "Vérifier les mises à jour", "MenuBarHelpCheckForUpdates": "Vérifier les mises à jour",
"MenuBarHelpAbout": "À propos", "MenuBarHelpAbout": "À propos",
@@ -92,6 +96,7 @@
"SettingsTabGeneralEnableDiscordRichPresence": "Activer Discord Rich Presence", "SettingsTabGeneralEnableDiscordRichPresence": "Activer Discord Rich Presence",
"SettingsTabGeneralCheckUpdatesOnLaunch": "Vérifier les mises à jour au démarrage", "SettingsTabGeneralCheckUpdatesOnLaunch": "Vérifier les mises à jour au démarrage",
"SettingsTabGeneralShowConfirmExitDialog": "Afficher le message de \"Confirmation de sortie\"", "SettingsTabGeneralShowConfirmExitDialog": "Afficher le message de \"Confirmation de sortie\"",
"SettingsTabGeneralRememberWindowState": "Remember Window Size/Position",
"SettingsTabGeneralHideCursor": "Masquer le Curseur :", "SettingsTabGeneralHideCursor": "Masquer le Curseur :",
"SettingsTabGeneralHideCursorNever": "Jamais", "SettingsTabGeneralHideCursorNever": "Jamais",
"SettingsTabGeneralHideCursorOnIdle": "Masquer le curseur si inactif", "SettingsTabGeneralHideCursorOnIdle": "Masquer le curseur si inactif",
@@ -266,6 +271,107 @@
"ControllerSettingsMotionGyroDeadzone": "Zone morte du gyroscope:", "ControllerSettingsMotionGyroDeadzone": "Zone morte du gyroscope:",
"ControllerSettingsSave": "Enregistrer", "ControllerSettingsSave": "Enregistrer",
"ControllerSettingsClose": "Fermer", "ControllerSettingsClose": "Fermer",
"KeyUnknown": "Unknown",
"KeyShiftLeft": "Shift Left",
"KeyShiftRight": "Shift Right",
"KeyControlLeft": "Ctrl Left",
"KeyMacControlLeft": "⌃ Left",
"KeyControlRight": "Ctrl Right",
"KeyMacControlRight": "⌃ Right",
"KeyAltLeft": "Alt Left",
"KeyMacAltLeft": "⌥ Left",
"KeyAltRight": "Alt Right",
"KeyMacAltRight": "⌥ Right",
"KeyWinLeft": "⊞ Left",
"KeyMacWinLeft": "⌘ Left",
"KeyWinRight": "⊞ Right",
"KeyMacWinRight": "⌘ Right",
"KeyMenu": "Menu",
"KeyUp": "Up",
"KeyDown": "Down",
"KeyLeft": "Left",
"KeyRight": "Right",
"KeyEnter": "Enter",
"KeyEscape": "Escape",
"KeySpace": "Space",
"KeyTab": "Tab",
"KeyBackSpace": "Backspace",
"KeyInsert": "Insert",
"KeyDelete": "Delete",
"KeyPageUp": "Page Up",
"KeyPageDown": "Page Down",
"KeyHome": "Home",
"KeyEnd": "End",
"KeyCapsLock": "Caps Lock",
"KeyScrollLock": "Scroll Lock",
"KeyPrintScreen": "Print Screen",
"KeyPause": "Pause",
"KeyNumLock": "Num Lock",
"KeyClear": "Clear",
"KeyKeypad0": "Keypad 0",
"KeyKeypad1": "Keypad 1",
"KeyKeypad2": "Keypad 2",
"KeyKeypad3": "Keypad 3",
"KeyKeypad4": "Keypad 4",
"KeyKeypad5": "Keypad 5",
"KeyKeypad6": "Keypad 6",
"KeyKeypad7": "Keypad 7",
"KeyKeypad8": "Keypad 8",
"KeyKeypad9": "Keypad 9",
"KeyKeypadDivide": "Keypad Divide",
"KeyKeypadMultiply": "Keypad Multiply",
"KeyKeypadSubtract": "Keypad Subtract",
"KeyKeypadAdd": "Keypad Add",
"KeyKeypadDecimal": "Keypad Decimal",
"KeyKeypadEnter": "Keypad Enter",
"KeyNumber0": "0",
"KeyNumber1": "1",
"KeyNumber2": "2",
"KeyNumber3": "3",
"KeyNumber4": "4",
"KeyNumber5": "5",
"KeyNumber6": "6",
"KeyNumber7": "7",
"KeyNumber8": "8",
"KeyNumber9": "9",
"KeyTilde": "~",
"KeyGrave": "`",
"KeyMinus": "-",
"KeyPlus": "+",
"KeyBracketLeft": "[",
"KeyBracketRight": "]",
"KeySemicolon": ";",
"KeyQuote": "\"",
"KeyComma": ",",
"KeyPeriod": ".",
"KeySlash": "/",
"KeyBackSlash": "\\",
"KeyUnbound": "Unbound",
"GamepadLeftStick": "L Stick Button",
"GamepadRightStick": "R Stick Button",
"GamepadLeftShoulder": "Left Shoulder",
"GamepadRightShoulder": "Right Shoulder",
"GamepadLeftTrigger": "Left Trigger",
"GamepadRightTrigger": "Right Trigger",
"GamepadDpadUp": "Up",
"GamepadDpadDown": "Down",
"GamepadDpadLeft": "Left",
"GamepadDpadRight": "Right",
"GamepadMinus": "-",
"GamepadPlus": "+",
"GamepadGuide": "Guide",
"GamepadMisc1": "Misc",
"GamepadPaddle1": "Paddle 1",
"GamepadPaddle2": "Paddle 2",
"GamepadPaddle3": "Paddle 3",
"GamepadPaddle4": "Paddle 4",
"GamepadTouchpad": "Touchpad",
"GamepadSingleLeftTrigger0": "Left Trigger 0",
"GamepadSingleRightTrigger0": "Right Trigger 0",
"GamepadSingleLeftTrigger1": "Left Trigger 1",
"GamepadSingleRightTrigger1": "Right Trigger 1",
"StickLeft": "Left Stick",
"StickRight": "Right Stick",
"UserProfilesSelectedUserProfile": "Profil utilisateur sélectionné :", "UserProfilesSelectedUserProfile": "Profil utilisateur sélectionné :",
"UserProfilesSaveProfileName": "Enregistrer le nom du profil", "UserProfilesSaveProfileName": "Enregistrer le nom du profil",
"UserProfilesChangeProfileImage": "Changer l'image du profil", "UserProfilesChangeProfileImage": "Changer l'image du profil",
@@ -597,6 +703,7 @@
"UserProfileWindowTitle": "Gestionnaire de profils utilisateur", "UserProfileWindowTitle": "Gestionnaire de profils utilisateur",
"CheatWindowTitle": "Gestionnaire de triches", "CheatWindowTitle": "Gestionnaire de triches",
"DlcWindowTitle": "Gérer le contenu téléchargeable pour {0} ({1})", "DlcWindowTitle": "Gérer le contenu téléchargeable pour {0} ({1})",
"ModWindowTitle": "Gérer les mods pour {0} ({1})",
"UpdateWindowTitle": "Gestionnaire de mises à jour", "UpdateWindowTitle": "Gestionnaire de mises à jour",
"CheatWindowHeading": "Cheats disponibles pour {0} [{1}]", "CheatWindowHeading": "Cheats disponibles pour {0} [{1}]",
"BuildId": "BuildId:", "BuildId": "BuildId:",
@@ -648,8 +755,8 @@
"GraphicsAALabel": "Anticrénelage :", "GraphicsAALabel": "Anticrénelage :",
"GraphicsScalingFilterLabel": "Filtre de mise à l'échelle :", "GraphicsScalingFilterLabel": "Filtre de mise à l'échelle :",
"GraphicsScalingFilterTooltip": "Choisissez le filtre de mise à l'échelle qui sera appliqué lors de l'utilisation de la mise à l'échelle de la résolution.\n\nLe filtre bilinéaire fonctionne bien pour les jeux en 3D et constitue une option par défaut sûre.\n\nLe filtre le plus proche est recommandé pour les jeux de pixel art.\n\nFSR 1.0 est simplement un filtre de netteté, non recommandé pour une utilisation avec FXAA ou SMAA.\n\nCette option peut être modifiée pendant qu'un jeu est en cours d'exécution en cliquant sur \"Appliquer\" ci-dessous ; vous pouvez simplement déplacer la fenêtre des paramètres de côté et expérimenter jusqu'à ce que vous trouviez l'aspect souhaité pour un jeu.\n\nLaissez sur BILINEAR si vous n'êtes pas sûr.", "GraphicsScalingFilterTooltip": "Choisissez le filtre de mise à l'échelle qui sera appliqué lors de l'utilisation de la mise à l'échelle de la résolution.\n\nLe filtre bilinéaire fonctionne bien pour les jeux en 3D et constitue une option par défaut sûre.\n\nLe filtre le plus proche est recommandé pour les jeux de pixel art.\n\nFSR 1.0 est simplement un filtre de netteté, non recommandé pour une utilisation avec FXAA ou SMAA.\n\nCette option peut être modifiée pendant qu'un jeu est en cours d'exécution en cliquant sur \"Appliquer\" ci-dessous ; vous pouvez simplement déplacer la fenêtre des paramètres de côté et expérimenter jusqu'à ce que vous trouviez l'aspect souhaité pour un jeu.\n\nLaissez sur BILINEAR si vous n'êtes pas sûr.",
"GraphicsScalingFilterBilinear": "Bilinear", "GraphicsScalingFilterBilinear": "Bilinéaire",
"GraphicsScalingFilterNearest": "Nearest", "GraphicsScalingFilterNearest": "Le plus proche",
"GraphicsScalingFilterFsr": "FSR", "GraphicsScalingFilterFsr": "FSR",
"GraphicsScalingFilterLevelLabel": "Niveau ", "GraphicsScalingFilterLevelLabel": "Niveau ",
"GraphicsScalingFilterLevelTooltip": "Définissez le niveau de netteté FSR 1.0. Plus élevé signifie plus net.", "GraphicsScalingFilterLevelTooltip": "Définissez le niveau de netteté FSR 1.0. Plus élevé signifie plus net.",
@@ -668,6 +775,6 @@
"SettingsTabNetworkMultiplayer": "Multijoueur", "SettingsTabNetworkMultiplayer": "Multijoueur",
"MultiplayerMode": "Mode :", "MultiplayerMode": "Mode :",
"MultiplayerModeTooltip": "Changer le mode multijoueur LDN.\n\nLdnMitm modifiera la fonctionnalité de jeu sans fil local/jeu local dans les jeux pour fonctionner comme s'il s'agissait d'un LAN, permettant des connexions locales sur le même réseau avec d'autres instances de Ryujinx et des consoles Nintendo Switch piratées ayant le module ldn_mitm installé.\n\nLe multijoueur nécessite que tous les joueurs soient sur la même version du jeu (par exemple, Super Smash Bros. Ultimate v13.0.1 ne peut pas se connecter à v13.0.0).\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.", "MultiplayerModeTooltip": "Changer le mode multijoueur LDN.\n\nLdnMitm modifiera la fonctionnalité de jeu sans fil local/jeu local dans les jeux pour fonctionner comme s'il s'agissait d'un LAN, permettant des connexions locales sur le même réseau avec d'autres instances de Ryujinx et des consoles Nintendo Switch piratées ayant le module ldn_mitm installé.\n\nLe multijoueur nécessite que tous les joueurs soient sur la même version du jeu (par exemple, Super Smash Bros. Ultimate v13.0.1 ne peut pas se connecter à v13.0.0).\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.",
"MultiplayerModeDisabled": "Disabled", "MultiplayerModeDisabled": "Désactivé",
"MultiplayerModeLdnMitm": "ldn_mitm" "MultiplayerModeLdnMitm": "ldn_mitm"
} }

View File

@@ -30,6 +30,10 @@
"MenuBarToolsManageFileTypes": "ניהול סוגי קבצים", "MenuBarToolsManageFileTypes": "ניהול סוגי קבצים",
"MenuBarToolsInstallFileTypes": "סוגי קבצי התקנה", "MenuBarToolsInstallFileTypes": "סוגי קבצי התקנה",
"MenuBarToolsUninstallFileTypes": "סוגי קבצי הסרה", "MenuBarToolsUninstallFileTypes": "סוגי קבצי הסרה",
"MenuBarView": "_View",
"MenuBarViewWindow": "Window Size",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_עזרה", "MenuBarHelp": "_עזרה",
"MenuBarHelpCheckForUpdates": "חפש עדכונים", "MenuBarHelpCheckForUpdates": "חפש עדכונים",
"MenuBarHelpAbout": "אודות", "MenuBarHelpAbout": "אודות",
@@ -92,6 +96,7 @@
"SettingsTabGeneralEnableDiscordRichPresence": "הפעלת תצוגה עשירה בדיסקורד", "SettingsTabGeneralEnableDiscordRichPresence": "הפעלת תצוגה עשירה בדיסקורד",
"SettingsTabGeneralCheckUpdatesOnLaunch": "בדוק אם קיימים עדכונים בהפעלה", "SettingsTabGeneralCheckUpdatesOnLaunch": "בדוק אם קיימים עדכונים בהפעלה",
"SettingsTabGeneralShowConfirmExitDialog": "הראה דיאלוג \"אשר יציאה\"", "SettingsTabGeneralShowConfirmExitDialog": "הראה דיאלוג \"אשר יציאה\"",
"SettingsTabGeneralRememberWindowState": "Remember Window Size/Position",
"SettingsTabGeneralHideCursor": "הסתר את הסמן", "SettingsTabGeneralHideCursor": "הסתר את הסמן",
"SettingsTabGeneralHideCursorNever": "אף פעם", "SettingsTabGeneralHideCursorNever": "אף פעם",
"SettingsTabGeneralHideCursorOnIdle": "במצב סרק", "SettingsTabGeneralHideCursorOnIdle": "במצב סרק",
@@ -266,6 +271,107 @@
"ControllerSettingsMotionGyroDeadzone": "שטח מת של הג'ירוסקופ:", "ControllerSettingsMotionGyroDeadzone": "שטח מת של הג'ירוסקופ:",
"ControllerSettingsSave": "שמירה", "ControllerSettingsSave": "שמירה",
"ControllerSettingsClose": "סגירה", "ControllerSettingsClose": "סגירה",
"KeyUnknown": "Unknown",
"KeyShiftLeft": "Shift Left",
"KeyShiftRight": "Shift Right",
"KeyControlLeft": "Ctrl Left",
"KeyMacControlLeft": "⌃ Left",
"KeyControlRight": "Ctrl Right",
"KeyMacControlRight": "⌃ Right",
"KeyAltLeft": "Alt Left",
"KeyMacAltLeft": "⌥ Left",
"KeyAltRight": "Alt Right",
"KeyMacAltRight": "⌥ Right",
"KeyWinLeft": "⊞ Left",
"KeyMacWinLeft": "⌘ Left",
"KeyWinRight": "⊞ Right",
"KeyMacWinRight": "⌘ Right",
"KeyMenu": "Menu",
"KeyUp": "Up",
"KeyDown": "Down",
"KeyLeft": "Left",
"KeyRight": "Right",
"KeyEnter": "Enter",
"KeyEscape": "Escape",
"KeySpace": "Space",
"KeyTab": "Tab",
"KeyBackSpace": "Backspace",
"KeyInsert": "Insert",
"KeyDelete": "Delete",
"KeyPageUp": "Page Up",
"KeyPageDown": "Page Down",
"KeyHome": "Home",
"KeyEnd": "End",
"KeyCapsLock": "Caps Lock",
"KeyScrollLock": "Scroll Lock",
"KeyPrintScreen": "Print Screen",
"KeyPause": "Pause",
"KeyNumLock": "Num Lock",
"KeyClear": "Clear",
"KeyKeypad0": "Keypad 0",
"KeyKeypad1": "Keypad 1",
"KeyKeypad2": "Keypad 2",
"KeyKeypad3": "Keypad 3",
"KeyKeypad4": "Keypad 4",
"KeyKeypad5": "Keypad 5",
"KeyKeypad6": "Keypad 6",
"KeyKeypad7": "Keypad 7",
"KeyKeypad8": "Keypad 8",
"KeyKeypad9": "Keypad 9",
"KeyKeypadDivide": "Keypad Divide",
"KeyKeypadMultiply": "Keypad Multiply",
"KeyKeypadSubtract": "Keypad Subtract",
"KeyKeypadAdd": "Keypad Add",
"KeyKeypadDecimal": "Keypad Decimal",
"KeyKeypadEnter": "Keypad Enter",
"KeyNumber0": "0",
"KeyNumber1": "1",
"KeyNumber2": "2",
"KeyNumber3": "3",
"KeyNumber4": "4",
"KeyNumber5": "5",
"KeyNumber6": "6",
"KeyNumber7": "7",
"KeyNumber8": "8",
"KeyNumber9": "9",
"KeyTilde": "~",
"KeyGrave": "`",
"KeyMinus": "-",
"KeyPlus": "+",
"KeyBracketLeft": "[",
"KeyBracketRight": "]",
"KeySemicolon": ";",
"KeyQuote": "\"",
"KeyComma": ",",
"KeyPeriod": ".",
"KeySlash": "/",
"KeyBackSlash": "\\",
"KeyUnbound": "Unbound",
"GamepadLeftStick": "L Stick Button",
"GamepadRightStick": "R Stick Button",
"GamepadLeftShoulder": "Left Shoulder",
"GamepadRightShoulder": "Right Shoulder",
"GamepadLeftTrigger": "Left Trigger",
"GamepadRightTrigger": "Right Trigger",
"GamepadDpadUp": "Up",
"GamepadDpadDown": "Down",
"GamepadDpadLeft": "Left",
"GamepadDpadRight": "Right",
"GamepadMinus": "-",
"GamepadPlus": "+",
"GamepadGuide": "Guide",
"GamepadMisc1": "Misc",
"GamepadPaddle1": "Paddle 1",
"GamepadPaddle2": "Paddle 2",
"GamepadPaddle3": "Paddle 3",
"GamepadPaddle4": "Paddle 4",
"GamepadTouchpad": "Touchpad",
"GamepadSingleLeftTrigger0": "Left Trigger 0",
"GamepadSingleRightTrigger0": "Right Trigger 0",
"GamepadSingleLeftTrigger1": "Left Trigger 1",
"GamepadSingleRightTrigger1": "Right Trigger 1",
"StickLeft": "Left Stick",
"StickRight": "Right Stick",
"UserProfilesSelectedUserProfile": "פרופיל המשתמש הנבחר:", "UserProfilesSelectedUserProfile": "פרופיל המשתמש הנבחר:",
"UserProfilesSaveProfileName": "שמור שם פרופיל", "UserProfilesSaveProfileName": "שמור שם פרופיל",
"UserProfilesChangeProfileImage": "שנה תמונת פרופיל", "UserProfilesChangeProfileImage": "שנה תמונת פרופיל",
@@ -597,6 +703,7 @@
"UserProfileWindowTitle": "ניהול פרופילי משתמש", "UserProfileWindowTitle": "ניהול פרופילי משתמש",
"CheatWindowTitle": "נהל צ'יטים למשחק", "CheatWindowTitle": "נהל צ'יטים למשחק",
"DlcWindowTitle": "נהל הרחבות משחק עבור {0} ({1})", "DlcWindowTitle": "נהל הרחבות משחק עבור {0} ({1})",
"ModWindowTitle": "Manage Mods for {0} ({1})",
"UpdateWindowTitle": "נהל עדכוני משחקים", "UpdateWindowTitle": "נהל עדכוני משחקים",
"CheatWindowHeading": "צ'יטים זמינים עבור {0} [{1}]", "CheatWindowHeading": "צ'יטים זמינים עבור {0} [{1}]",
"BuildId": "מזהה בניה:", "BuildId": "מזהה בניה:",

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