Compare commits

..

73 Commits

Author SHA1 Message Date
402f05b8ef Replace constant buffer access on shader with new Load instruction (#4646) 2023-05-20 16:19:26 -03:00
fb27042e01 Limit compute storage buffer size (#5028) 2023-05-20 16:15:07 +00:00
69a9de33d3 SPIR-V: Only allow implicit LOD sampling on fragment (#5026) 2023-05-20 15:52:26 +02:00
bba51c2eeb Fix macOS Update Script (#5014)
* Update updater.sh

* Better script

* Revert "Better script"

This reverts commit 9bf6be863892e5e10c2f2dba45f1d0a60daca688.
2023-05-19 21:20:01 +02:00
fc26189fe1 Eliminate redundant multiplications by gl_FragCoord.w on the shader (#4578)
* Eliminate redundant multiplications by gl_FragCoord.w on the shader

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

---
updated-dependencies:
- dependency-name: DynamicData
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-19 06:52:44 +02:00
f864a49014 Fix Vulkan blit-like operations swizzle (#5003) 2023-05-18 18:16:03 -03:00
ecbf303266 GPU: Avoid using garbage size for non-cb0 storage buffers (#4999)
* GPU: Avoid using garbage size for non-cb0 storage buffers

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

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

* Limit max size

* Add TODO

* Feedback
2023-05-18 08:56:34 +02:00
b3bf05356b ava: Fix crash when extracting sections from NCA with no data section (#5002)
Tested against Omega Strickers.
2023-05-17 19:27:49 +00:00
cb4b58052f Start GPU performance counter at 0 instead of host GPU value (#4992)
* Start performance counter at 0 instead of host perf counter value

* whitespace

* init _firstTimestamp in constructer per feedback

* change comment

* punctuation

* address feedback

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

* ava: Add missing events and default isCursorInRenderer to true

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

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

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

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

Fixes updater

* Update build.yml

Don't publish x86 Mac builds

* Naming nitpick

* Berry changes

* Use the same prefix for PR and release build archives

---------

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

* Actually do the workaround

* Remove GS on VS stuff

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

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

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

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

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

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

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

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

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

* Address feedback 1, fix an issue

* Workaround: Do not allow copies for format alias.

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

* Fix the updater for the Ava UI too.

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

* Fix restarting on Linux Avalonia.

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

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

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

* Updated comment style

* Added MarkupExtension to NullableDateTimeConverter and changed its usage

Cleaned up leftover usings

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

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

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

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

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

* fix: merge issues

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

* refactor: add more separators and increase margin

* refactor: center deadzone and range descriptions

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

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

* style: apply suggestions from code review

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

---------

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

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

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

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

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

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

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

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

* Use matrix strategy for releases

* Add macOS jobs

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

* Fix wrong version argument

* Fix check for the correct amount of args

* Install latest rcodesign release

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

* Set executable bits for PR builds on linux

---------

Co-authored-by: Mary <thog@protonmail.com>
2023-05-11 17:36:53 +02:00
ba71141bdc Ensure background translation threads exited before disposing JIT (#4874) 2023-05-10 21:46:38 -03:00
0a0675a7f6 Fix missing domain service object dispose (#4879) 2023-05-11 00:29:17 +00:00
a7c6e6a8cf fix(mvk): resumeLostDevice (#4800)
Command buffer errors currently trigger an exception "DeviceLost" crashing the process.

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

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

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

* fix hipc buffer flags

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

Removal of unnecessary hashset and dictionary

* Thread safety for GetBufferData in PersistentFlushBuffer

* Fix WaitForFencesImpl thread safety

* Proper methods for risky reference increments

* Wrong type of CB.

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

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

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

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

* Only rebind vertex buffers if they have changed

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

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

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

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

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

This commit implements this new part.

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

As always, reverse and work done by your truly.

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

* time: Do not set setup value to posix time

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

This probably fix #2430.

* Address gdkchan's comment

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

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

Fix Pokemon Sword Pokejobs for real.

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

* Fix bad merge

* Change integrity check level to error on invalid

* Add support for GDK

* Remove whitespace

* Add BID identifier

* PR Comments fix

* Restore title id in cheats GTK window

* use halign center instead of margin_left

* Merge

* fix after merge

* PR comments fix - design AVA

* PR fix - Move GetApplicationBuildId to ApplicationData class

* PR comment fix - Add empty line before method

* Align with PR #4755

* PR comments fix

* Change BuildId label to support translation

* Comments fix

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

* fix: move translation to bottom of locale file

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

* style: fix formatting

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

---------

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

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

* ICommonStateGetter disposable
2023-05-06 03:33:50 +02:00
332891b5ff Use correct offset for storage constant buffer elimination (#4821) 2023-05-05 23:59:36 +02:00
7df4fcada7 GPU: Remove CPU region handle containers (#4817)
* GPU: Remove CPU region handle containers.

Another one for the "I don't know why I didn't do this earlier" pile.

This removes the "Cpu" prefixed region handle classes, which each mirror a region handle type from Ryujinx.Memory.

Originally, not all projects had a reference to Ryujinx.Memory, so these classes were introduced to bridge the gap. Someone else crossed that bridge since, so these classes don't have much of a purpose anymore.

This PR replaces all uses of CpuRegionHandle etc to their direct Ryujinx.Memory versions.

RegionHandle methods (specifically QueryModified) are about the hottest path there is in the entire emulator, so there is a nice boost from doing this.

* Add docs
2023-05-05 23:40:46 +02:00
d6698680be UI: Fix sections extraction (#4820)
* UI: Fix sections extraction

There is currently an issue when the update NCA doesn't contains the section we want to extract, this is fixed by adding a check.
I have fixed the inverted handler of ExeFs/Logo introduced in #4755.

Fixes #4521

* Addresses feedback
2023-05-05 21:24:35 +00:00
e5c9838b0b Correct tooltips for add,remove,removeall buttons (#4819) 2023-05-05 22:38:57 +02:00
f8ec878796 Fix typo in TextureBindingsManager.cs (#4798)
accomodate -> accommodate
2023-05-05 22:17:36 +02:00
9ff21f9ab6 Use ToLowerInvariant when detecting GPU vendor. (#4815) 2023-05-05 16:35:59 +00:00
aa021085cf Allow any shader SSBO constant buffer slot and offset (#2237)
* Allow any shader SSBO constant buffer slot and offset

* Fix slot value passed to SetUsedStorageBuffer on fallback case

* Shader cache version

* Ensure that the storage buffer source constant buffer offset is word aligned

* Fix FirstBinding on GetUniformBufferDescriptors
2023-05-05 14:20:20 +00:00
1f5d881860 GPU: Allow granular buffer updates from the constant buffer updater (#4749)
* GPU: Allow granular buffer updates from the constant buffer updater

Sometimes, constant buffer updates can't be avoided, either due to a cb0 access that cannot be eliminated, or the game updating a buffer between draws to the detriment of everyone.

To avoid uploading the full 4096 bytes each time, this PR remembers the offset and size containing all constant buffer updates since the last sync. It will then upload that range after sync.

* Allow clearing the dirty range

* Always use precise

Might want to not do this if distance between the existing range and new one is too high.

* Use old force dirty mechanism when distance between regions is too great

* Update src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs

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

* Fix inheritance of _dirtyStart and _dirtyEnd

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-05-05 13:47:15 +00:00
1f664100bd ModLoader: Fix case sensitivy issues without breaking cheats (#4783)
* Fix case sensitivity for mod subdirectories

* Small refactoring of ModLoader

* Don't share instruction list between all cheats

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2023-05-05 09:39:08 +02:00
1f5e1ffa80 fix: linux launcher breaks when there are spaces in the directory path (#4795)
* fix: linux launcher breaks when there are spaces in the directory path

* Add quotes around $0 as well

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-05-05 09:06:15 +02:00
264438ff19 Revert "bcat ipc (#4446)" (#4801)
This reverts commit 4250732353.
2023-05-04 18:16:51 +02:00
3b8ac1641a UI: Move ApplicationContextMenu in a separated class (#4755)
* UI: Move ApplicationContextMenu in a separated class

This PR remove duplicated code related to the context menu on the Application list/grid by create a control for the menu which include related handler.

I've renamed "GameList/GameGrid" by "Application" for consistencies. And I've removed all uneeded field from the project file too.

While I cleaned up things, I've found an issue about purging Ptc/Shader cache, both methods list files even if the user say "No", shader cache is purged even if the user say "No". It's fixed.

* Adresses feedbacks
2023-05-04 14:41:06 +00:00
4250732353 bcat ipc (#4446) 2023-05-04 16:26:10 +02:00
4d1579acbf Fix some invalid blits involving depth textures (#4723) 2023-05-03 21:20:12 -03:00
6279f5e430 Update SettingsWindow.cs (#4785)
fix saving if directory path directly pasted in to the text field instead of using FileChooser.
2023-05-03 16:04:40 +02:00
b7d2bff6aa Revert "ModLoader: Fix case sensitivy issues (#4720)" (#4781)
This reverts commit cc1a933a2f.
2023-05-03 11:20:05 +02:00
7c327fecb3 Vulkan: Record modifications after changing the framebuffer (#4775)
Our Vulkan backend inserts image barriers when a texture is sampled after it is rendered. This is done via a "modification flag" which is set when a render target is unbound (presuming that a texture has finished drawing to it).

Imagine the following scenario:
- Game sets render target to texture A
- Game renders to texture A
- (render pass ends)
- Game binds texture A to a sampler
- Game sets render target to texture B
- Renders to texture B using texture A (barrier required)

Because of the previous behaviour, the check to add a barrier for sampling a texture actually happens before it is registered as modified, meaning no barrier was added at all. This isn't always the case, but it was definitely causing issues in Xenoblade 2.

This doesn't fix any more complicated issues where a texture is repeatedly sampled while it is currently being rendered.

Fixes visual glitches at lower resolutions in Xenoblade 2. May fix other cases.
2023-05-03 10:42:21 +02:00
cc1a933a2f ModLoader: Fix case sensitivy issues (#4720)
* Fix case sensitivity for mod subdirectories

* Small refactoring of ModLoader
2023-05-03 02:07:16 +02:00
dd574146fb Add hide-cursor command line argument & always hide cursor option (#4613)
* Add hide-cursor command line argument

* gtk: Adjust SettingsWindow for hide cursor options

* ava: Adjust SettingsWindow for hide cursor options

* ava: Add override check for HideCursor arg

* Remove copy&paste sins

* ava: Leave a little more room between the options

* gtk: Fix hide cursor issues

* ava: Only hide cursor if it's within the embedded window
2023-05-02 03:29:47 +02:00
2c94ac455e GPU: Keep rendered textures without any pool references alive (#4662)
* GPU: Keep sampled textures without any pool references alive

Occasionally games are very wasteful and clear/write to a texture without ever sampling it. As rendered textures in NVN games seem to all have overlapping memory ranges, the texture will eventually get overwritten.

Normally, this would trigger a removal from the auto delete cache, but a pool entry would keep the texture alive. However, with these textures that are never used, they will get deleted immediately and recreated on the next frame.

This change makes it so the ShortTextureCache can keep textures that have naver had a pool reference alive for a few frames, so they're not constantly being created and deleted.

This improves performance in Zelda BOTW a little.

* Cleanup
2023-05-01 16:27:51 -03:00
e18d258fa0 GPU: Pre-emptively flush textures that are flushed often (to imported memory when available) (#4711)
* WIP texture pre-flush

Improve performance of TextureView GetData to buffer

Fix copy/sync ordering

Fix minor bug

Make this actually work

WIP host mapping stuff

* Fix usage flags

* message

* Cleanup 1

* Fix rebase

* Fix

* Improve pre-flush rules

* Fix pre-flush

* A lot of cleanup

* Use the host memory bits

* Select the correct memory type

* Cleanup TextureGroupHandle

* Missing comment

* Remove debugging logs

* Revert BufferHandle _value access modifier

* One interrupt action at a time.

* Support D32S8 to D24S8 conversion, safeguards

* Interrupt cannot happen in sync handle's lock

Waitable needs to be checked twice now, but this should stop it from deadlocking.

* Remove unused using

* Address some feedback

* Address feedback

* Address more feedback

* Address more feedback

* Improve sync rules

Should allow for faster sync in some cases.
2023-05-01 16:05:12 -03:00
36f10df775 GPU: Fix errors handling texture remapping (#4745)
* GPU: Fix errors handling texture remapping

- Fixes an error where a pool entry and memory mapping changing at the same time could cause a texture to rebind its data from the wrong GPU VA (data swaps)
- Fixes an error where the texture pool could act on a mapping change before the mapping has actually been changed ("Unmapped" event happens before change, we need to signal it changed _after_ it completes)

TODO: remove textures from partially mapped list... if they aren't.

* Add Remap actions for handling post-mapping behaviours

* Remove unused code.

* Address feedback

* Nit
2023-05-01 15:32:32 -03:00
680e548022 Uneven frame pacing with vsync (#4744)
fixes issue #3906
2023-04-29 21:54:41 +01:00
249 changed files with 6601 additions and 3402 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageVersion Include="DynamicData" Version="7.13.5" /> <PackageVersion Include="DynamicData" Version="7.13.8" />
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
@ -44,7 +44,7 @@
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.0" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" />
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
<PackageVersion Include="System.Management" Version="7.0.1" /> <PackageVersion Include="System.Management" Version="7.0.1" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />

View File

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

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
SCRIPT_DIR=$(dirname $(realpath $0)) SCRIPT_DIR=$(dirname "$(realpath "$0")")
RYUJINX_BIN="Ryujinx" RYUJINX_BIN="Ryujinx"
if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then

View File

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

View File

@ -25,15 +25,20 @@ error_handler() {
exit 1 exit 1
} }
trap 'error_handler ${LINENO}' ERR
# Wait for Ryujinx to exit # Wait for Ryujinx to exit
# NOTE: in case no fds are open, lsof could be returning with a process still living. # NOTE: in case no fds are open, lsof could be returning with a process still living.
# We wait 1s and assume the process stopped after that # We wait 1s and assume the process stopped after that
lsof -p $APP_PID +r 1 &>/dev/null lsof -p $APP_PID +r 1 &>/dev/null
sleep 1 sleep 1
trap 'error_handler ${LINENO}' ERR
# Now replace and reopen. # Now replace and reopen.
rm -rf "$INSTALL_DIRECTORY" rm -rf "$INSTALL_DIRECTORY"
mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY" mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY"
open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS"
if [ "$#" -le 3 ]; then
open -a "$INSTALL_DIRECTORY"
else
open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS"
fi

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,6 +35,7 @@ using Ryujinx.Input.HLE;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using Silk.NET.Vulkan;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -85,7 +86,7 @@ namespace Ryujinx.Ava
private KeyboardHotkeyState _prevHotkeyState; private KeyboardHotkeyState _prevHotkeyState;
private long _lastCursorMoveTime; private long _lastCursorMoveTime;
private bool _isCursorInRenderer; private bool _isCursorInRenderer = true;
private bool _isStopped; private bool _isStopped;
private bool _isActive; private bool _isActive;
@ -157,9 +158,11 @@ namespace Ryujinx.Ava
_isFirmwareTitle = true; _isFirmwareTitle = true;
} }
ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorState_Changed; ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed;
_topLevel.PointerMoved += TopLevel_PointerMoved; _topLevel.PointerMoved += TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter += TopLevel_PointerEnterOrMoved;
_topLevel.PointerLeave += TopLevel_PointerLeave;
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
@ -182,7 +185,7 @@ namespace Ryujinx.Ava
_gpuCancellationTokenSource = new CancellationTokenSource(); _gpuCancellationTokenSource = new CancellationTokenSource();
} }
private void TopLevel_PointerMoved(object sender, PointerEventArgs e) private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
{ {
if (sender is MainWindow window) if (sender is MainWindow window)
{ {
@ -200,6 +203,12 @@ namespace Ryujinx.Ava
} }
} }
} }
private void TopLevel_PointerLeave(object sender, PointerEventArgs e)
{
_isCursorInRenderer = false;
}
private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e) private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
{ {
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
@ -445,7 +454,9 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
_topLevel.PointerMoved -= TopLevel_PointerMoved; _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerLeave -= TopLevel_PointerLeave;
_gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Cancel();
_gpuCancellationTokenSource.Dispose(); _gpuCancellationTokenSource.Dispose();
@ -468,9 +479,9 @@ namespace Ryujinx.Ava
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null); (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null);
} }
private void HideCursorState_Changed(object sender, ReactiveEventArgs<bool> state) private void HideCursorState_Changed(object sender, ReactiveEventArgs<HideCursorMode> state)
{ {
if (state.NewValue) if (state.NewValue == HideCursorMode.OnIdle)
{ {
_lastCursorMoveTime = Stopwatch.GetTimestamp(); _lastCursorMoveTime = Stopwatch.GetTimestamp();
} }
@ -670,7 +681,7 @@ namespace Ryujinx.Ava
_viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata =>
{ {
appMetadata.LastPlayed = DateTime.UtcNow.ToString(); appMetadata.LastPlayed = DateTime.UtcNow;
}); });
return true; return true;
@ -701,6 +712,7 @@ namespace Ryujinx.Ava
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
{ {
renderer = new VulkanRenderer( renderer = new VulkanRenderer(
Vk.GetApi(),
(_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, (_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface,
VulkanHelper.GetRequiredInstanceExtensions, VulkanHelper.GetRequiredInstanceExtensions,
ConfigurationState.Instance.Graphics.PreferredGpu.Value); ConfigurationState.Instance.Graphics.PreferredGpu.Value);
@ -964,22 +976,21 @@ namespace Ryujinx.Ava
NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
if (_viewModel.IsActive) if (_viewModel.IsActive)
{
if (ConfigurationState.Instance.Hid.EnableMouse)
{ {
if (_isCursorInRenderer) if (_isCursorInRenderer)
{
if (ConfigurationState.Instance.Hid.EnableMouse)
{ {
HideCursor(); HideCursor();
} }
else else
{ {
switch (ConfigurationState.Instance.HideCursor.Value)
{
case HideCursorMode.Never:
ShowCursor(); ShowCursor();
} break;
} case HideCursorMode.OnIdle:
else
{
if (ConfigurationState.Instance.HideCursorOnIdle)
{
if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency) if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency)
{ {
HideCursor(); HideCursor();
@ -988,8 +999,17 @@ namespace Ryujinx.Ava
{ {
ShowCursor(); ShowCursor();
} }
break;
case HideCursorMode.Always:
HideCursor();
break;
} }
} }
}
else
{
ShowCursor();
}
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {

View File

@ -80,7 +80,10 @@
"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",
"SettingsTabGeneralHideCursorOnIdle": "Hide Cursor on Idle", "SettingsTabGeneralHideCursor": "Hide Cursor:",
"SettingsTabGeneralHideCursorNever": "Never",
"SettingsTabGeneralHideCursorOnIdle": "On Idle",
"SettingsTabGeneralHideCursorAlways": "Always",
"SettingsTabGeneralGameDirectories": "Game Directories", "SettingsTabGeneralGameDirectories": "Game Directories",
"SettingsTabGeneralAdd": "Add", "SettingsTabGeneralAdd": "Add",
"SettingsTabGeneralRemove": "Remove", "SettingsTabGeneralRemove": "Remove",
@ -587,6 +590,7 @@
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})", "DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
"UpdateWindowTitle": "Title Update Manager", "UpdateWindowTitle": "Title Update Manager",
"CheatWindowHeading": "Cheats Available for {0} [{1}]", "CheatWindowHeading": "Cheats Available for {0} [{1}]",
"BuildId": "BuildId:",
"DlcWindowHeading": "{0} Downloadable Content(s)", "DlcWindowHeading": "{0} Downloadable Content(s)",
"UserProfilesEditProfile": "Edit Selected", "UserProfilesEditProfile": "Edit Selected",
"Cancel": "Cancel", "Cancel": "Cancel",
@ -641,5 +645,6 @@
"UserEditorTitleCreate" : "Create User", "UserEditorTitleCreate" : "Create User",
"SettingsTabNetworkInterface": "Network Interface:", "SettingsTabNetworkInterface": "Network Interface:",
"NetworkInterfaceTooltip": "The network interface used for LAN features", "NetworkInterfaceTooltip": "The network interface used for LAN features",
"NetworkInterfaceDefault": "Default" "NetworkInterfaceDefault": "Default",
"PackagingShaders": "Packaging Shaders"
} }

View File

@ -193,7 +193,7 @@ namespace Ryujinx.Ava.Common
if (nca.Header.ContentType == NcaContentType.Program) if (nca.Header.ContentType == NcaContentType.Program)
{ {
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{ {
patchNca = nca; patchNca = nca;
} }
@ -233,8 +233,13 @@ namespace Ryujinx.Ava.Common
try try
{ {
IFileSystem ncaFileSystem = patchNca != null bool sectionExistsInPatch = false;
? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) if (patchNca != null)
{
sectionExistsInPatch = patchNca.CanOpenSection(index);
}
IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
FileSystemClient fsClient = _horizonClient.Fs; FileSystemClient fsClient = _horizonClient.Fs;

View File

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

View File

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

View File

@ -183,6 +183,18 @@ namespace Ryujinx.Ava
{ {
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
} }
// Check if HideCursor was overridden.
if (CommandLineState.OverrideHideCursor is not null)
{
ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor!.ToLower() switch
{
"never" => HideCursorMode.Never,
"onidle" => HideCursorMode.OnIdle,
"always" => HideCursorMode.Always,
_ => ConfigurationState.Instance.HideCursor.Value
};
}
} }
private static void PrintSystemInfo() private static void PrintSystemInfo()

View File

@ -103,50 +103,6 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</AvaloniaResource> </AvaloniaResource>
<AvaloniaResource Include="Assets\Styles\Styles.xaml" /> <AvaloniaResource Include="Assets\Styles\Styles.xaml" />
<Compile Update="App.axaml.cs">
<DependentUpon>App.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Ui\Windows\MainWindow.axaml.cs">
<DependentUpon>MainWindow.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Ui\Windows\AboutWindow.axaml.cs">
<DependentUpon>AboutWindow.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Ui\Applet\ErrorAppletWindow.axaml.cs">
<DependentUpon>ProfileWindow.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Ui\Applet\SwkbdAppletWindow.axaml.cs">
<DependentUpon>ProfileWindow.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Ui\Controls\InputDialog.axaml.cs">
<DependentUpon>InputDialog.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Ui\Windows\ContentDialogOverlay.xaml.cs">
<DependentUpon>ContentDialogOverlay.xaml</DependentUpon>
</Compile>
<Compile Update="Ui\Controls\GameListView.axaml.cs">
<DependentUpon>GameListView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="UI\Views\User\UserEditorView.axaml.cs">
<DependentUpon>UserEditor.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="UI\Views\User\UserRecovererView.axaml.cs">
<DependentUpon>UserRecoverer.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="UI\Views\User\UserSelectorView.axaml.cs">
<DependentUpon>UserSelector.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,80 @@
<MenuFlyout
x:Class="Ryujinx.Ava.UI.Controls.ApplicationContextMenu"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
<MenuItem
Click="ToggleFavorite_Click"
Header="{locale:Locale GameListContextMenuToggleFavorite}"
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
<Separator />
<MenuItem
Click="OpenUserSaveDirectory_Click"
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
IsEnabled="{Binding OpenUserSaveDirectoryEnabled}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem
Click="OpenDeviceSaveDirectory_Click"
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
IsEnabled="{Binding OpenDeviceSaveDirectoryEnabled}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
<MenuItem
Click="OpenBcatSaveDirectory_Click"
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
IsEnabled="{Binding OpenBcatSaveDirectoryEnabled}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
<Separator />
<MenuItem
Click="OpenTitleUpdateManager_Click"
Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
<MenuItem
Click="OpenDownloadableContentManager_Click"
Header="{locale:Locale GameListContextMenuManageDlc}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
<MenuItem
Click="OpenCheatManager_Click"
Header="{locale:Locale GameListContextMenuManageCheat}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
<MenuItem
Click="OpenModsDirectory_Click"
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
<MenuItem
Click="OpenSdModsDirectory_Click"
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
<Separator />
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
<MenuItem
Click="PurgePtcCache_Click"
Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
<MenuItem
Click="PurgeShaderCache_Click"
Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
<MenuItem
Click="OpenPtcDirectory_Click"
Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
<MenuItem
Click="OpenShaderCacheDirectory_Click"
Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
</MenuItem>
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
<MenuItem
Click="ExtractApplicationExeFs_Click"
Header="{locale:Locale GameListContextMenuExtractDataExeFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" />
<MenuItem
Click="ExtractApplicationRomFs_Click"
Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
<MenuItem
Click="ExtractApplicationLogo_Click"
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
</MenuItem>
</MenuFlyout>

View File

@ -0,0 +1,327 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using LibHac.Fs;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration;
using Ryujinx.Ui.App.Common;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.Common.Helper;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Path = System.IO.Path;
using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Controls
{
public class ApplicationContextMenu : MenuFlyout
{
public ApplicationContextMenu()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void ToggleFavorite_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite;
viewModel.ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.TitleId, appMetadata =>
{
appMetadata.Favorite = viewModel.SelectedApplication.Favorite;
});
viewModel.RefreshView();
}
}
public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args)
{
if ((sender as MenuItem)?.DataContext is MainWindowViewModel viewModel)
{
OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
}
}
public void OpenDeviceSaveDirectory_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
OpenSaveDirectory(viewModel, SaveDataType.Device, userId: default);
}
public void OpenBcatSaveDirectory_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
OpenSaveDirectory(viewModel, SaveDataType.Bcat, userId: default);
}
private static void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId)
{
if (viewModel?.SelectedApplication != null)
{
if (!ulong.TryParse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
});
return;
}
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default);
ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.TitleName);
}
}
public async void OpenTitleUpdateManager_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName);
}
}
public async void OpenDownloadableContentManager_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName);
}
}
public async void OpenCheatManager_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await new CheatWindow(
viewModel.VirtualFileSystem,
viewModel.SelectedApplication.TitleId,
viewModel.SelectedApplication.TitleName,
viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window);
}
}
public void OpenModsDirectory_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId);
OpenHelper.OpenFolder(titleModsPath);
}
}
public void OpenSdModsDirectory_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
string sdModsBasePath = ModLoader.GetSdModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId);
OpenHelper.OpenFolder(titleModsPath);
}
}
public async void PurgePtcCache_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result == UserResult.Yes)
{
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "0"));
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "1"));
List<FileInfo> cacheFiles = new();
if (mainDir.Exists)
{
cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache"));
}
if (backupDir.Exists)
{
cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache"));
}
if (cacheFiles.Count > 0)
{
foreach (FileInfo file in cacheFiles)
{
try
{
file.Delete();
}
catch (Exception ex)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, ex));
}
}
}
}
}
}
public async void PurgeShaderCache_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result == UserResult.Yes)
{
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader"));
List<DirectoryInfo> oldCacheDirectories = new();
List<FileInfo> newCacheFiles = new();
if (shaderCacheDir.Exists)
{
oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*"));
newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc"));
newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data"));
}
if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0))
{
foreach (DirectoryInfo directory in oldCacheDirectories)
{
try
{
directory.Delete(true);
}
catch (Exception ex)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, ex));
}
}
foreach (FileInfo file in newCacheFiles)
{
try
{
file.Delete();
}
catch (Exception ex)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, ex));
}
}
}
}
}
}
public void OpenPtcDirectory_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu");
string mainDir = Path.Combine(ptcDir, "0");
string backupDir = Path.Combine(ptcDir, "1");
if (!Directory.Exists(ptcDir))
{
Directory.CreateDirectory(ptcDir);
Directory.CreateDirectory(mainDir);
Directory.CreateDirectory(backupDir);
}
OpenHelper.OpenFolder(ptcDir);
}
}
public void OpenShaderCacheDirectory_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader");
if (!Directory.Exists(shaderCacheDir))
{
Directory.CreateDirectory(shaderCacheDir);
}
OpenHelper.OpenFolder(shaderCacheDir);
}
}
public async void ExtractApplicationExeFs_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
}
}
public async void ExtractApplicationRomFs_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Data, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
}
}
public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
}
}
}
}

View File

@ -0,0 +1,102 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Controls.ApplicationGridView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
d:DesignHeight="450"
d:DesignWidth="800"
Focusable="True"
mc:Ignorable="d">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<controls:ApplicationContextMenu x:Key="ApplicationContextMenu" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox
Grid.Row="0"
Padding="8"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ContextFlyout="{StaticResource ApplicationContextMenu}"
DoubleTapped="GameList_DoubleTapped"
Items="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<flex:FlexPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AlignContent="FlexStart"
JustifyContent="Center" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Margin" Value="5" />
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" />
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border
Margin="10"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
ClipToBounds="True"
CornerRadius="4">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Grid.Row="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<Panel
Grid.Row="1"
Height="50"
Margin="0,10,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}">
<TextBlock
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding TitleName}"
TextAlignment="Center"
TextWrapping="Wrap" />
</Panel>
</Grid>
</Border>
<ui:SymbolIcon
Margin="5,5,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontSize="16"
Foreground="{DynamicResource SystemAccentColor}"
IsVisible="{Binding Favorite}"
Symbol="StarFilled" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

View File

@ -9,10 +9,10 @@ using System;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class GameGridView : UserControl public partial class ApplicationGridView : UserControl
{ {
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); RoutedEvent.Register<ApplicationGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
{ {
@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Controls
remove { RemoveHandler(ApplicationOpenedEvent, value); } remove { RemoveHandler(ApplicationOpenedEvent, value); }
} }
public GameGridView() public ApplicationGridView()
{ {
InitializeComponent(); InitializeComponent();
} }
@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.Controls
} }
} }
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) private void SearchBox_OnKeyUp(object sender, KeyEventArgs args)
{ {
(DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text;
} }

View File

@ -1,10 +1,10 @@
<UserControl <UserControl
x:Class="Ryujinx.Ava.UI.Controls.GameListView" x:Class="Ryujinx.Ava.UI.Controls.ApplicationListView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
d:DesignHeight="450" d:DesignHeight="450"
@ -13,82 +13,7 @@
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" /> <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu"> <controls:ApplicationContextMenu x:Key="ApplicationContextMenu" />
<MenuItem
Command="{Binding ToggleFavorite}"
Header="{locale:Locale GameListContextMenuToggleFavorite}"
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
<Separator />
<MenuItem
Command="{Binding OpenUserSaveDirectory}"
IsEnabled="{Binding EnabledUserSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenDeviceSaveDirectory}"
IsEnabled="{Binding EnabledDeviceSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenBcatSaveDirectory}"
IsEnabled="{Binding EnabledBcatSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
<Separator />
<MenuItem
Command="{Binding OpenTitleUpdateManager}"
Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
<MenuItem
Command="{Binding OpenDownloadableContentManager}"
Header="{locale:Locale GameListContextMenuManageDlc}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
<MenuItem
Command="{Binding OpenCheatManager}"
Header="{locale:Locale GameListContextMenuManageCheat}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
<MenuItem
Command="{Binding OpenModsDirectory}"
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenSdModsDirectory}"
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
<Separator />
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
<MenuItem
Command="{Binding PurgePtcCache}"
Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
<MenuItem
Command="{Binding PurgeShaderCache}"
Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
<MenuItem
Command="{Binding OpenPtcDirectory}"
Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenShaderCacheDirectory}"
Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
</MenuItem>
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
<MenuItem
Command="{Binding ExtractExeFs}"
Header="{locale:Locale GameListContextMenuExtractDataExeFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" />
<MenuItem
Command="{Binding ExtractRomFs}"
Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
<MenuItem
Command="{Binding ExtractLogo}"
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
</MenuItem>
</MenuFlyout>
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@ -100,7 +25,7 @@
Padding="8" Padding="8"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
ContextFlyout="{StaticResource GameContextMenu}" ContextFlyout="{StaticResource ApplicationContextMenu}"
DoubleTapped="GameList_DoubleTapped" DoubleTapped="GameList_DoubleTapped"
Items="{Binding AppsObservableList}" Items="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged"> SelectionChanged="GameList_SelectionChanged">
@ -204,7 +129,7 @@
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding LastPlayed}" Text="{Binding LastPlayed, Converter={helpers:NullableDateTimeConverter}}"
TextAlignment="Right" TextAlignment="Right"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock

View File

@ -9,10 +9,10 @@ using System;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class GameListView : UserControl public partial class ApplicationListView : UserControl
{ {
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); RoutedEvent.Register<ApplicationListView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
{ {
@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Controls
remove { RemoveHandler(ApplicationOpenedEvent, value); } remove { RemoveHandler(ApplicationOpenedEvent, value); }
} }
public GameListView() public ApplicationListView()
{ {
InitializeComponent(); InitializeComponent();
} }
@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.Controls
} }
} }
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) private void SearchBox_OnKeyUp(object sender, KeyEventArgs args)
{ {
(DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text;
} }

View File

@ -1,177 +0,0 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Controls.GameGridView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
Focusable="True">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu">
<MenuItem
Command="{Binding ToggleFavorite}"
Header="{locale:Locale GameListContextMenuToggleFavorite}"
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
<Separator />
<MenuItem
Command="{Binding OpenUserSaveDirectory}"
IsEnabled="{Binding EnabledUserSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenDeviceSaveDirectory}"
IsEnabled="{Binding EnabledDeviceSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenBcatSaveDirectory}"
IsEnabled="{Binding EnabledBcatSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
<Separator />
<MenuItem
Command="{Binding OpenTitleUpdateManager}"
Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
<MenuItem
Command="{Binding OpenDownloadableContentManager}"
Header="{locale:Locale GameListContextMenuManageDlc}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
<MenuItem
Command="{Binding OpenCheatManager}"
Header="{locale:Locale GameListContextMenuManageCheat}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
<MenuItem
Command="{Binding OpenModsDirectory}"
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenSdModsDirectory}"
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
<Separator />
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
<MenuItem
Command="{Binding PurgePtcCache}"
Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
<MenuItem
Command="{Binding PurgeShaderCache}"
Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
<MenuItem
Command="{Binding OpenPtcDirectory}"
Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
<MenuItem
Command="{Binding OpenShaderCacheDirectory}"
Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
</MenuItem>
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
<MenuItem
Command="{Binding ExtractExeFs}"
Header="{locale:Locale GameListContextMenuExtractDataExeFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" />
<MenuItem
Command="{Binding ExtractRomFs}"
Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
<MenuItem
Command="{Binding ExtractLogo}"
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
</MenuItem>
</MenuFlyout>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox
Grid.Row="0"
Padding="8"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ContextFlyout="{StaticResource GameContextMenu}"
DoubleTapped="GameList_DoubleTapped"
Items="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<flex:FlexPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AlignContent="FlexStart"
JustifyContent="Center" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Margin" Value="5" />
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" />
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border
Margin="10"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
ClipToBounds="True"
CornerRadius="4">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Grid.Row="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<Panel
Grid.Row="1"
Height="50"
Margin="0 10 0 0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}">
<TextBlock
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding TitleName}"
TextAlignment="Center"
TextWrapping="Wrap" />
</Panel>
</Grid>
</Border>
<ui:SymbolIcon
Margin="5,5,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontSize="16"
Foreground="{DynamicResource SystemAccentColor}"
IsVisible="{Binding Favorite}"
Symbol="StarFilled" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

View File

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

View File

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

View File

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

View File

@ -6,8 +6,6 @@ using Avalonia.Threading;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input; using Ryujinx.Ava.Input;
@ -33,13 +31,11 @@ using SixLabors.ImageSharp.PixelFormats;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Globalization;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Path = System.IO.Path; using Path = System.IO.Path;
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
@ -346,11 +342,11 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public bool EnabledUserSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; public bool OpenUserSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
public bool EnabledDeviceSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; public bool OpenDeviceSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
public bool EnabledBcatSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; public bool OpenBcatSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public string LoadHeading public string LoadHeading
{ {
@ -941,7 +937,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}; };
} }
private void RefreshView() public void RefreshView()
{ {
RefreshGrid(); RefreshGrid();
} }
@ -1103,6 +1099,10 @@ namespace Ryujinx.Ava.UI.ViewModels
LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders]; LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders];
IsLoadingIndeterminate = false; IsLoadingIndeterminate = false;
break; break;
case ShaderCacheLoadingState.Packaging:
LoadHeading = LocaleManager.Instance[LocaleKeys.PackagingShaders];
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Loaded: case ShaderCacheLoadingState.Loaded:
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName); LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
IsLoadingIndeterminate = true; IsLoadingIndeterminate = true;
@ -1116,30 +1116,6 @@ namespace Ryujinx.Ava.UI.ViewModels
})); }));
} }
private async void ExtractLogo()
{
if (SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, SelectedApplication.Path, SelectedApplication.TitleName);
}
}
private async void ExtractRomFs()
{
if (SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Data, SelectedApplication.Path, SelectedApplication.TitleName);
}
}
private async void ExtractExeFs()
{
if (SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Code, SelectedApplication.Path, SelectedApplication.TitleName);
}
}
private void PrepareLoadScreen() private void PrepareLoadScreen()
{ {
using MemoryStream stream = new(SelectedIcon); using MemoryStream stream = new(SelectedIcon);
@ -1383,241 +1359,11 @@ namespace Ryujinx.Ava.UI.ViewModels
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient); await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
} }
public void OpenPtcDirectory()
{
ApplicationData selection = SelectedApplication;
if (selection != null)
{
string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu");
string mainPath = Path.Combine(ptcDir, "0");
string backupPath = Path.Combine(ptcDir, "1");
if (!Directory.Exists(ptcDir))
{
Directory.CreateDirectory(ptcDir);
Directory.CreateDirectory(mainPath);
Directory.CreateDirectory(backupPath);
}
OpenHelper.OpenFolder(ptcDir);
}
}
public async void PurgePtcCache()
{
ApplicationData selection = SelectedApplication;
if (selection != null)
{
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0"));
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1"));
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, selection.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
List<FileInfo> cacheFiles = new();
if (mainDir.Exists)
{
cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache"));
}
if (backupDir.Exists)
{
cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache"));
}
if (cacheFiles.Count > 0 && result == UserResult.Yes)
{
foreach (FileInfo file in cacheFiles)
{
try
{
file.Delete();
}
catch (Exception e)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, e));
}
}
}
}
}
public void OpenShaderCacheDirectory()
{
ApplicationData selection = SelectedApplication;
if (selection != null)
{
string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader");
if (!Directory.Exists(shaderCacheDir))
{
Directory.CreateDirectory(shaderCacheDir);
}
OpenHelper.OpenFolder(shaderCacheDir);
}
}
public void SimulateWakeUpMessage() public void SimulateWakeUpMessage()
{ {
AppHost.Device.System.SimulateWakeUpMessage(); AppHost.Device.System.SimulateWakeUpMessage();
} }
public async void PurgeShaderCache()
{
ApplicationData selection = SelectedApplication;
if (selection != null)
{
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"));
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, selection.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
List<DirectoryInfo> oldCacheDirectories = new();
List<FileInfo> newCacheFiles = new();
if (shaderCacheDir.Exists)
{
oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*"));
newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc"));
newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data"));
}
if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0) && result == UserResult.Yes)
{
foreach (DirectoryInfo directory in oldCacheDirectories)
{
try
{
directory.Delete(true);
}
catch (Exception e)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, e));
}
}
}
foreach (FileInfo file in newCacheFiles)
{
try
{
file.Delete();
}
catch (Exception e)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, e));
}
}
}
}
public void ToggleFavorite()
{
ApplicationData selection = SelectedApplication;
if (selection != null)
{
selection.Favorite = !selection.Favorite;
ApplicationLibrary.LoadAndSaveMetaData(selection.TitleId, appMetadata =>
{
appMetadata.Favorite = selection.Favorite;
});
RefreshView();
}
}
public void OpenUserSaveDirectory()
{
OpenSaveDirectory(SaveDataType.Account, userId: new UserId((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low));
}
public void OpenDeviceSaveDirectory()
{
OpenSaveDirectory(SaveDataType.Device, userId: default);
}
public void OpenBcatSaveDirectory()
{
OpenSaveDirectory(SaveDataType.Bcat, userId: default);
}
private void OpenSaveDirectory(SaveDataType saveDataType, UserId userId)
{
if (SelectedApplication != null)
{
if (!ulong.TryParse(SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
});
return;
}
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default);
ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, SelectedApplication.ControlHolder, SelectedApplication.TitleName);
}
}
public void OpenModsDirectory()
{
if (SelectedApplication != null)
{
string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath();
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId);
OpenHelper.OpenFolder(titleModsPath);
}
}
public void OpenSdModsDirectory()
{
if (SelectedApplication != null)
{
string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath();
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId);
OpenHelper.OpenFolder(titleModsPath);
}
}
public async void OpenTitleUpdateManager()
{
if (SelectedApplication != null)
{
await TitleUpdateWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName);
}
}
public async void OpenDownloadableContentManager()
{
if (SelectedApplication != null)
{
await DownloadableContentManagerWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName);
}
}
public async void OpenCheatManager()
{
if (SelectedApplication != null)
{
await new CheatWindow(VirtualFileSystem, SelectedApplication.TitleId, SelectedApplication.TitleName).ShowDialog(TopLevel as Window);
}
}
public async void LoadApplications() public async void LoadApplications()
{ {
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
@ -1778,10 +1524,9 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
{ {
if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime)) if (appMetadata.LastPlayed.HasValue)
{ {
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
} }
}); });

View File

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

View File

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

View File

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

View File

@ -37,9 +37,24 @@
<CheckBox IsChecked="{Binding ShowConfirmExit}"> <CheckBox IsChecked="{Binding ShowConfirmExit}">
<TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" /> <TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding HideCursorOnIdle}"> <StackPanel Margin="0, 15, 0, 10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralHideCursor}"
Width="150" />
<ComboBox SelectedIndex="{Binding HideCursor}"
HorizontalContentAlignment="Left"
MinWidth="100">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorNever}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" /> <TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" />
</CheckBox> </ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorAlways}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />

View File

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

View File

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

View File

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

View File

@ -88,16 +88,16 @@
<main:MainViewControls <main:MainViewControls
Name="ViewControls" Name="ViewControls"
Grid.Row="0"/> Grid.Row="0"/>
<controls:GameListView <controls:ApplicationListView
x:Name="GameList" x:Name="ApplicationList"
Grid.Row="1" Grid.Row="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
IsVisible="{Binding IsList}" /> IsVisible="{Binding IsList}" />
<controls:GameGridView <controls:ApplicationGridView
x:Name="GameGrid" x:Name="ApplicationGrid"
Grid.Row="1" Grid.Row="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"

View File

@ -288,13 +288,13 @@ namespace Ryujinx.Ava.UI.Windows
{ {
StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged; StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged;
GameGrid.ApplicationOpened += Application_Opened; ApplicationGrid.ApplicationOpened += Application_Opened;
GameGrid.DataContext = ViewModel; ApplicationGrid.DataContext = ViewModel;
GameList.ApplicationOpened += Application_Opened; ApplicationList.ApplicationOpened += Application_Opened;
GameList.DataContext = ViewModel; ApplicationList.DataContext = ViewModel;
LoadHotKeys(); LoadHotKeys();
} }

View File

@ -0,0 +1,9 @@
namespace Ryujinx.Common.Configuration
{
public enum HideCursorMode
{
Never,
OnIdle,
Always
}
}

View File

@ -1,5 +1,4 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
@ -822,21 +821,21 @@ namespace Ryujinx.Cpu.AppleHv
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) public RegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); return Tracking.BeginTracking(address, size, id);
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); return Tracking.BeginGranularTracking(address, size, handles, granularity, id);
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
} }
/// <summary> /// <summary>

View File

@ -1,5 +1,4 @@
using Ryujinx.Cpu.Tracking; using Ryujinx.Memory;
using Ryujinx.Memory;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -30,7 +29,7 @@ namespace Ryujinx.Cpu
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="id">Handle ID</param> /// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuRegionHandle BeginTracking(ulong address, ulong size, int id); RegionHandle BeginTracking(ulong address, ulong size, int id);
/// <summary> /// <summary>
/// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. /// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
@ -41,7 +40,7 @@ namespace Ryujinx.Cpu
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param> /// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id); MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id);
/// <summary> /// <summary>
/// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. /// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
@ -51,6 +50,6 @@ namespace Ryujinx.Cpu
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param> /// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id); SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id);
} }
} }

View File

@ -1,5 +1,4 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
@ -629,21 +628,21 @@ namespace Ryujinx.Cpu.Jit
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) public RegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); return Tracking.BeginTracking(address, size, id);
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); return Tracking.BeginGranularTracking(address, size, handles, granularity, id);
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@ -1,5 +1,4 @@
using ARMeilleure.Memory; using ARMeilleure.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
@ -706,21 +705,21 @@ namespace Ryujinx.Cpu.Jit
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) public RegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); return Tracking.BeginTracking(address, size, id);
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id) public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); return Tracking.BeginGranularTracking(address, size, handles, granularity, id);
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
} }
/// <summary> /// <summary>

View File

@ -1,28 +0,0 @@
using Ryujinx.Memory.Tracking;
using System;
using System.Collections.Generic;
namespace Ryujinx.Cpu.Tracking
{
public class CpuMultiRegionHandle : IMultiRegionHandle
{
private readonly MultiRegionHandle _impl;
public bool Dirty => _impl.Dirty;
internal CpuMultiRegionHandle(MultiRegionHandle impl)
{
_impl = impl;
}
public void Dispose() => _impl.Dispose();
public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size);
public IEnumerable<IRegionHandle> GetHandles() => _impl.GetHandles();
public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction);
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction);
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber);
public void RegisterAction(ulong address, ulong size, RegionSignal action) => _impl.RegisterAction(address, size, action);
public void RegisterPreciseAction(ulong address, ulong size, PreciseRegionSignal action) => _impl.RegisterPreciseAction(address, size, action);
public void SignalWrite() => _impl.SignalWrite();
}
}

View File

@ -1,37 +0,0 @@
using Ryujinx.Memory.Tracking;
using System;
namespace Ryujinx.Cpu.Tracking
{
public class CpuRegionHandle : IRegionHandle
{
private readonly RegionHandle _impl;
public bool Dirty => _impl.Dirty;
public bool Unmapped => _impl.Unmapped;
public ulong Address => _impl.Address;
public ulong Size => _impl.Size;
public ulong EndAddress => _impl.EndAddress;
internal CpuRegionHandle(RegionHandle impl)
{
_impl = impl;
}
public void Dispose() => _impl.Dispose();
public bool DirtyOrVolatile() => _impl.DirtyOrVolatile();
public void ForceDirty() => _impl.ForceDirty();
public IRegionHandle GetHandle() => _impl;
public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action);
public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action);
public void RegisterDirtyEvent(Action action) => _impl.RegisterDirtyEvent(action);
public void Reprotect(bool asDirty = false) => _impl.Reprotect(asDirty);
public bool OverlapsWith(ulong address, ulong size) => _impl.OverlapsWith(address, size);
public bool RangeEquals(CpuRegionHandle other)
{
return _impl.RealAddress == other._impl.RealAddress && _impl.RealSize == other._impl.RealSize;
}
}
}

View File

@ -1,26 +0,0 @@
using Ryujinx.Memory.Tracking;
using System;
namespace Ryujinx.Cpu.Tracking
{
public class CpuSmartMultiRegionHandle : IMultiRegionHandle
{
private readonly SmartMultiRegionHandle _impl;
public bool Dirty => _impl.Dirty;
internal CpuSmartMultiRegionHandle(SmartMultiRegionHandle impl)
{
_impl = impl;
}
public void Dispose() => _impl.Dispose();
public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size);
public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action);
public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action);
public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction);
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction);
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber);
public void SignalWrite() => _impl.SignalWrite();
}
}

View File

@ -0,0 +1,8 @@
namespace Ryujinx.Graphics.GAL
{
public enum BufferAccess
{
Default,
FlushPersistent
}
}

View File

@ -21,11 +21,14 @@ namespace Ryujinx.Graphics.GAL
{ {
return CreateBuffer(size, BufferHandle.Null); return CreateBuffer(size, BufferHandle.Null);
} }
BufferHandle CreateBuffer(nint pointer, int size);
BufferHandle CreateBuffer(int size, BufferAccess access);
IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info); IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info);
ISampler CreateSampler(SamplerCreateInfo info); ISampler CreateSampler(SamplerCreateInfo info);
ITexture CreateTexture(TextureCreateInfo info, float scale); ITexture CreateTexture(TextureCreateInfo info, float scale);
bool PrepareHostMapping(nint address, ulong size);
void CreateSync(ulong id, bool strict); void CreateSync(ulong id, bool strict);

View File

@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.GAL
void CopyTo(ITexture destination, int firstLayer, int firstLevel); void CopyTo(ITexture destination, int firstLayer, int firstLevel);
void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel); void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel);
void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter); void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter);
void CopyTo(BufferRange range, int layer, int level, int stride);
ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel); ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel);

View File

@ -43,6 +43,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<ActionCommand>(CommandType.Action); Register<ActionCommand>(CommandType.Action);
Register<CreateBufferCommand>(CommandType.CreateBuffer); Register<CreateBufferCommand>(CommandType.CreateBuffer);
Register<CreateBufferAccessCommand>(CommandType.CreateBufferAccess);
Register<CreateHostBufferCommand>(CommandType.CreateHostBuffer);
Register<CreateProgramCommand>(CommandType.CreateProgram); Register<CreateProgramCommand>(CommandType.CreateProgram);
Register<CreateSamplerCommand>(CommandType.CreateSampler); Register<CreateSamplerCommand>(CommandType.CreateSampler);
Register<CreateSyncCommand>(CommandType.CreateSync); Register<CreateSyncCommand>(CommandType.CreateSync);
@ -69,6 +71,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<TextureCopyToCommand>(CommandType.TextureCopyTo); Register<TextureCopyToCommand>(CommandType.TextureCopyTo);
Register<TextureCopyToScaledCommand>(CommandType.TextureCopyToScaled); Register<TextureCopyToScaledCommand>(CommandType.TextureCopyToScaled);
Register<TextureCopyToSliceCommand>(CommandType.TextureCopyToSlice); Register<TextureCopyToSliceCommand>(CommandType.TextureCopyToSlice);
Register<TextureCopyToBufferCommand>(CommandType.TextureCopyToBuffer);
Register<TextureCreateViewCommand>(CommandType.TextureCreateView); Register<TextureCreateViewCommand>(CommandType.TextureCreateView);
Register<TextureGetDataCommand>(CommandType.TextureGetData); Register<TextureGetDataCommand>(CommandType.TextureGetData);
Register<TextureGetDataSliceCommand>(CommandType.TextureGetDataSlice); Register<TextureGetDataSliceCommand>(CommandType.TextureGetDataSlice);

View File

@ -4,6 +4,8 @@
{ {
Action, Action,
CreateBuffer, CreateBuffer,
CreateBufferAccess,
CreateHostBuffer,
CreateProgram, CreateProgram,
CreateSampler, CreateSampler,
CreateSync, CreateSync,
@ -29,6 +31,7 @@
SamplerDispose, SamplerDispose,
TextureCopyTo, TextureCopyTo,
TextureCopyToBuffer,
TextureCopyToScaled, TextureCopyToScaled,
TextureCopyToSlice, TextureCopyToSlice,
TextureCreateView, TextureCreateView,

View File

@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
{
struct CreateBufferAccessCommand : IGALCommand, IGALCommand<CreateBufferAccessCommand>
{
public CommandType CommandType => CommandType.CreateBufferAccess;
private BufferHandle _threadedHandle;
private int _size;
private BufferAccess _access;
public void Set(BufferHandle threadedHandle, int size, BufferAccess access)
{
_threadedHandle = threadedHandle;
_size = size;
_access = access;
}
public static void Run(ref CreateBufferAccessCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._size, command._access));
}
}
}

View File

@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
{
struct CreateHostBufferCommand : IGALCommand, IGALCommand<CreateHostBufferCommand>
{
public CommandType CommandType => CommandType.CreateHostBuffer;
private BufferHandle _threadedHandle;
private nint _pointer;
private int _size;
public void Set(BufferHandle threadedHandle, nint pointer, int size)
{
_threadedHandle = threadedHandle;
_pointer = pointer;
_size = size;
}
public static void Run(ref CreateHostBufferCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
threaded.Buffers.AssignBuffer(command._threadedHandle, renderer.CreateBuffer(command._pointer, command._size));
}
}
}

View File

@ -0,0 +1,29 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
{
struct TextureCopyToBufferCommand : IGALCommand, IGALCommand<TextureCopyToBufferCommand>
{
public CommandType CommandType => CommandType.TextureCopyToBuffer;
private TableRef<ThreadedTexture> _texture;
private BufferRange _range;
private int _layer;
private int _level;
private int _stride;
public void Set(TableRef<ThreadedTexture> texture, BufferRange range, int layer, int level, int stride)
{
_texture = texture;
_range = range;
_layer = layer;
_level = level;
_stride = stride;
}
public static void Run(ref TextureCopyToBufferCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
command._texture.Get(threaded).Base.CopyTo(threaded.Buffers.MapBufferRange(command._range), command._layer, command._level, command._stride);
}
}
}

View File

@ -108,6 +108,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
} }
} }
public void CopyTo(BufferRange range, int layer, int level, int stride)
{
_renderer.New<TextureCopyToBufferCommand>().Set(Ref(this), range, layer, level, stride);
_renderer.QueueCommand();
}
public void SetData(SpanOrArray<byte> data) public void SetData(SpanOrArray<byte> data)
{ {
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray())); _renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray()));

View File

@ -57,6 +57,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
private int _refConsumerPtr; private int _refConsumerPtr;
private Action _interruptAction; private Action _interruptAction;
private object _interruptLock = new();
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
@ -274,6 +275,24 @@ namespace Ryujinx.Graphics.GAL.Multithreading
return handle; return handle;
} }
public BufferHandle CreateBuffer(nint pointer, int size)
{
BufferHandle handle = Buffers.CreateBufferHandle();
New<CreateHostBufferCommand>().Set(handle, pointer, size);
QueueCommand();
return handle;
}
public BufferHandle CreateBuffer(int size, BufferAccess access)
{
BufferHandle handle = Buffers.CreateBufferHandle();
New<CreateBufferAccessCommand>().Set(handle, size, access);
QueueCommand();
return handle;
}
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
{ {
var program = new ThreadedProgram(this); var program = new ThreadedProgram(this);
@ -447,6 +466,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
action(); action();
} }
else else
{
lock (_interruptLock)
{ {
while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { } while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { }
@ -455,12 +476,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_interruptRun.WaitOne(); _interruptRun.WaitOne();
} }
} }
}
public void SetInterruptAction(Action<Action> interruptAction) public void SetInterruptAction(Action<Action> interruptAction)
{ {
// Threaded renderer ignores given interrupt action, as it provides its own to the child renderer. // Threaded renderer ignores given interrupt action, as it provides its own to the child renderer.
} }
public bool PrepareHostMapping(nint address, ulong size)
{
return _baseRenderer.PrepareHostMapping(address, size);
}
public void Dispose() public void Dispose()
{ {
// Dispose must happen from the render thread, after all commands have completed. // Dispose must happen from the render thread, after all commands have completed.

View File

@ -40,22 +40,6 @@ namespace Ryujinx.Graphics.Gpu
/// </summary> /// </summary>
public const int TotalTransformFeedbackBuffers = 4; public const int TotalTransformFeedbackBuffers = 4;
/// <summary>
/// Maximum number of textures on a single shader stage.
/// </summary>
/// <remarks>
/// The maximum number of textures is API limited, the hardware supports an unlimited amount.
/// </remarks>
public const int TotalTextures = 32;
/// <summary>
/// Maximum number of images on a single shader stage.
/// </summary>
/// <remarks>
/// The maximum number of images is API limited, the hardware supports an unlimited amount.
/// </remarks>
public const int TotalImages = 8;
/// <summary> /// <summary>
/// Maximum number of render target color buffers. /// Maximum number of render target color buffers.
/// </summary> /// </summary>
@ -100,5 +84,15 @@ namespace Ryujinx.Graphics.Gpu
/// Expected byte alignment for storage buffers /// Expected byte alignment for storage buffers
/// </summary> /// </summary>
public const int StorageAlignment = 16; public const int StorageAlignment = 16;
/// <summary>
/// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses.
/// </summary>
public const int DriverReservedUniformBuffer = 0;
/// <summary>
/// Maximum size that an storage buffer is assumed to have when the correct size is unknown.
/// </summary>
public const ulong MaxUnknownStorageSize = 0x100000;
} }
} }

View File

@ -157,15 +157,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
{ {
BufferDescriptor sb = info.SBuffers[index]; BufferDescriptor sb = info.SBuffers[index];
ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0); ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4;
int sbDescOffset = 0x310 + sb.Slot * 0x10;
sbDescAddress += (ulong)sbDescOffset;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress); SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
_channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); uint size;
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
{
// Only trust the SbDescriptor size if it comes from slot 0.
size = (uint)sbDescriptor.Size;
}
else
{
// TODO: Use full mapped size and somehow speed up buffer sync.
size = (uint)_channel.MemoryManager.GetMappedSize(sbDescriptor.PackAddress(), Constants.MaxUnknownStorageSize);
}
_channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags);
} }
if ((_channel.BufferManager.HasUnalignedStorageBuffers) != hasUnaligned) if ((_channel.BufferManager.HasUnalignedStorageBuffers) != hasUnaligned)

View File

@ -1,5 +1,6 @@
using Ryujinx.Graphics.Device; using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Engine.MME; using Ryujinx.Graphics.Gpu.Engine.MME;
using Ryujinx.Graphics.Gpu.Synchronization;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -59,7 +60,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
if (_createSyncPending) if (_createSyncPending)
{ {
_createSyncPending = false; _createSyncPending = false;
_context.CreateHostSyncIfNeeded(false, false); _context.CreateHostSyncIfNeeded(HostSyncFlags.None);
} }
} }
@ -157,7 +158,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
} }
else if (operation == SyncpointbOperation.Incr) else if (operation == SyncpointbOperation.Incr)
{ {
_context.CreateHostSyncIfNeeded(true, true); _context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint);
_context.Synchronization.IncrementSyncpoint(syncpointId); _context.Synchronization.IncrementSyncpoint(syncpointId);
} }
@ -184,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
{ {
_context.Renderer.Pipeline.CommandBufferBarrier(); _context.Renderer.Pipeline.CommandBufferBarrier();
_context.CreateHostSyncIfNeeded(false, true); _context.CreateHostSyncIfNeeded(HostSyncFlags.Strict);
} }
/// <summary> /// <summary>

View File

@ -252,6 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
public void Interrupt() public void Interrupt()
{ {
_interrupt = true; _interrupt = true;
_event.Set();
} }
/// <summary> /// <summary>

View File

@ -351,15 +351,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
BufferDescriptor sb = info.SBuffers[index]; BufferDescriptor sb = info.SBuffers[index];
ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, 0); ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4;
int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10;
sbDescAddress += (ulong)sbDescOffset;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress); SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
_channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); uint size;
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
{
// Only trust the SbDescriptor size if it comes from slot 0.
size = (uint)sbDescriptor.Size;
}
else
{
// TODO: Use full mapped size and somehow speed up buffer sync.
size = (uint)_channel.MemoryManager.GetMappedSize(sbDescriptor.PackAddress(), Constants.MaxUnknownStorageSize);
}
_channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags);
} }
} }
} }

View File

@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory; using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender; using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
using Ryujinx.Graphics.Gpu.Synchronization;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -254,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
uint syncpointId = (uint)argument & 0xFFFF; uint syncpointId = (uint)argument & 0xFFFF;
_context.AdvanceSequence(); _context.AdvanceSequence();
_context.CreateHostSyncIfNeeded(true, true); _context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint);
_context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result. _context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
_context.Synchronization.IncrementSyncpoint(syncpointId); _context.Synchronization.IncrementSyncpoint(syncpointId);
} }

View File

@ -300,11 +300,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
IsCopyRegionComplete(srcCopyTexture, srcCopyTextureFormat, srcX1, srcY1, srcX2, srcY2) && IsCopyRegionComplete(srcCopyTexture, srcCopyTextureFormat, srcX1, srcY1, srcX2, srcY2) &&
IsCopyRegionComplete(dstCopyTexture, dstCopyTextureFormat, dstX1, dstY1, dstX2, dstY2); IsCopyRegionComplete(dstCopyTexture, dstCopyTextureFormat, dstX1, dstY1, dstX2, dstY2);
// We can only allow aliasing of color formats as depth if the source and destination textures
// are the same, as we can't blit between different depth formats.
bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format;
var srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture( var srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager, memoryManager,
srcCopyTexture, srcCopyTexture,
offset, offset,
srcCopyTextureFormat, srcCopyTextureFormat,
srcDepthAlias,
!canDirectCopy, !canDirectCopy,
false, false,
srcHint); srcHint);
@ -325,6 +330,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
// When the source texture that was found has a depth format, // When the source texture that was found has a depth format,
// we must enforce the target texture also has a depth format, // we must enforce the target texture also has a depth format,
// as copies between depth and color formats are not allowed. // as copies between depth and color formats are not allowed.
// For depth blit, the destination texture format should always match exactly.
if (srcTexture.Format.IsDepthOrStencil()) if (srcTexture.Format.IsDepthOrStencil())
{ {
@ -340,7 +346,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
dstCopyTexture, dstCopyTexture,
0, 0,
dstCopyTextureFormat, dstCopyTextureFormat,
true, depthAlias: false,
shouldCreate: true,
srcTexture.ScaleMode == TextureScaleMode.Scaled, srcTexture.ScaleMode == TextureScaleMode.Scaled,
dstHint); dstHint);

View File

@ -60,14 +60,14 @@ namespace Ryujinx.Graphics.Gpu
/// If there are more than 0 items when this happens, a host sync object will be generated for the given <see cref="SyncNumber"/>, /// If there are more than 0 items when this happens, a host sync object will be generated for the given <see cref="SyncNumber"/>,
/// and the SyncNumber will be incremented. /// and the SyncNumber will be incremented.
/// </summary> /// </summary>
internal List<Action> SyncActions { get; } internal List<ISyncActionHandler> SyncActions { get; }
/// <summary> /// <summary>
/// Actions to be performed when a CPU waiting syncpoint is triggered. /// Actions to be performed when a CPU waiting syncpoint is triggered.
/// If there are more than 0 items when this happens, a host sync object will be generated for the given <see cref="SyncNumber"/>, /// If there are more than 0 items when this happens, a host sync object will be generated for the given <see cref="SyncNumber"/>,
/// and the SyncNumber will be incremented. /// and the SyncNumber will be incremented.
/// </summary> /// </summary>
internal List<Action> SyncpointActions { get; } internal List<ISyncActionHandler> SyncpointActions { get; }
/// <summary> /// <summary>
/// Buffer migrations that are currently in-flight. These are checked whenever sync is created to determine if buffer migration /// Buffer migrations that are currently in-flight. These are checked whenever sync is created to determine if buffer migration
@ -98,6 +98,9 @@ namespace Ryujinx.Graphics.Gpu
private Thread _gpuThread; private Thread _gpuThread;
private bool _pendingSync; private bool _pendingSync;
private long _modifiedSequence;
private ulong _firstTimestamp;
/// <summary> /// <summary>
/// Creates a new instance of the GPU emulation context. /// Creates a new instance of the GPU emulation context.
/// </summary> /// </summary>
@ -114,13 +117,15 @@ namespace Ryujinx.Graphics.Gpu
HostInitalized = new ManualResetEvent(false); HostInitalized = new ManualResetEvent(false);
SyncActions = new List<Action>(); SyncActions = new List<ISyncActionHandler>();
SyncpointActions = new List<Action>(); SyncpointActions = new List<ISyncActionHandler>();
BufferMigrations = new List<BufferMigration>(); BufferMigrations = new List<BufferMigration>();
DeferredActions = new Queue<Action>(); DeferredActions = new Queue<Action>();
PhysicalMemoryRegistry = new ConcurrentDictionary<ulong, PhysicalMemory>(); PhysicalMemoryRegistry = new ConcurrentDictionary<ulong, PhysicalMemory>();
_firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
} }
/// <summary> /// <summary>
@ -200,13 +205,23 @@ namespace Ryujinx.Graphics.Gpu
return divided * NsToTicksFractionNumerator + errorBias; return divided * NsToTicksFractionNumerator + errorBias;
} }
/// <summary>
/// Gets a sequence number for resource modification ordering. This increments on each call.
/// </summary>
/// <returns>A sequence number for resource modification ordering</returns>
public long GetModifiedSequence()
{
return _modifiedSequence++;
}
/// <summary> /// <summary>
/// Gets the value of the GPU timer. /// Gets the value of the GPU timer.
/// </summary> /// </summary>
/// <returns>The current GPU timestamp</returns> /// <returns>The current GPU timestamp</returns>
public ulong GetTimestamp() public ulong GetTimestamp()
{ {
ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); // Guest timestamp will start at 0, instead of host value.
ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds) - _firstTimestamp;
if (GraphicsConfig.FastGpuTime) if (GraphicsConfig.FastGpuTime)
{ {
@ -296,9 +311,9 @@ namespace Ryujinx.Graphics.Gpu
/// Registers an action to be performed the next time a syncpoint is incremented. /// Registers an action to be performed the next time a syncpoint is incremented.
/// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented. /// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented.
/// </summary> /// </summary>
/// <param name="action">The action to be performed on sync object creation</param> /// <param name="action">The resource with action to be performed on sync object creation</param>
/// <param name="syncpointOnly">True if the sync action should only run when syncpoints are incremented</param> /// <param name="syncpointOnly">True if the sync action should only run when syncpoints are incremented</param>
public void RegisterSyncAction(Action action, bool syncpointOnly = false) internal void RegisterSyncAction(ISyncActionHandler action, bool syncpointOnly = false)
{ {
if (syncpointOnly) if (syncpointOnly)
{ {
@ -315,10 +330,13 @@ namespace Ryujinx.Graphics.Gpu
/// Creates a host sync object if there are any pending sync actions. The actions will then be called. /// Creates a host sync object if there are any pending sync actions. The actions will then be called.
/// If no actions are present, a host sync object is not created. /// If no actions are present, a host sync object is not created.
/// </summary> /// </summary>
/// <param name="syncpoint">True if host sync is being created by a syncpoint</param> /// <param name="flags">Modifiers for how host sync should be created</param>
/// <param name="strict">True if the sync should signal as soon as possible</param> internal void CreateHostSyncIfNeeded(HostSyncFlags flags)
public void CreateHostSyncIfNeeded(bool syncpoint, bool strict)
{ {
bool syncpoint = flags.HasFlag(HostSyncFlags.Syncpoint);
bool strict = flags.HasFlag(HostSyncFlags.Strict);
bool force = flags.HasFlag(HostSyncFlags.Force);
if (BufferMigrations.Count > 0) if (BufferMigrations.Count > 0)
{ {
ulong currentSyncNumber = Renderer.GetCurrentSync(); ulong currentSyncNumber = Renderer.GetCurrentSync();
@ -336,24 +354,17 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
if (_pendingSync || (syncpoint && SyncpointActions.Count > 0)) if (force || _pendingSync || (syncpoint && SyncpointActions.Count > 0))
{ {
Renderer.CreateSync(SyncNumber, strict); Renderer.CreateSync(SyncNumber, strict);
SyncActions.ForEach(action => action.SyncPreAction(syncpoint));
SyncpointActions.ForEach(action => action.SyncPreAction(syncpoint));
SyncNumber++; SyncNumber++;
foreach (Action action in SyncActions) SyncActions.RemoveAll(action => action.SyncAction(syncpoint));
{ SyncpointActions.RemoveAll(action => action.SyncAction(syncpoint));
action();
}
foreach (Action action in SyncpointActions)
{
action();
}
SyncActions.Clear();
SyncpointActions.Clear();
} }
_pendingSync = false; _pendingSync = false;

View File

@ -1,5 +1,4 @@
using System.Collections; using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
@ -9,6 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
class ShortTextureCacheEntry class ShortTextureCacheEntry
{ {
public bool IsAutoDelete;
public readonly TextureDescriptor Descriptor; public readonly TextureDescriptor Descriptor;
public readonly int InvalidatedSequence; public readonly int InvalidatedSequence;
public readonly Texture Texture; public readonly Texture Texture;
@ -24,6 +24,17 @@ namespace Ryujinx.Graphics.Gpu.Image
InvalidatedSequence = texture.InvalidatedSequence; InvalidatedSequence = texture.InvalidatedSequence;
Texture = texture; Texture = texture;
} }
/// <summary>
/// Create a new entry on the short duration texture cache from the auto delete cache.
/// </summary>
/// <param name="texture">The texture</param>
public ShortTextureCacheEntry(Texture texture)
{
IsAutoDelete = true;
InvalidatedSequence = texture.InvalidatedSequence;
Texture = texture;
}
} }
/// <summary> /// <summary>
@ -199,7 +210,11 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
texture.DecrementReferenceCount(); texture.DecrementReferenceCount();
if (!texture.ShortCacheEntry.IsAutoDelete)
{
_shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor); _shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor);
}
texture.ShortCacheEntry = null; texture.ShortCacheEntry = null;
} }
} }
@ -222,6 +237,25 @@ namespace Ryujinx.Graphics.Gpu.Image
texture.IncrementReferenceCount(); texture.IncrementReferenceCount();
} }
/// <summary>
/// Adds a texture to the short duration cache without a descriptor. This typically keeps it alive for two ticks.
/// On expiry, it will be removed from the AutoDeleteCache.
/// </summary>
/// <param name="texture">Texture to add to the short cache</param>
public void AddShortCache(Texture texture)
{
if (texture.ShortCacheEntry != null)
{
var entry = new ShortTextureCacheEntry(texture);
_shortCacheBuilder.Add(entry);
texture.ShortCacheEntry = entry;
texture.IncrementReferenceCount();
}
}
/// <summary> /// <summary>
/// Delete textures from the short duration cache. /// Delete textures from the short duration cache.
/// Moves the builder set to be deleted on next process. /// Moves the builder set to be deleted on next process.
@ -234,7 +268,15 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
entry.Texture.DecrementReferenceCount(); entry.Texture.DecrementReferenceCount();
if (entry.IsAutoDelete)
{
Remove(entry.Texture, false);
}
else
{
_shortCacheLookup.Remove(entry.Descriptor); _shortCacheLookup.Remove(entry.Descriptor);
}
entry.Texture.ShortCacheEntry = null; entry.Texture.ShortCacheEntry = null;
} }

View File

@ -1,5 +1,5 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Memory.Tracking;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public ulong Size { get; } public ulong Size { get; }
private readonly CpuMultiRegionHandle _memoryTracking; private readonly MultiRegionHandle _memoryTracking;
private readonly Action<ulong, ulong> _modifiedDelegate; private readonly Action<ulong, ulong> _modifiedDelegate;
private int _modifiedSequenceOffset; private int _modifiedSequenceOffset;

View File

@ -144,6 +144,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public ShortTextureCacheEntry ShortCacheEntry { get; set; } public ShortTextureCacheEntry ShortCacheEntry { get; set; }
/// <summary>
/// Whether this texture has ever been referenced by a pool.
/// </summary>
public bool HadPoolOwner { get; private set; }
/// Physical memory ranges where the texture data is located. /// Physical memory ranges where the texture data is located.
/// </summary> /// </summary>
public MultiRange Range { get; private set; } public MultiRange Range { get; private set; }
@ -1113,7 +1118,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0; bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0;
TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0); TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.DepthAlias) != 0);
if (matchQuality == TextureMatchQuality.NoMatch) if (matchQuality == TextureMatchQuality.NoMatch)
{ {
@ -1165,6 +1170,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="caps">Host GPU capabilities</param> /// <param name="caps">Host GPU capabilities</param>
/// <param name="firstLayer">Texture view initial layer on this texture</param> /// <param name="firstLayer">Texture view initial layer on this texture</param>
/// <param name="firstLevel">Texture view first mipmap level on this texture</param> /// <param name="firstLevel">Texture view first mipmap level on this texture</param>
/// <param name="flags">Texture search flags</param>
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns> /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
public TextureViewCompatibility IsViewCompatible( public TextureViewCompatibility IsViewCompatible(
TextureInfo info, TextureInfo info,
@ -1173,11 +1179,12 @@ namespace Ryujinx.Graphics.Gpu.Image
int layerSize, int layerSize,
Capabilities caps, Capabilities caps,
out int firstLayer, out int firstLayer,
out int firstLevel) out int firstLevel,
TextureSearchFlags flags = TextureSearchFlags.None)
{ {
TextureViewCompatibility result = TextureViewCompatibility.Full; TextureViewCompatibility result = TextureViewCompatibility.Full;
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps, flags));
if (result != TextureViewCompatibility.Incompatible) if (result != TextureViewCompatibility.Incompatible)
{ {
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps));
@ -1423,7 +1430,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_scaledSetScore = Math.Max(0, _scaledSetScore - 1); _scaledSetScore = Math.Max(0, _scaledSetScore - 1);
} }
if (_modifiedStale || Group.HasCopyDependencies) if (_modifiedStale || Group.HasCopyDependencies || Group.HasFlushBuffer)
{ {
_modifiedStale = false; _modifiedStale = false;
Group.SignalModifying(this, bound); Group.SignalModifying(this, bound);
@ -1506,10 +1513,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="gpuVa">GPU VA of the pool reference</param> /// <param name="gpuVa">GPU VA of the pool reference</param>
public void IncrementReferenceCount(TexturePool pool, int id, ulong gpuVa) public void IncrementReferenceCount(TexturePool pool, int id, ulong gpuVa)
{ {
HadPoolOwner = true;
lock (_poolOwners) lock (_poolOwners)
{ {
_poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id, GpuAddress = gpuVa }); _poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id, GpuAddress = gpuVa });
} }
_referenceCount++; _referenceCount++;
if (ShortCacheEntry != null) if (ShortCacheEntry != null)
@ -1594,7 +1604,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_poolOwners.Clear(); _poolOwners.Clear();
} }
if (ShortCacheEntry != null && _context.IsGpuThread()) if (ShortCacheEntry != null && !ShortCacheEntry.IsAutoDelete && _context.IsGpuThread())
{ {
// If this is called from another thread (unmapped), the short cache will // If this is called from another thread (unmapped), the short cache will
// have to remove this texture on a future tick. // have to remove this texture on a future tick.
@ -1610,6 +1620,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public void UpdatePoolMappings() public void UpdatePoolMappings()
{ {
ChangedMapping = true;
lock (_poolOwners) lock (_poolOwners)
{ {
ulong address = 0; ulong address = 0;
@ -1683,10 +1695,10 @@ namespace Ryujinx.Graphics.Gpu.Image
if (Group.Storage == this) if (Group.Storage == this)
{ {
Group.Unmapped();
Group.ClearModified(unmapRange); Group.ClearModified(unmapRange);
} }
UpdatePoolMappings();
} }
/// <summary> /// <summary>

View File

@ -537,7 +537,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (hostTexture != null && texture.Target == Target.TextureBuffer) if (hostTexture != null && texture.Target == Target.TextureBuffer)
{ {
// Ensure that the buffer texture is using the correct buffer as storage. // Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind // 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. // to ensure we're not using a old buffer that was already deleted.
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false); _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
@ -666,7 +666,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (hostTexture != null && texture.Target == Target.TextureBuffer) if (hostTexture != null && texture.Target == Target.TextureBuffer)
{ {
// Ensure that the buffer texture is using the correct buffer as storage. // Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind // 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. // to ensure we're not using a old buffer that was already deleted.
Format format = bindingInfo.Format; Format format = bindingInfo.Format;

View File

@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
/// <summary> /// <summary>
/// Handles removal of textures written to a memory region being unmapped. /// Handles marking of textures written to a memory region being (partially) remapped.
/// </summary> /// </summary>
/// <param name="sender">Sender object</param> /// <param name="sender">Sender object</param>
/// <param name="e">Event arguments</param> /// <param name="e">Event arguments</param>
@ -80,26 +80,41 @@ namespace Ryujinx.Graphics.Gpu.Image
overlapCount = _textures.FindOverlaps(unmapped, ref overlaps); overlapCount = _textures.FindOverlaps(unmapped, ref overlaps);
} }
if (overlapCount > 0)
{
for (int i = 0; i < overlapCount; i++) for (int i = 0; i < overlapCount; i++)
{ {
overlaps[i].Unmapped(unmapped); overlaps[i].Unmapped(unmapped);
} }
}
// If any range was previously unmapped, we also need to purge lock (_partiallyMappedTextures)
// all partially mapped texture, as they might be fully mapped now.
for (int i = 0; i < unmapped.Count; i++)
{ {
if (unmapped.GetSubRange(i).Address == MemoryManager.PteUnmapped) if (overlapCount > 0 || _partiallyMappedTextures.Count > 0)
{
e.AddRemapAction(() =>
{ {
lock (_partiallyMappedTextures) lock (_partiallyMappedTextures)
{ {
foreach (var texture in _partiallyMappedTextures) if (overlapCount > 0)
{ {
texture.Unmapped(unmapped); for (int i = 0; i < overlapCount; i++)
{
_partiallyMappedTextures.Add(overlaps[i]);
} }
} }
break; // Any texture that has been unmapped at any point or is partially unmapped
// should update their pool references after the remap completes.
MultiRange unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
foreach (var texture in _partiallyMappedTextures)
{
texture.UpdatePoolMappings();
}
}
});
} }
} }
} }
@ -234,6 +249,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="copyTexture">Copy texture to find or create</param> /// <param name="copyTexture">Copy texture to find or create</param>
/// <param name="offset">Offset to be added to the physical texture address</param> /// <param name="offset">Offset to be added to the physical texture address</param>
/// <param name="formatInfo">Format information of the copy texture</param> /// <param name="formatInfo">Format information of the copy texture</param>
/// <param name="depthAlias">Indicates if aliasing between color and depth format should be allowed</param>
/// <param name="shouldCreate">Indicates if a new texture should be created if none is found on the cache</param>
/// <param name="preferScaling">Indicates if the texture should be scaled from the start</param> /// <param name="preferScaling">Indicates if the texture should be scaled from the start</param>
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param> /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <returns>The texture</returns> /// <returns>The texture</returns>
@ -242,6 +259,7 @@ namespace Ryujinx.Graphics.Gpu.Image
TwodTexture copyTexture, TwodTexture copyTexture,
ulong offset, ulong offset,
FormatInfo formatInfo, FormatInfo formatInfo,
bool depthAlias,
bool shouldCreate, bool shouldCreate,
bool preferScaling, bool preferScaling,
Size sizeHint) Size sizeHint)
@ -278,6 +296,11 @@ namespace Ryujinx.Graphics.Gpu.Image
TextureSearchFlags flags = TextureSearchFlags.ForCopy; TextureSearchFlags flags = TextureSearchFlags.ForCopy;
if (depthAlias)
{
flags |= TextureSearchFlags.DepthAlias;
}
if (preferScaling) if (preferScaling)
{ {
flags |= TextureSearchFlags.WithUpscale; flags |= TextureSearchFlags.WithUpscale;
@ -546,7 +569,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Texture texture = null; Texture texture = null;
TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch; long bestSequence = 0;
for (int index = 0; index < sameAddressOverlapsCount; index++) for (int index = 0; index < sameAddressOverlapsCount; index++)
{ {
@ -578,17 +601,12 @@ namespace Ryujinx.Graphics.Gpu.Image
continue; continue;
} }
} }
}
if (matchQuality == TextureMatchQuality.Perfect) if (texture == null || overlap.Group.ModifiedSequence - bestSequence > 0)
{ {
texture = overlap; texture = overlap;
break; bestSequence = overlap.Group.ModifiedSequence;
} }
else if (matchQuality > bestQuality)
{
texture = overlap;
bestQuality = matchQuality;
} }
} }
@ -641,6 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int fullyCompatible = 0; int fullyCompatible = 0;
// Evaluate compatibility of overlaps, add temporary references // Evaluate compatibility of overlaps, add temporary references
int preferredOverlap = -1;
for (int index = 0; index < overlapsCount; index++) for (int index = 0; index < overlapsCount; index++)
{ {
@ -652,17 +671,26 @@ namespace Ryujinx.Graphics.Gpu.Image
sizeInfo.LayerSize, sizeInfo.LayerSize,
_context.Capabilities, _context.Capabilities,
out int firstLayer, out int firstLayer,
out int firstLevel); out int firstLevel,
flags);
if (overlapCompatibility == TextureViewCompatibility.Full) if (overlapCompatibility >= TextureViewCompatibility.FormatAlias)
{ {
if (overlap.IsView) if (overlap.IsView)
{ {
overlapCompatibility = TextureViewCompatibility.CopyOnly; overlapCompatibility = overlapCompatibility == TextureViewCompatibility.FormatAlias ?
TextureViewCompatibility.Incompatible :
TextureViewCompatibility.CopyOnly;
} }
else else
{ {
fullyCompatible++; fullyCompatible++;
if (preferredOverlap == -1 || overlap.Group.ModifiedSequence - bestSequence > 0)
{
preferredOverlap = index;
bestSequence = overlap.Group.ModifiedSequence;
}
} }
} }
@ -672,26 +700,38 @@ namespace Ryujinx.Graphics.Gpu.Image
// Search through the overlaps to find a compatible view and establish any copy dependencies. // Search through the overlaps to find a compatible view and establish any copy dependencies.
for (int index = 0; index < overlapsCount; index++) if (preferredOverlap != -1)
{ {
Texture overlap = _textureOverlaps[index]; Texture overlap = _textureOverlaps[preferredOverlap];
OverlapInfo oInfo = _overlapInfo[index]; OverlapInfo oInfo = _overlapInfo[preferredOverlap];
bool aliased = oInfo.Compatibility == TextureViewCompatibility.FormatAlias;
if (oInfo.Compatibility == TextureViewCompatibility.Full)
{
if (!isSamplerTexture) if (!isSamplerTexture)
{ {
// If this is not a sampler texture, the size might be different from the requested size, // If this is not a sampler texture, the size might be different from the requested size,
// so we need to make sure the texture information has the correct size for this base texture, // so we need to make sure the texture information has the correct size for this base texture,
// before creating the view. // before creating the view.
info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel);
info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel, aliased);
}
else if (aliased)
{
// The format must be changed to match the parent.
info = info.CreateInfoWithFormat(overlap.Info.FormatInfo);
} }
texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
texture.SynchronizeMemory(); texture.SynchronizeMemory();
break;
} }
else if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0) else
{
for (int index = 0; index < overlapsCount; index++)
{
Texture overlap = _textureOverlaps[index];
OverlapInfo oInfo = _overlapInfo[index];
if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0)
{ {
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead. // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
@ -705,6 +745,7 @@ namespace Ryujinx.Graphics.Gpu.Image
break; break;
} }
} }
}
if (texture != null) if (texture != null)
{ {
@ -717,7 +758,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Texture overlap = _textureOverlaps[index]; Texture overlap = _textureOverlaps[index];
OverlapInfo oInfo = _overlapInfo[index]; OverlapInfo oInfo = _overlapInfo[index];
if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible) if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible || oInfo.Compatibility == TextureViewCompatibility.FormatAlias)
{ {
if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility)) if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility))
{ {
@ -832,9 +873,19 @@ namespace Ryujinx.Graphics.Gpu.Image
setData |= modified || flush; setData |= modified || flush;
if (overlapInCache) if (overlapInCache)
{
if (flush || overlap.HadPoolOwner || overlap.IsView)
{ {
_cache.Remove(overlap, flush); _cache.Remove(overlap, flush);
} }
else
{
// This texture has only ever been referenced in the AutoDeleteCache.
// Keep this texture alive with the short duration cache, as it may be used often but not sampled.
_cache.AddShortCache(overlap);
}
}
removeOverlap = modified; removeOverlap = modified;
} }
@ -1135,6 +1186,44 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
/// <summary>
/// Queries a texture's memory range and marks it as partially mapped or not.
/// Partially mapped textures re-evaluate their memory range after each time GPU memory is mapped.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="address">The virtual address of the texture</param>
/// <param name="texture">The texture to be marked</param>
/// <returns>The physical regions for the texture, found when evaluating whether the texture was partially mapped</returns>
public MultiRange UpdatePartiallyMapped(MemoryManager memoryManager, ulong address, Texture texture)
{
MultiRange range;
lock (_partiallyMappedTextures)
{
range = memoryManager.GetPhysicalRegions(address, texture.Size);
bool partiallyMapped = false;
for (int i = 0; i < range.Count; i++)
{
if (range.GetSubRange(i).Address == MemoryManager.PteUnmapped)
{
partiallyMapped = true;
break;
}
}
if (partiallyMapped)
{
_partiallyMappedTextures.Add(texture);
}
else
{
_partiallyMappedTextures.Remove(texture);
}
}
return range;
}
/// <summary> /// <summary>
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks. /// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
/// </summary> /// </summary>
@ -1145,6 +1234,16 @@ namespace Ryujinx.Graphics.Gpu.Image
_cache.AddShortCache(texture, ref descriptor); _cache.AddShortCache(texture, ref descriptor);
} }
/// <summary>
/// Adds a texture to the short duration cache without a descriptor. This typically keeps it alive for two ticks.
/// On expiry, it will be removed from the AutoDeleteCache.
/// </summary>
/// <param name="texture">Texture to add to the short cache</param>
public void AddShortCache(Texture texture)
{
_cache.AddShortCache(texture);
}
/// <summary> /// <summary>
/// Removes a texture from the short duration cache. /// Removes a texture from the short duration cache.
/// </summary> /// </summary>

View File

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

View File

@ -1,10 +1,10 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -57,11 +57,22 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public bool HasCopyDependencies { get; set; } public bool HasCopyDependencies { get; set; }
/// <summary>
/// Indicates if the texture group has a pre-emptive flush buffer.
/// When one is present, the group must always be notified on unbind.
/// </summary>
public bool HasFlushBuffer => _flushBuffer != BufferHandle.Null;
/// <summary> /// <summary>
/// Indicates if this texture has any incompatible overlaps alive. /// Indicates if this texture has any incompatible overlaps alive.
/// </summary> /// </summary>
public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0; public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0;
/// <summary>
/// Number indicating the order this texture group was modified relative to others.
/// </summary>
public long ModifiedSequence { get; private set; }
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory; private readonly PhysicalMemory _physicalMemory;
@ -89,6 +100,10 @@ namespace Ryujinx.Graphics.Gpu.Image
private bool _incompatibleOverlapsDirty = true; private bool _incompatibleOverlapsDirty = true;
private bool _flushIncompatibleOverlaps; private bool _flushIncompatibleOverlaps;
private BufferHandle _flushBuffer;
private bool _flushBufferImported;
private bool _flushBufferInvalid;
/// <summary> /// <summary>
/// Create a new texture group. /// Create a new texture group.
/// </summary> /// </summary>
@ -245,7 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
TextureGroupHandle group = _handles[baseHandle + i]; TextureGroupHandle group = _handles[baseHandle + i];
foreach (CpuRegionHandle handle in group.Handles) foreach (RegionHandle handle in group.Handles)
{ {
if (handle.Dirty) if (handle.Dirty)
{ {
@ -286,7 +301,7 @@ namespace Ryujinx.Graphics.Gpu.Image
bool handleDirty = false; bool handleDirty = false;
bool handleUnmapped = false; bool handleUnmapped = false;
foreach (CpuRegionHandle handle in group.Handles) foreach (RegionHandle handle in group.Handles)
{ {
if (handle.Dirty) if (handle.Dirty)
{ {
@ -464,8 +479,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </remarks> /// </remarks>
/// <param name="tracked">True if writing the texture data is tracked, false otherwise</param> /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
/// <param name="sliceIndex">The index of the slice to flush</param> /// <param name="sliceIndex">The index of the slice to flush</param>
/// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param>
/// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param> /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, ITexture texture = null) private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, bool inBuffer, ITexture texture = null)
{ {
(int layer, int level) = GetLayerLevelForView(sliceIndex); (int layer, int level) = GetLayerLevelForView(sliceIndex);
@ -475,8 +491,17 @@ namespace Ryujinx.Graphics.Gpu.Image
using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked); using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked);
if (inBuffer)
{
using PinnedSpan<byte> data = _context.Renderer.GetBufferData(_flushBuffer, offset, size);
Storage.ConvertFromHostCompatibleFormat(region.Memory.Span, data.Get(), level, true);
}
else
{
Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture); Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture);
} }
}
/// <summary> /// <summary>
/// Gets and flushes a number of slices of the storage texture to guest memory. /// Gets and flushes a number of slices of the storage texture to guest memory.
@ -484,12 +509,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="tracked">True if writing the texture data is tracked, false otherwise</param> /// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
/// <param name="sliceStart">The first slice to flush</param> /// <param name="sliceStart">The first slice to flush</param>
/// <param name="sliceEnd">The slice to finish flushing on (exclusive)</param> /// <param name="sliceEnd">The slice to finish flushing on (exclusive)</param>
/// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param>
/// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param> /// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, ITexture texture = null) private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, bool inBuffer, ITexture texture = null)
{ {
for (int i = sliceStart; i < sliceEnd; i++) for (int i = sliceStart; i < sliceEnd; i++)
{ {
FlushTextureDataSliceToGuest(tracked, i, texture); FlushTextureDataSliceToGuest(tracked, i, inBuffer, texture);
} }
} }
@ -520,7 +546,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
if (endSlice > startSlice) if (endSlice > startSlice)
{ {
FlushSliceRange(tracked, startSlice, endSlice); FlushSliceRange(tracked, startSlice, endSlice, false);
flushed = true; flushed = true;
} }
@ -553,7 +579,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
else else
{ {
FlushSliceRange(tracked, startSlice, endSlice); FlushSliceRange(tracked, startSlice, endSlice, false);
} }
flushed = true; flushed = true;
@ -565,6 +591,58 @@ namespace Ryujinx.Graphics.Gpu.Image
return flushed; return flushed;
} }
/// <summary>
/// Flush the texture data into a persistently mapped buffer.
/// If the buffer does not exist, this method will create it.
/// </summary>
/// <param name="handle">Handle of the texture group to flush slices of</param>
public void FlushIntoBuffer(TextureGroupHandle handle)
{
// Ensure that the buffer exists.
if (_flushBufferInvalid && _flushBuffer != BufferHandle.Null)
{
_flushBufferInvalid = false;
_context.Renderer.DeleteBuffer(_flushBuffer);
_flushBuffer = BufferHandle.Null;
}
if (_flushBuffer == BufferHandle.Null)
{
if (!TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities))
{
return;
}
bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel;
var hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0;
if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size))
{
_flushBuffer = _context.Renderer.CreateBuffer(hostPointer, (int)Storage.Size);
_flushBufferImported = true;
}
else
{
_flushBuffer = _context.Renderer.CreateBuffer((int)Storage.Size, BufferAccess.FlushPersistent);
_flushBufferImported = false;
}
Storage.BlacklistScale();
}
int sliceStart = handle.BaseSlice;
int sliceEnd = sliceStart + handle.SliceCount;
for (int i = sliceStart; i < sliceEnd; i++)
{
(int layer, int level) = GetLayerLevelForView(i);
Storage.GetFlushTexture().CopyTo(new BufferRange(_flushBuffer, _allOffsets[i], _sliceSizes[level]), layer, level, _flushBufferImported ? Storage.Info.Stride : 0);
}
}
/// <summary> /// <summary>
/// Clears competing modified flags for all incompatible ranges, if they have possibly been modified. /// Clears competing modified flags for all incompatible ranges, if they have possibly been modified.
/// </summary> /// </summary>
@ -591,6 +669,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture that has been modified</param> /// <param name="texture">The texture that has been modified</param>
public void SignalModified(Texture texture) public void SignalModified(Texture texture)
{ {
ModifiedSequence = _context.GetModifiedSequence();
ClearIncompatibleOverlaps(texture); ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
@ -611,6 +691,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="bound">True if this texture is being bound, false if unbound</param> /// <param name="bound">True if this texture is being bound, false if unbound</param>
public void SignalModifying(Texture texture, bool bound) public void SignalModifying(Texture texture, bool bound)
{ {
ModifiedSequence = _context.GetModifiedSequence();
ClearIncompatibleOverlaps(texture); ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
@ -630,7 +712,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="group">The group to register an action for</param> /// <param name="group">The group to register an action for</param>
public void RegisterAction(TextureGroupHandle group) public void RegisterAction(TextureGroupHandle group)
{ {
foreach (CpuRegionHandle handle in group.Handles) foreach (RegionHandle handle in group.Handles)
{ {
handle.RegisterAction((address, size) => FlushAction(group, address, size)); handle.RegisterAction((address, size) => FlushAction(group, address, size));
} }
@ -912,7 +994,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="address">The start address of the tracked region</param> /// <param name="address">The start address of the tracked region</param>
/// <param name="size">The size of the tracked region</param> /// <param name="size">The size of the tracked region</param>
/// <returns>A CpuRegionHandle covering the given range</returns> /// <returns>A CpuRegionHandle covering the given range</returns>
private CpuRegionHandle GenerateHandle(ulong address, ulong size) private RegionHandle GenerateHandle(ulong address, ulong size)
{ {
return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture); return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture);
} }
@ -932,7 +1014,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel]; int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel];
int size = endOffset - offset; int size = endOffset - offset;
var result = new List<CpuRegionHandle>(); var result = new List<RegionHandle>();
for (int i = 0; i < TextureRange.Count; i++) for (int i = 0; i < TextureRange.Count; i++)
{ {
@ -977,7 +1059,7 @@ namespace Ryujinx.Graphics.Gpu.Image
views, views,
result.ToArray()); result.ToArray());
foreach (CpuRegionHandle handle in result) foreach (RegionHandle handle in result)
{ {
handle.RegisterDirtyEvent(() => DirtyAction(groupHandle)); handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
} }
@ -1175,7 +1257,7 @@ namespace Ryujinx.Graphics.Gpu.Image
continue; continue;
} }
foreach (CpuRegionHandle handle in groupHandle.Handles) foreach (RegionHandle handle in groupHandle.Handles)
{ {
bool hasMatch = false; bool hasMatch = false;
@ -1197,7 +1279,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
else else
{ {
foreach (CpuRegionHandle handle in groupHandle.Handles) foreach (RegionHandle handle in groupHandle.Handles)
{ {
handle.Reprotect(); handle.Reprotect();
} }
@ -1230,7 +1312,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!(_hasMipViews || _hasLayerViews)) if (!(_hasMipViews || _hasLayerViews))
{ {
// Single dirty region. // Single dirty region.
var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count]; var cpuRegionHandles = new RegionHandle[TextureRange.Count];
int count = 0; int count = 0;
for (int i = 0; i < TextureRange.Count; i++) for (int i = 0; i < TextureRange.Count; i++)
@ -1249,7 +1331,7 @@ namespace Ryujinx.Graphics.Gpu.Image
var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles); var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
foreach (CpuRegionHandle handle in cpuRegionHandles) foreach (RegionHandle handle in cpuRegionHandles)
{ {
handle.RegisterDirtyEvent(() => DirtyAction(groupHandle)); handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
} }
@ -1570,10 +1652,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_context.Renderer.BackgroundContextAction(() => _context.Renderer.BackgroundContextAction(() =>
{ {
if (!isGpuThread) bool inBuffer = !isGpuThread && handle.Sync(_context);
{
handle.Sync(_context);
}
Storage.SignalModifiedDirty(); Storage.SignalModifiedDirty();
@ -1585,13 +1664,24 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities)) if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities) && !(inBuffer && _flushBufferImported))
{ {
FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, Storage.GetFlushTexture()); FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, inBuffer, Storage.GetFlushTexture());
} }
}); });
} }
/// <summary>
/// Called if any part of the storage texture is unmapped.
/// </summary>
public void Unmapped()
{
if (_flushBufferImported)
{
_flushBufferInvalid = true;
}
}
/// <summary> /// <summary>
/// Dispose this texture group, disposing all related memory tracking handles. /// Dispose this texture group, disposing all related memory tracking handles.
/// </summary> /// </summary>
@ -1606,6 +1696,11 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == this); incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == this);
} }
if (_flushBuffer != BufferHandle.Null)
{
_context.Renderer.DeleteBuffer(_flushBuffer);
}
} }
} }
} }

View File

@ -1,4 +1,5 @@
using Ryujinx.Cpu.Tracking; using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory.Tracking;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -13,8 +14,14 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Also tracks copy dependencies for the handle - references to other handles that must be kept /// Also tracks copy dependencies for the handle - references to other handles that must be kept
/// in sync with this one before use. /// in sync with this one before use.
/// </summary> /// </summary>
class TextureGroupHandle : IDisposable class TextureGroupHandle : ISyncActionHandler, IDisposable
{ {
private const int FlushBalanceIncrement = 6;
private const int FlushBalanceWriteCost = 1;
private const int FlushBalanceThreshold = 7;
private const int FlushBalanceMax = 60;
private const int FlushBalanceMin = -10;
private TextureGroup _group; private TextureGroup _group;
private int _bindCount; private int _bindCount;
private int _firstLevel; private int _firstLevel;
@ -26,6 +33,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// The sync number last registered. /// The sync number last registered.
/// </summary> /// </summary>
private ulong _registeredSync; private ulong _registeredSync;
private ulong _registeredBufferSync = ulong.MaxValue;
private ulong _registeredBufferGuestSync = ulong.MaxValue;
/// <summary> /// <summary>
/// The sync number when the texture was last modified by GPU. /// The sync number when the texture was last modified by GPU.
@ -42,6 +51,12 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
private bool _syncActionRegistered; private bool _syncActionRegistered;
/// <summary>
/// Determines the balance of synced writes to flushes.
/// Used to determine if the texture should always write data to a persistent buffer for flush.
/// </summary>
private int _flushBalance;
/// <summary> /// <summary>
/// The byte offset from the start of the storage of this handle. /// The byte offset from the start of the storage of this handle.
/// </summary> /// </summary>
@ -70,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// The CPU memory tracking handles that cover this handle. /// The CPU memory tracking handles that cover this handle.
/// </summary> /// </summary>
public CpuRegionHandle[] Handles { get; } public RegionHandle[] Handles { get; }
/// <summary> /// <summary>
/// True if a texture overlapping this handle has been modified. Is set false when the flush action is called. /// True if a texture overlapping this handle has been modified. Is set false when the flush action is called.
@ -112,7 +127,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int firstLevel, int firstLevel,
int baseSlice, int baseSlice,
int sliceCount, int sliceCount,
CpuRegionHandle[] handles) RegionHandle[] handles)
{ {
_group = group; _group = group;
_firstLayer = firstLayer; _firstLayer = firstLayer;
@ -132,6 +147,12 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
Handles = handles; Handles = handles;
if (group.Storage.Info.IsLinear)
{
// Linear textures are presumed to be used for readback initially.
_flushBalance = FlushBalanceThreshold + FlushBalanceIncrement;
}
} }
/// <summary> /// <summary>
@ -159,6 +180,35 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
/// <summary>
/// Determine if the next sync will copy into the flush buffer.
/// </summary>
/// <returns>True if it will copy, false otherwise</returns>
private bool NextSyncCopies()
{
return _flushBalance - FlushBalanceWriteCost > FlushBalanceThreshold;
}
/// <summary>
/// Alters the flush balance by the given value. Should increase significantly with each sync, decrease with each write.
/// A flush balance higher than the threshold will cause a texture to repeatedly copy to a flush buffer on each use.
/// </summary>
/// <param name="modifier">Value to add to the existing flush balance</param>
/// <returns>True if the new balance is over the threshold, false otherwise</returns>
private bool ModifyFlushBalance(int modifier)
{
int result;
int existingBalance;
do
{
existingBalance = _flushBalance;
result = Math.Max(FlushBalanceMin, Math.Min(FlushBalanceMax, existingBalance + modifier));
}
while (Interlocked.CompareExchange(ref _flushBalance, result, existingBalance) != existingBalance);
return result > FlushBalanceThreshold;
}
/// <summary> /// <summary>
/// Adds a single texture view as an overlap if its range overlaps. /// Adds a single texture view as an overlap if its range overlaps.
/// </summary> /// </summary>
@ -204,7 +254,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!_syncActionRegistered) if (!_syncActionRegistered)
{ {
_modifiedSync = context.SyncNumber; _modifiedSync = context.SyncNumber;
context.RegisterSyncAction(SyncAction, true); context.RegisterSyncAction(this, true);
_syncActionRegistered = true; _syncActionRegistered = true;
} }
@ -241,6 +291,13 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
SignalModified(context); SignalModified(context);
if (!bound && _syncActionRegistered && NextSyncCopies())
{
// On unbind, textures that flush often should immediately create sync so their result can be obtained as soon as possible.
context.CreateHostSyncIfNeeded(HostSyncFlags.Force);
}
// Note: Bind count currently resets to 0 on inherit for safety, as the handle <-> view relationship can change. // Note: Bind count currently resets to 0 on inherit for safety, as the handle <-> view relationship can change.
_bindCount = Math.Max(0, _bindCount + (bound ? 1 : -1)); _bindCount = Math.Max(0, _bindCount + (bound ? 1 : -1));
} }
@ -266,25 +323,35 @@ namespace Ryujinx.Graphics.Gpu.Image
/// removing the modified flag if it was reached, or leaving it set if it has not yet been created. /// removing the modified flag if it was reached, or leaving it set if it has not yet been created.
/// </summary> /// </summary>
/// <param name="context">The GPU context used to wait for sync</param> /// <param name="context">The GPU context used to wait for sync</param>
public void Sync(GpuContext context) /// <returns>True if the texture data can be read from the flush buffer</returns>
public bool Sync(GpuContext context)
{ {
ulong registeredSync = _registeredSync; // Currently assumes the calling thread is a guest thread.
long diff = (long)(context.SyncNumber - registeredSync);
bool inBuffer = _registeredBufferGuestSync != ulong.MaxValue;
ulong sync = inBuffer ? _registeredBufferGuestSync : _registeredSync;
long diff = (long)(context.SyncNumber - sync);
ModifyFlushBalance(FlushBalanceIncrement);
if (diff > 0) if (diff > 0)
{ {
context.Renderer.WaitSync(registeredSync); context.Renderer.WaitSync(sync);
if ((long)(_modifiedSync - registeredSync) > 0) if ((long)(_modifiedSync - sync) > 0)
{ {
// Flush the data in a previous state. Do not remove the modified flag - it will be removed to ignore following writes. // Flush the data in a previous state. Do not remove the modified flag - it will be removed to ignore following writes.
return; return inBuffer;
} }
Modified = false; Modified = false;
return inBuffer;
} }
// If the difference is <= 0, no data is not ready yet. Flush any data we can without waiting or removing modified flag. // If the difference is <= 0, no data is not ready yet. Flush any data we can without waiting or removing modified flag.
return false;
} }
/// <summary> /// <summary>
@ -296,15 +363,41 @@ namespace Ryujinx.Graphics.Gpu.Image
Interlocked.Exchange(ref _actionRegistered, 0); Interlocked.Exchange(ref _actionRegistered, 0);
} }
/// <summary>
/// Action to perform before a sync number is registered after modification.
/// This action will copy the texture data to the flush buffer if this texture
/// flushes often enough, which is determined by the flush balance.
/// </summary>
/// <inheritdoc/>
public void SyncPreAction(bool syncpoint)
{
if (syncpoint || NextSyncCopies())
{
if (ModifyFlushBalance(0) && _registeredBufferSync != _modifiedSync)
{
_group.FlushIntoBuffer(this);
_registeredBufferSync = _modifiedSync;
}
}
}
/// <summary> /// <summary>
/// Action to perform when a sync number is registered after modification. /// Action to perform when a sync number is registered after modification.
/// This action will register a read tracking action on the memory tracking handle so that a flush from CPU can happen. /// This action will register a read tracking action on the memory tracking handle so that a flush from CPU can happen.
/// </summary> /// </summary>
private void SyncAction() /// <inheritdoc/>
public bool SyncAction(bool syncpoint)
{ {
// The storage will need to signal modified again to update the sync number in future. // The storage will need to signal modified again to update the sync number in future.
_group.Storage.SignalModifiedDirty(); _group.Storage.SignalModifiedDirty();
bool lastInBuffer = _registeredBufferSync == _modifiedSync;
if (!lastInBuffer)
{
_registeredBufferSync = ulong.MaxValue;
}
lock (Overlaps) lock (Overlaps)
{ {
foreach (Texture texture in Overlaps) foreach (Texture texture in Overlaps)
@ -314,6 +407,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
// Register region tracking for CPU? (again) // Register region tracking for CPU? (again)
_registeredSync = _modifiedSync; _registeredSync = _modifiedSync;
_syncActionRegistered = false; _syncActionRegistered = false;
@ -321,6 +415,14 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
_group.RegisterAction(this); _group.RegisterAction(this);
} }
if (syncpoint)
{
_registeredBufferGuestSync = _registeredBufferSync;
}
// If the last modification is in the buffer, keep this sync action alive until it sees a syncpoint.
return syncpoint || !lastInBuffer;
} }
/// <summary> /// <summary>
@ -540,7 +642,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
foreach (CpuRegionHandle handle in Handles) foreach (RegionHandle handle in Handles)
{ {
handle.Dispose(); handle.Dispose();
} }

View File

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

View File

@ -272,7 +272,15 @@ namespace Ryujinx.Graphics.Gpu.Image
ulong address = descriptor.UnpackAddress(); ulong address = descriptor.UnpackAddress();
MultiRange range = _channel.MemoryManager.GetPhysicalRegions(address, texture.Size); if (!descriptor.Equals(ref DescriptorCache[request.ID]))
{
// If the pool entry has already been replaced, just remove the texture.
texture.DecrementReferenceCount();
continue;
}
MultiRange range = _channel.MemoryManager.Physical.TextureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
// If the texture is not mapped at all, delete its reference. // If the texture is not mapped at all, delete its reference.

View File

@ -11,7 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Image
None = 0, None = 0,
ForSampler = 1 << 1, ForSampler = 1 << 1,
ForCopy = 1 << 2, ForCopy = 1 << 2,
WithUpscale = 1 << 3, DepthAlias = 1 << 3,
NoCreate = 1 << 4 WithUpscale = 1 << 4,
NoCreate = 1 << 5
} }
} }

View File

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

View File

@ -1,5 +1,5 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
using System; using System;
@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others. /// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
/// </summary> /// </summary>
class Buffer : IRange, IDisposable class Buffer : IRange, ISyncActionHandler, IDisposable
{ {
private const ulong GranularBufferThreshold = 4096; private const ulong GranularBufferThreshold = 4096;
@ -53,8 +53,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </remarks> /// </remarks>
private BufferModifiedRangeList _modifiedRanges = null; private BufferModifiedRangeList _modifiedRanges = null;
private readonly CpuMultiRegionHandle _memoryTrackingGranular; private readonly MultiRegionHandle _memoryTrackingGranular;
private readonly CpuRegionHandle _memoryTracking; private readonly RegionHandle _memoryTracking;
private readonly RegionSignal _externalFlushDelegate; private readonly RegionSignal _externalFlushDelegate;
private readonly Action<ulong, ulong> _loadDelegate; private readonly Action<ulong, ulong> _loadDelegate;
@ -67,6 +67,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
private int _referenceCount = 1; private int _referenceCount = 1;
private ulong _dirtyStart = ulong.MaxValue;
private ulong _dirtyEnd = ulong.MaxValue;
/// <summary> /// <summary>
/// Creates a new instance of the buffer. /// Creates a new instance of the buffer.
/// </summary> /// </summary>
@ -98,7 +101,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
else else
{ {
return Enumerable.Repeat(buffer._memoryTracking.GetHandle(), 1); return Enumerable.Repeat(buffer._memoryTracking, 1);
} }
}); });
} }
@ -220,6 +223,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
_sequenceNumber = _context.SequenceNumber; _sequenceNumber = _context.SequenceNumber;
_dirtyStart = ulong.MaxValue;
}
}
if (_dirtyStart != ulong.MaxValue)
{
ulong end = address + size;
if (end > _dirtyStart && address < _dirtyEnd)
{
if (_modifiedRanges != null)
{
_modifiedRanges.ExcludeModifiedRegions(_dirtyStart, _dirtyEnd - _dirtyStart, _loadDelegate);
}
else
{
LoadRegion(_dirtyStart, _dirtyEnd - _dirtyStart);
}
_dirtyStart = ulong.MaxValue;
} }
} }
} }
@ -248,7 +271,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (!_syncActionRegistered) if (!_syncActionRegistered)
{ {
_context.RegisterSyncAction(SyncAction); _context.RegisterSyncAction(this);
_syncActionRegistered = true; _syncActionRegistered = true;
} }
} }
@ -267,7 +290,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Action to be performed when a syncpoint is reached after modification. /// Action to be performed when a syncpoint is reached after modification.
/// This will register read/write tracking to flush the buffer from GPU when its memory is used. /// This will register read/write tracking to flush the buffer from GPU when its memory is used.
/// </summary> /// </summary>
private void SyncAction() /// <inheritdoc/>
public bool SyncAction(bool syncpoint)
{ {
_syncActionRegistered = false; _syncActionRegistered = false;
@ -284,10 +308,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
_memoryTracking.RegisterAction(_externalFlushDelegate); _memoryTracking.RegisterAction(_externalFlushDelegate);
SynchronizeMemory(Address, Size); SynchronizeMemory(Address, Size);
} }
return true;
} }
/// <summary> /// <summary>
/// Inherit modified ranges from another buffer. /// Inherit modified and dirty ranges from another buffer.
/// </summary> /// </summary>
/// <param name="from">The buffer to inherit from</param> /// <param name="from">The buffer to inherit from</param>
public void InheritModifiedRanges(Buffer from) public void InheritModifiedRanges(Buffer from)
@ -296,7 +322,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (from._syncActionRegistered && !_syncActionRegistered) if (from._syncActionRegistered && !_syncActionRegistered)
{ {
_context.RegisterSyncAction(SyncAction); _context.RegisterSyncAction(this);
_syncActionRegistered = true; _syncActionRegistered = true;
} }
@ -316,6 +342,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
_modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction); _modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction);
} }
if (from._dirtyStart != ulong.MaxValue)
{
ForceDirty(from._dirtyStart, from._dirtyEnd - from._dirtyStart);
}
} }
/// <summary> /// <summary>
@ -334,6 +365,44 @@ namespace Ryujinx.Graphics.Gpu.Memory
return false; return false;
} }
/// <summary>
/// Clear the dirty range that overlaps with the given region.
/// </summary>
/// <param name="address">Start address of the modified region</param>
/// <param name="size">Size of the modified region</param>
private void ClearDirty(ulong address, ulong size)
{
if (_dirtyStart != ulong.MaxValue)
{
ulong end = address + size;
if (end > _dirtyStart && address < _dirtyEnd)
{
if (address <= _dirtyStart)
{
// Cut off the start.
if (end < _dirtyEnd)
{
_dirtyStart = end;
}
else
{
_dirtyStart = ulong.MaxValue;
}
}
else if (end >= _dirtyEnd)
{
// Cut off the end.
_dirtyEnd = address;
}
// If fully contained, do nothing.
}
}
}
/// <summary> /// <summary>
/// Indicate that a region of the buffer was modified, and must be loaded from memory. /// Indicate that a region of the buffer was modified, and must be loaded from memory.
/// </summary> /// </summary>
@ -353,6 +422,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
mSize = maxSize; mSize = maxSize;
} }
ClearDirty(mAddress, mSize);
if (_modifiedRanges != null) if (_modifiedRanges != null)
{ {
_modifiedRanges.ExcludeModifiedRegions(mAddress, mSize, _loadDelegate); _modifiedRanges.ExcludeModifiedRegions(mAddress, mSize, _loadDelegate);
@ -376,14 +447,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
/// <summary> /// <summary>
/// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check. /// Force a region of the buffer to be dirty within the memory tracking. Avoids reprotection and nullifies sequence number check.
/// </summary> /// </summary>
/// <param name="mAddress">Start address of the modified region</param> /// <param name="mAddress">Start address of the modified region</param>
/// <param name="mSize">Size of the region to force dirty</param> /// <param name="mSize">Size of the region to force dirty</param>
public void ForceDirty(ulong mAddress, ulong mSize) private void ForceTrackingDirty(ulong mAddress, ulong mSize)
{ {
_modifiedRanges?.Clear(mAddress, mSize);
if (_useGranular) if (_useGranular)
{ {
_memoryTrackingGranular.ForceDirty(mAddress, mSize); _memoryTrackingGranular.ForceDirty(mAddress, mSize);
@ -395,6 +464,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check.
/// </summary>
/// <param name="mAddress">Start address of the modified region</param>
/// <param name="mSize">Size of the region to force dirty</param>
public void ForceDirty(ulong mAddress, ulong mSize)
{
_modifiedRanges?.Clear(mAddress, mSize);
ulong end = mAddress + mSize;
if (_dirtyStart == ulong.MaxValue)
{
_dirtyStart = mAddress;
_dirtyEnd = end;
}
else
{
// Is the new range more than a page away from the existing one?
if ((long)(mAddress - _dirtyEnd) >= (long)MemoryManager.PageSize ||
(long)(_dirtyStart - end) >= (long)MemoryManager.PageSize)
{
ForceTrackingDirty(mAddress, mSize);
}
else
{
_dirtyStart = Math.Min(_dirtyStart, mAddress);
_dirtyEnd = Math.Max(_dirtyEnd, end);
}
}
}
/// <summary> /// <summary>
/// Performs copy of all the buffer data from one buffer to another. /// Performs copy of all the buffer data from one buffer to another.
/// </summary> /// </summary>

View File

@ -1,5 +1,4 @@
using Ryujinx.Cpu.Tracking; using Ryujinx.Memory.Tracking;
using Ryujinx.Memory.Tracking;
using System; using System;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
@ -9,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
class GpuRegionHandle : IRegionHandle class GpuRegionHandle : IRegionHandle
{ {
private readonly CpuRegionHandle[] _cpuRegionHandles; private readonly RegionHandle[] _cpuRegionHandles;
public bool Dirty public bool Dirty
{ {
@ -35,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Create a new GpuRegionHandle, made up of mulitple CpuRegionHandles. /// Create a new GpuRegionHandle, made up of mulitple CpuRegionHandles.
/// </summary> /// </summary>
/// <param name="cpuRegionHandles">The CpuRegionHandles that make up this handle</param> /// <param name="cpuRegionHandles">The CpuRegionHandles that make up this handle</param>
public GpuRegionHandle(CpuRegionHandle[] cpuRegionHandles) public GpuRegionHandle(RegionHandle[] cpuRegionHandles)
{ {
_cpuRegionHandles = cpuRegionHandles; _cpuRegionHandles = cpuRegionHandles;
} }

View File

@ -365,6 +365,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Runs remap actions that are added to an unmap event.
/// These must run after the mapping completes.
/// </summary>
/// <param name="e">Event with remap actions</param>
private void RunRemapActions(UnmapEventArgs e)
{
if (e.RemapActions != null)
{
foreach (Action action in e.RemapActions)
{
action();
}
}
}
/// <summary> /// <summary>
/// Maps a given range of pages to the specified CPU virtual address. /// Maps a given range of pages to the specified CPU virtual address.
/// </summary> /// </summary>
@ -379,12 +395,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
lock (_pageTable) lock (_pageTable)
{ {
MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size)); UnmapEventArgs e = new(va, size);
MemoryUnmapped?.Invoke(this, e);
for (ulong offset = 0; offset < size; offset += PageSize) for (ulong offset = 0; offset < size; offset += PageSize)
{ {
SetPte(va + offset, PackPte(pa + offset, kind)); SetPte(va + offset, PackPte(pa + offset, kind));
} }
RunRemapActions(e);
} }
} }
@ -398,12 +417,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
lock (_pageTable) lock (_pageTable)
{ {
// Event handlers are not expected to be thread safe. // Event handlers are not expected to be thread safe.
MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size)); UnmapEventArgs e = new(va, size);
MemoryUnmapped?.Invoke(this, e);
for (ulong offset = 0; offset < size; offset += PageSize) for (ulong offset = 0; offset < size; offset += PageSize)
{ {
SetPte(va + offset, PteUnmapped); SetPte(va + offset, PteUnmapped);
} }
RunRemapActions(e);
} }
} }
@ -615,6 +637,33 @@ namespace Ryujinx.Graphics.Gpu.Memory
return UnpackPaFromPte(pte) + (va & PageMask); return UnpackPaFromPte(pte) + (va & PageMask);
} }
/// <summary>
/// Translates a GPU virtual address and returns the number of bytes that are mapped after it.
/// </summary>
/// <param name="va">GPU virtual address to be translated</param>
/// <param name="maxSize">Maximum size in bytes to scan</param>
/// <returns>Number of bytes, 0 if unmapped</returns>
public ulong GetMappedSize(ulong va, ulong maxSize)
{
if (!ValidateAddress(va))
{
return 0;
}
ulong startVa = va;
ulong endVa = va + maxSize;
ulong pte = GetPte(va);
while (pte != PteUnmapped && va < endVa)
{
va += PageSize - (va & PageMask);
pte = GetPte(va);
}
return Math.Min(maxSize, va - startVa);
}
/// <summary> /// <summary>
/// Gets the kind of a given memory page. /// Gets the kind of a given memory page.
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling. /// This might indicate the type of resource that can be allocated on the page, and also texture tiling.

View File

@ -1,5 +1,4 @@
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Memory; using Ryujinx.Memory;
@ -8,6 +7,7 @@ using Ryujinx.Memory.Tracking;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Linq;
using System.Threading; using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
@ -82,6 +82,34 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Gets a host pointer for a given range of application memory.
/// If the memory region is not a single contiguous block, this method returns 0.
/// </summary>
/// <remarks>
/// Getting a host pointer is unsafe. It should be considered invalid immediately if the GPU memory is unmapped.
/// </remarks>
/// <param name="range">Ranges of physical memory where the target data is located</param>
/// <returns>Pointer to the range of memory</returns>
public nint GetHostPointer(MultiRange range)
{
if (range.Count == 1)
{
var singleRange = range.GetSubRange(0);
if (singleRange.Address != MemoryManager.PteUnmapped)
{
var regions = _cpuMemory.GetHostRegions(singleRange.Address, singleRange.Size);
if (regions != null && regions.Count() == 1)
{
return (nint)regions.First().Address;
}
}
}
return 0;
}
/// <summary> /// <summary>
/// Gets a span of data from the application process. /// Gets a span of data from the application process.
/// </summary> /// </summary>
@ -319,7 +347,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="kind">Kind of the resource being tracked</param> /// <param name="kind">Kind of the resource being tracked</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuRegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind) public RegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind)
{ {
return _cpuMemory.BeginTracking(address, size, (int)kind); return _cpuMemory.BeginTracking(address, size, (int)kind);
} }
@ -332,7 +360,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind) public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind)
{ {
var cpuRegionHandles = new CpuRegionHandle[range.Count]; var cpuRegionHandles = new RegionHandle[range.Count];
int count = 0; int count = 0;
for (int i = 0; i < range.Count; i++) for (int i = 0; i < range.Count; i++)
@ -361,7 +389,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="handles">Handles to inherit state from or reuse</param> /// <param name="handles">Handles to inherit state from or reuse</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096) public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096)
{ {
return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind); return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind);
} }
@ -374,7 +402,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="kind">Kind of the resource being tracked</param> /// <param name="kind">Kind of the resource being tracked</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096) public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096)
{ {
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind); return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind);
} }

View File

@ -1,14 +1,24 @@
namespace Ryujinx.Graphics.Gpu.Memory using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Memory
{ {
public class UnmapEventArgs public class UnmapEventArgs
{ {
public ulong Address { get; } public ulong Address { get; }
public ulong Size { get; } public ulong Size { get; }
public List<Action> RemapActions { get; private set; }
public UnmapEventArgs(ulong address, ulong size) public UnmapEventArgs(ulong address, ulong size)
{ {
Address = address; Address = address;
Size = size; Size = size;
} }
public void AddRemapAction(Action action)
{
RemapActions ??= new List<Action>();
RemapActions.Add(action);
}
} }
} }

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 = 4735; private const uint CodeGenVersion = 4646;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

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

View File

@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System;
namespace Ryujinx.Graphics.Gpu.Shader namespace Ryujinx.Graphics.Gpu.Shader
{ {
@ -16,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly ResourceCounts _resourceCounts; private readonly ResourceCounts _resourceCounts;
private readonly int _stageIndex; private readonly int _stageIndex;
private readonly int[] _constantBufferBindings;
/// <summary> /// <summary>
/// Creates a new GPU accessor. /// Creates a new GPU accessor.
/// </summary> /// </summary>
@ -25,6 +28,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
_context = context; _context = context;
_resourceCounts = resourceCounts; _resourceCounts = resourceCounts;
_stageIndex = stageIndex; _stageIndex = stageIndex;
if (context.Capabilities.Api != TargetApi.Vulkan)
{
_constantBufferBindings = new int[Constants.TotalGpUniformBuffers];
_constantBufferBindings.AsSpan().Fill(-1);
}
} }
public int QueryBindingConstantBuffer(int index) public int QueryBindingConstantBuffer(int index)
@ -36,7 +45,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
else else
{ {
return _resourceCounts.UniformBuffersCount++; int binding = _constantBufferBindings[index];
if (binding < 0)
{
binding = _resourceCounts.UniformBuffersCount++;
_constantBufferBindings[index] = binding;
}
return binding;
} }
} }

View File

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

View File

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

View File

@ -0,0 +1,30 @@
using System;
namespace Ryujinx.Graphics.Gpu.Synchronization
{
/// <summary>
/// Modifier flags for creating host sync.
/// </summary>
[Flags]
internal enum HostSyncFlags
{
None = 0,
/// <summary>
/// Present if host sync is being created by a syncpoint.
/// </summary>
Syncpoint = 1 << 0,
/// <summary>
/// Present if the sync should signal as soon as possible.
/// </summary>
Strict = 1 << 1,
/// <summary>
/// Present will force the sync to be created, even if no actions are eligible.
/// </summary>
Force = 1 << 2,
StrictSyncpoint = Strict | Syncpoint
}
}

View File

@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.Gpu.Synchronization
{
/// <summary>
/// This interface indicates that a class can be registered for a sync action.
/// </summary>
interface ISyncActionHandler
{
/// <summary>
/// Action to be performed when some synchronizing action is reached after modification.
/// Generally used to register read/write tracking to flush resources from GPU when their memory is used.
/// </summary>
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
/// <returns>True if the action is to be removed, false otherwise</returns>
bool SyncAction(bool syncpoint);
/// <summary>
/// Action to be performed immediately before sync is created.
/// </summary>
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
void SyncPreAction(bool syncpoint) { }
}
}

View File

@ -42,6 +42,20 @@ namespace Ryujinx.Graphics.OpenGL
return Handle.FromInt32<BufferHandle>(handle); return Handle.FromInt32<BufferHandle>(handle);
} }
public static BufferHandle CreatePersistent(int size)
{
int handle = GL.GenBuffer();
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle);
GL.BufferStorage(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero,
BufferStorageFlags.MapPersistentBit |
BufferStorageFlags.MapCoherentBit |
BufferStorageFlags.ClientStorageBit |
BufferStorageFlags.MapReadBit);
return Handle.FromInt32<BufferHandle>(handle);
}
public static void Copy(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) public static void Copy(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
{ {
GL.BindBuffer(BufferTarget.CopyReadBuffer, source.ToInt32()); GL.BindBuffer(BufferTarget.CopyReadBuffer, source.ToInt32());
@ -60,7 +74,11 @@ namespace Ryujinx.Graphics.OpenGL
// Data in the persistent buffer and host array is guaranteed to be available // Data in the persistent buffer and host array is guaranteed to be available
// until the next time the host thread requests data. // until the next time the host thread requests data.
if (HwCapabilities.UsePersistentBufferForFlush) if (renderer.PersistentBuffers.TryGet(buffer, out IntPtr ptr))
{
return new PinnedSpan<byte>(IntPtr.Add(ptr, offset).ToPointer(), size);
}
else if (HwCapabilities.UsePersistentBufferForFlush)
{ {
return PinnedSpan<byte>.UnsafeFromSpan(renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size)); return PinnedSpan<byte>.UnsafeFromSpan(renderer.PersistentBuffers.Default.GetBufferData(buffer, offset, size));
} }

View File

@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.OpenGL
private static GpuVendor GetGpuVendor() private static GpuVendor GetGpuVendor()
{ {
string vendor = GL.GetString(StringName.Vendor).ToLower(); string vendor = GL.GetString(StringName.Vendor).ToLowerInvariant();
if (vendor == "nvidia corporation") if (vendor == "nvidia corporation")
{ {
@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.OpenGL
} }
else if (vendor == "intel") else if (vendor == "intel")
{ {
string renderer = GL.GetString(StringName.Renderer).ToLower(); string renderer = GL.GetString(StringName.Renderer).ToLowerInvariant();
return renderer.Contains("mesa") ? GpuVendor.IntelUnix : GpuVendor.IntelWindows; return renderer.Contains("mesa") ? GpuVendor.IntelUnix : GpuVendor.IntelWindows;
} }

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