Compare commits

...

52 Commits

Author SHA1 Message Date
cd74ae1bbd Implement fast DMA texture to texture copy (#7299)
* Implement fast DMA texture to texture copy

* PR feedback
2024-09-15 18:12:05 -03:00
62216782ca Make GetFunctionPointerForDelegate as explicit as possible (#7279)
Co-authored-by: gdk <gab.dark.100@gmail.com>
2024-09-15 17:39:10 -03:00
2f36a6665c Implement Arm32 VSHLL and QADD16 instructions (#7301) 2024-09-12 18:22:30 -03:00
ca59c3f499 Vulkan: Feedback loop detection and barriers (#7226)
* Vulkan: Feedback loop improvements

This PR allows the Vulkan backend to detect attachment feedback loops. These are currently used in the following ways:

- Partial use of VK_EXT_attachment_feedback_loop_layout
  - All renderable textures have AttachmentFeedbackLoopBitExt
  - Compile pipelines with Color/DepthStencil feedback loop flags when present
- Support using FragmentBarrier for feedback loops (fixes regressions from https://github.com/Ryujinx/Ryujinx/pull/7012 )

TODO:
- AMD GPUs may need layout transitions for it to properly allow textures to be used in feedback loops.
- Use dynamic state for feedback loops. The background pipeline will always miss since feedback loop state isn't known on the GPU project.
- How is the barrier dependency flag used? (DXVK just ignores it, there's no vulkan validation...)
- Improve subpass dependencies to fix validation errors

* Mark field readonly

* Add feedback loop dynamic state

* fix: add MoltenVK resolver workaround

fix: add MoltenVK resolver workaround

* Formatting

* Fix more complaints

* RADV dcc workaround

* Use dynamic state properly, cleanup.

* Use aspects flags in more places
2024-09-01 21:28:16 -03:00
fdd7ee791c Fix incorrect depth texture 3D flag (#7262) 2024-09-01 17:58:01 -03:00
398fa1c238 Vulkan: Update Silk.NET to 2.21 (#7266)
* Update Silk.NET version

* fix: add MoltenVK resolver workaround

fix: add MoltenVK resolver workaround

* Cleanup

* Readonly ref warnings

* Remove driver id todo
2024-09-01 17:33:11 -03:00
2c5c0392f9 Make HLE project AOT friendly (#7085)
* add hle service generator

remove usage of reflection in device state

* remove rd.xml generation

* make applet manager reflection free

* fix typos

* fix encoding

* fix style report

* remove rogue generator reference

* remove double assignment
2024-08-31 11:39:26 -03:00
e0acde04bb Replace ImageSharp with SkiaSharp everywhere (#7030)
* replace ImageSharp with SkiaSharp for inline keyboard applet rendering

* fix avalonia inline keyboard input

* remove image sharp from gtk3 project

* add skiasharp linux assets

* fix whitespace

* fix format

* fix ico image offset when saving shortcut to windows
2024-08-31 11:32:53 -03:00
3c61d560c3 Fix deadlock in background translation thread shutdown (#7239)
TryDequeue checks for _disposed before taking the lock.  If another
thread calls Dispose before it takes the lock, it won't get woken up by
the PulseAll call, and will deadlock in Monitor.Wait.

Double-checking _disposed with the lock taken should avoid this.
2024-08-27 19:10:24 +02:00
b45a81458a nuget: bump DynamicData from 9.0.1 to 9.0.4 (#7220)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 9.0.1 to 9.0.4.
- [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/9.0.1...9.0.4)

---
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>
2024-08-21 12:30:43 +02:00
460f9faf4e Fix NRE when using buffer image array (#7159) 2024-08-21 00:49:17 +01:00
552c15739c nuget: bump ImageSharp from 2.1.8 to 2.1.9 (#7160)
While building I got some warnings, so I updated the dependency.

`warning NU1903: Package 'SixLabors.ImageSharp' 2.1.8 has a known high severity vulnerability, https://github.com/advisories/GHSA-63p8-c4ww-9cg7`
2024-08-20 22:26:32 +01:00
0137c9e635 nim:eca : Stub CreateServerInterface2 (#7128)
* Add files via upload

* Add files via upload

* Update src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2024-08-17 09:57:22 +01:00
23fa5f4c9c Fix arbitrary game ordering when sorting by Favorites (#7170)
* Fix arbitrary sorting by "Favorite" in the UI by making it the same as sorting alphabetically while giving favorites priority.

* Use a more engineered solution rather than string hacks.

* Address code style warnings. Add null checking. Make title name comparison case insensitive.

* one more style fix

---------

Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
2024-08-13 15:23:11 +02:00
4f75e26ec7 Clamp amount of mipmap levels to max allowed for all backends (#7197)
* Clamp amount of mipmap levels to max allowed for all backends

* XML docs

* Remove using
2024-08-12 17:45:25 -03:00
8d8983049e Implement UQADD16, UQADD8, UQSUB16, UQSUB8, VQRDMULH, VSLI and VSWP Arm32 instructions (#7174) 2024-08-08 17:07:24 -03:00
7969fb6bba Replace and remove obsolete ByteMemoryPool type (#7155)
* refactor: replace usage of ByteMemoryPool with MemoryOwner<byte>

* refactor: delete unused ByteMemoryPool and ByteMemoryPool.ByteMemoryPoolBuffer types

* refactor: change IMemoryOwner<byte> return types to MemoryOwner<byte>

* fix(perf): get span via `MemoryOwner<T>.Span` directly instead of `MemoryOwner<T>.Memory.Span`

* fix(perf): get span via MemoryOwner<T>.Span directly instead of `MemoryOwner<T>.Memory.Span`

* fix(perf): get span via MemoryOwner<T>.Span directly instead of `MemoryOwner<T>.Memory.Span`
2024-08-05 21:09:08 -03:00
4a4b11871e Fix same textures with unmapped start being considered different (#7141)
* Fix same textures with unmapped start being considered different

* Consolidate IsInvalid check

* InvalidAddress const

* Fix typo

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

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2024-08-05 11:00:41 -03:00
e85ee673b1 Fix LocaleExtension SetRawSource usages + language perf improvement (#7121)
* Avoid Avalonia CompiledBindingPathBuilder.SetRawSource

* Improve UI language change performance
2024-08-04 19:04:12 +01:00
42f22fe5d7 Infra: Update Microsoft.IdentityModel.JsonWebTokens (#7070)
* Update Microsoft.IdentityModel.JsonWebTokens

* Update
2024-08-04 18:56:27 +01:00
263eb97f79 Avoid race conditions while launching games directly from the command line (#7116)
* optimization: Load application metadata only for applications with IDs

* Load applications when necessary

This prevents loading applications when launching an application
directly from the command line (or a shortcut).
Instead, applications will be loaded after the emulation was stopped by the user.

* Show the title in the configured language when launching an application

* Rename DesiredTitleLanguage to DesiredLanguage
2024-08-03 22:31:34 +01:00
3004902257 nuget: bump DynamicData from 8.4.1 to 9.0.1 (#7040)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 8.4.1 to 9.0.1.
- [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/8.4.1...9.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-03 22:34:41 +02:00
59ddb26628 replace ByteMemoryPool usage in Ryujinx.Graphics (#7129)
* chore: replace `ByteMemoryPool` usage with `MemoryOwner<byte>`

* refactor: `PixelConverter.ConvertR4G4ToR4G4B4A4()` - rename old `outputSpan` to `outputSpanUInt16`, reuse same output `Span<byte>` as newly-freed name `outputSpan`

* eliminate temporary buffer allocations

* chore, perf: use MemoryOwner<byte> instead of IMemoryOwner<byte>
2024-08-03 19:50:53 +01:00
83fda10f6e Fix FileNotFoundException in TryGetApplicationsFromFile() and improve loading applications (#7145)
* Don't load files from hidden subdirectories

* Catch FileNotFoundException in TryGetApplicationsFromFile()

* Skip non-existent files and bad symlinks when loading applications
2024-08-03 19:46:59 +02:00
d97e995e59 Fix off-by-one on audio renderer PerformanceManager.GetNextEntry (#7139) 2024-07-31 22:22:11 -03:00
56b2f84702 Fix shader RegisterUsage pass only taking first operation dest into account (#7131)
* Fix shader RegisterUsage pass only taking first operation dest into account

* Shader cache version bump
2024-07-30 21:57:55 -03:00
698e36bbd2 Vulkan: Force topology to PatchList for Tessellation (#7102)
Vulkan spec states that input topology should always be PatchList when a tessellation pipeline is present. The AMD GPU on windows crashes so hard it BSODs the machine if this isn't the case, so it's forced here just in case.

I'm not sure what providing a different topology here would even do, as you'd think it would always be a patch list input.
2024-07-30 21:48:30 -03:00
6ce49a2dc7 Ava UI: Handle updates containing non numeric characters (#7043)
* Handle updates containing non numeric characters

Smh

Dont be stupid

* Use Berry’s method

* Thanks gdk

* Remove using
2024-07-25 16:44:33 -03:00
ccd330ba0f Vulkan: Add missing barriers for texture to buffer copy (#7092)
This barrier has always been missing, but it only became apparent when #7012 merged.

I also added some barriers in case the target buffer used here is used by other commands, though right now it isn't.

Fixes a regression where water would turn white on AMD GPUs with the proprietary driver. May fix other issues on this driver.
2024-07-25 16:34:30 -03:00
95d252b7b8 Update kernel GetInfo SVC for firmware 18.0.0 (#7075)
* Implement kernel GetInfo AliasRegionExtraSize

* Implement IsSvcPermitted

* Remove warning supressions that are no longer needed

* Remove useless cast
2024-07-22 12:46:04 -03:00
add681144b Fix checking for the wrong metadata files for applications launched with a different program index (#7055)
* Fix checking for the wrong update metadata file

* Apply the same fix for dlc.json

* Use the base application ids for updates and DLCs in the GUI too

This shouldn't actually change anything, since the program index part of the application id
should always be 0 for all applications currently seen by the GUI.

This was just done for completeness.
2024-07-21 14:42:23 -03:00
c6dc00815a Make sure TryGetApplicationsFromFile() doesn't throw exceptions anymore (#7046)
* Add docstrings for exceptions to methods near TryGetApplicationsFromFile()

* Make sure TryGetApplicationsFromFile() doesn't throw exceptions anymore

* Add missing filePath to ApplicationData when loading applications from ExeFS

* Fix typo

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

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2024-07-20 16:35:43 -03:00
99f04ac1a6 Fix Skia saving screenshot with transparent background and incorrect origin (#7073)
* Fix Skia saving screenshot with transparent background and incorrect origin

* Remove code that is no longer necessary
2024-07-20 16:27:40 -03:00
ce09450743 Unlink server sessions from multi-wait when service stops processing requests (#7072) 2024-07-20 16:17:40 -03:00
2cb80f37d4 Ava UI: Auto select newly added updates & DLC (#7026)
* Fix DLC not being selected

* FIx conflicts

* Apply suggestions from code review

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

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2024-07-19 19:00:15 +02:00
827069e784 Add missing Buffer attribute on NGC Check method (#7051) 2024-07-18 15:11:00 -03:00
1a919e99b2 Vulkan: Defer guest barriers, and improve image barrier timings (#7012)
* More guarantees for buffer correct placement, defer guest requested buffers

* Split RP on indirect barrier rn

* Better handling for feedback loops.

* Qualcomm barriers suck too

* Fix condition

* Remove unused field

* Allow render pass barriers on turnip for now
2024-07-17 20:21:32 -03:00
f77bebac80 Include content data foreach-loop in try-catch (#7036) 2024-07-17 19:02:20 -03:00
6fbf279fac Add support for multi game XCIs (second try) (#6515)
* Add default values to ApplicationData directly

* Refactor application loading

It should now be possible to load multi game XCIs.
Included updates won't be detected for now.
Opening a game from the command line currently only opens the first one.

* Only include program NCAs where at least one tuple item is not null

* Get application data by title id and add programIndex check back

* Refactor application loading again and remove duplicate code

* Actually use patch ncas for updates

* Fix number of applications found with multi game xcis

* Don't load bundled updates from multi game xcis

* Change ApplicationData.TitleId type to ulong & Add TitleIdString property

* Use cnmt files and ContentCollection to load programs

* Ava: Add updates and DLCs from gamecarts

* Get the cnmt file from its NCA

* Ava: Identify bundled updates in updater window

* Fix the (hopefully) last few bugs

* Add idOffset parameter to GetNcaByType

* Handle missing file for dlc.json

* Ava: Shorten error message for invalid files

* Gtk: Add additional string for bundled updates in TitleUpdateWindow

* Hopefully fix DLC issues

* Apply formatting

* Finally fix DLC issues

* Adjust property names and fileSize field

* Read the correct update file

* Fix wrong casing for application id strings

* Rename TitleId to ApplicationId

* Address review comments

* Apply suggestions from code review

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

* Gracefully fail when loading pfs for update and dlc window

* Fix applications with multiple programs

* Fix DLCWindow crash on GTK

* Fix some GUI issues

* Remove IsXci again

* Don't add duplicates to update/dlc windows

* Avoid double lookup

* Preserve DLC enabled state for bundled DLCs

* Fix DLCWindow not opening using GTK

* Fix missing information when loading applications from file

* Address review feedback

Rename ContentCollection to ContentMetaData
Fix casing issues in log messages
Use null as the default value for updatePath

* Fix re-adding bundled DLCs every time

* Fix bundled DLCs disappearing

* Abstract common code to open application pfs

* Remove unused imports

* Fix file exists check when loading DLCs

* Load bundled DLCs only using dlc.json

* Load AoC items correctly

* Add all DLCs from a PFS

* Add argument to launch a specific application id

* Use application-id argument for shortcuts if necessary

* Return the application id from the control NCA if possible

* GetApplicationInformation: Don't overwrite application ids

Move SaveDataOwnerId check to the top, since it seems to be more reliable.

* Get application ids from CNMT again

This commit reverts some parts of 61615b8f0d6f90ae86778958ddc38eaf6dc280ab.
Since the issue wasn't actually related to the application id in CMNTs, we can remove the wrong assumptions.

* Revert erroneous axaml change from adca8900

* Rename title to application

* Wrap nsp/pfs0 case with curly braces

* Check if _applicationData.ControlHolder.ByteSpan is zeros only once

* Catch exceptions while loading applications from nsps

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2024-07-16 18:17:32 -03:00
344f4f52c1 Remove CommandBufferScoped Dependencies (#6958) 2024-07-16 17:01:06 -03:00
eb212aa91b misc: Re-order and manually update DriverID to name. (#7027)
* Re-order and update DriverID -> Name.

* Fix whitespace
2024-07-15 19:27:59 -03:00
a6dbb2ad2b replace ByteMemoryPool usage in Ryujinx.HLE (#6953) 2024-07-15 19:21:53 -03:00
595e514f18 Use SkiaSharp for Avalonia in place of ImageSharp (#6269)
* Rebased

Transformation all at once

Use SkiaSharp instead of ImageSharp

* Apply suggestions from code review

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

* Change back unintentionally changed comment

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: Emmanuel Hansen <emmausssss@gmail.com>
2024-07-14 08:16:14 +00:00
07435ad844 Use draw clear on Adreno, instead of vkCmdClearAttachments (#7013)
* Use draw clear on Adreno, instead of vkCmdClearAttachments

* Fix GTX TITAN detection
2024-07-10 17:52:45 -03:00
1668ba913f Force dynamic state update after rasterizer discard disable (#7007) 2024-07-09 23:31:01 -03:00
a830eb666b Disallow concurrent fence waits on Adreno (#7001)
* Disallow concurrent fence waits on Adreno

* Ensure locks are released if exceptions are thrown
2024-07-07 19:33:28 -03:00
cfc75d7e78 Disable descriptor set template updates for buffer textures on Adreno (#7002)
* Do not use template updates for buffer textures and buffer images

* No need to do it for images

* Simply buffer texture existence check

* Pipeline is now unused on DescriptorSetUpdater
2024-07-07 19:19:55 -03:00
c525d7d9a9 Force Vulkan swapchain re-creation when window size changes (#7003) 2024-07-07 19:02:11 -03:00
1a0a351a15 Resolve some Vulkan validation errors (#6915)
* Fix some validation errors

* Whitespace correction

* Resolve some runtime validation errors.

* Whitespace

* Properly fix usage realted validation error by setting Extended Usage image creation flag.

* Only if supported

* Remove checking extension for features that are core functionality of Vulkan 1.2
2024-06-26 09:21:44 -03:00
bd3335c143 Make sure the string is long enough before performing basic trim (#6982) 2024-06-26 11:27:23 +02:00
a94445b23e nuget: bump Microsoft.IdentityModel.JsonWebTokens from 7.6.0 to 7.6.2 (#6965)
Bumps [Microsoft.IdentityModel.JsonWebTokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 7.6.0 to 7.6.2.
- [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/7.6.2/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/7.6.0...7.6.2)

---
updated-dependencies:
- dependency-name: Microsoft.IdentityModel.JsonWebTokens
  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>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2024-06-26 10:45:51 +02:00
0c3421973c SetProcessMemoryPermission address and size are always 64-bit (#6977) 2024-06-25 09:40:53 +02:00
191 changed files with 4884 additions and 2554 deletions

View File

@ -13,14 +13,14 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="2.2.0" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="8.4.1" />
<PackageVersion Include="DynamicData" Version="9.0.4" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="LibHac" Version="0.19.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
@ -39,11 +39,11 @@
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.8" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
<PackageVersion Include="System.Management" Version="8.0.0" />

View File

@ -87,6 +87,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -249,6 +251,10 @@ Global
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -746,6 +746,7 @@ namespace ARMeilleure.Decoders
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
SetA32("<<<<01100010xxxxxxxx11110001xxxx", InstName.Qadd16, InstEmit32.Qadd16, OpCode32AluReg.Create);
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
@ -822,6 +823,10 @@ namespace ARMeilleure.Decoders
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
SetA32("<<<<01100110xxxxxxxx11110001xxxx", InstName.Uqadd16, InstEmit32.Uqadd16, OpCode32AluReg.Create);
SetA32("<<<<01100110xxxxxxxx11111001xxxx", InstName.Uqadd8, InstEmit32.Uqadd8, OpCode32AluReg.Create);
SetA32("<<<<01100110xxxxxxxx11110111xxxx", InstName.Uqsub16, InstEmit32.Uqsub16, OpCode32AluReg.Create);
SetA32("<<<<01100110xxxxxxxx11111111xxxx", InstName.Uqsub8, InstEmit32.Uqsub8, OpCode32AluReg.Create);
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
@ -1007,6 +1012,8 @@ namespace ARMeilleure.Decoders
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
SetAsimd("111100110x01xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("111100110x10xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
@ -1028,8 +1035,10 @@ namespace ARMeilleure.Decoders
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
SetAsimd("111100111x11<<10xxxx001100x0xxxx", InstName.Vshll, InstEmit32.Vshll2, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); // A2 encoding.
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
@ -1054,6 +1063,7 @@ namespace ARMeilleure.Decoders
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x<<xxxxxxx00010x0x0xxxx", InstName.Vsubl, InstEmit32.Vsubl_I, OpCode32SimdRegLong.Create, OpCode32SimdRegLong.CreateT32);
SetAsimd("1111001x1x<<xxxxxxx00011x0x0xxxx", InstName.Vsubw, InstEmit32.Vsubw_I, OpCode32SimdRegWide.Create, OpCode32SimdRegWide.CreateT32);
SetAsimd("111100111x110010xxxx00000xx0xxxx", InstName.Vswp, InstEmit32.Vswp, OpCode32Simd.Create, OpCode32Simd.CreateT32);
SetAsimd("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, OpCode32SimdTbl.Create, OpCode32SimdTbl.CreateT32);
SetAsimd("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, OpCode32SimdCmpZ.Create, OpCode32SimdCmpZ.CreateT32);
SetAsimd("111100100x<<xxxxxxxx1000xxx1xxxx", InstName.Vtst, InstEmit32.Vtst, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);

View File

@ -2,6 +2,8 @@ using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using static ARMeilleure.Instructions.InstEmitAluHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
@ -290,6 +292,16 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res);
}
public static void Qadd16(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitSigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateRange(context, d, context.Add(n, m), 16, unsigned: false, setQ: false);
}));
}
public static void Rbit(ArmEmitterContext context)
{
Operand m = GetAluM(context);
@ -558,6 +570,46 @@ namespace ARMeilleure.Instructions
EmitHsub8(context, unsigned: true);
}
public static void Uqadd16(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateUqadd(context, d, context.Add(n, m), 16);
}));
}
public static void Uqadd8(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateUqadd(context, d, context.Add(n, m), 8);
}));
}
public static void Uqsub16(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateUqsub(context, d, context.Subtract(n, m), 16);
}));
}
public static void Uqsub8(ArmEmitterContext context)
{
OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
{
EmitSaturateUqsub(context, d, context.Subtract(n, m), 8);
}));
}
public static void Usat(ArmEmitterContext context)
{
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
@ -934,6 +986,251 @@ namespace ARMeilleure.Instructions
}
}
private static void EmitSaturateRange(ArmEmitterContext context, Operand result, Operand value, uint saturateTo, bool unsigned, bool setQ = true)
{
Debug.Assert(saturateTo <= 32);
Debug.Assert(!unsigned || saturateTo < 32);
if (!unsigned && saturateTo == 32)
{
// No saturation possible for this case.
context.Copy(result, value);
return;
}
else if (saturateTo == 0)
{
// Result is always zero if we saturate 0 bits.
context.Copy(result, Const(0));
return;
}
Operand satValue;
if (unsigned)
{
// Negative values always saturate (to zero).
// So we must always ignore the sign bit when masking, so that the truncated value will differ from the original one.
satValue = context.BitwiseAnd(value, Const((int)(uint.MaxValue >> (32 - (int)saturateTo))));
}
else
{
satValue = context.ShiftLeft(value, Const(32 - (int)saturateTo));
satValue = context.ShiftRightSI(satValue, Const(32 - (int)saturateTo));
}
// If the result is 0, the values are equal and we don't need saturation.
Operand lblNoSat = Label();
context.BranchIfFalse(lblNoSat, context.Subtract(value, satValue));
// Saturate and set Q flag.
if (unsigned)
{
if (saturateTo == 31)
{
// Only saturation case possible when going from 32 bits signed to 32 or 31 bits unsigned
// is when the signed input is negative, as all positive values are representable on a 31 bits range.
satValue = Const(0);
}
else
{
satValue = context.ShiftRightSI(value, Const(31));
satValue = context.BitwiseNot(satValue);
satValue = context.ShiftRightUI(satValue, Const(32 - (int)saturateTo));
}
}
else
{
if (saturateTo == 1)
{
satValue = context.ShiftRightSI(value, Const(31));
}
else
{
satValue = Const(uint.MaxValue >> (33 - (int)saturateTo));
satValue = context.BitwiseExclusiveOr(satValue, context.ShiftRightSI(value, Const(31)));
}
}
if (setQ)
{
SetFlag(context, PState.QFlag, Const(1));
}
context.Copy(result, satValue);
Operand lblExit = Label();
context.Branch(lblExit);
context.MarkLabel(lblNoSat);
context.Copy(result, value);
context.MarkLabel(lblExit);
}
private static void EmitSaturateUqadd(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
{
Debug.Assert(saturateTo <= 32);
if (saturateTo == 32)
{
// No saturation possible for this case.
context.Copy(result, value);
return;
}
else if (saturateTo == 0)
{
// Result is always zero if we saturate 0 bits.
context.Copy(result, Const(0));
return;
}
// If the result is 0, the values are equal and we don't need saturation.
Operand lblNoSat = Label();
context.BranchIfFalse(lblNoSat, context.ShiftRightUI(value, Const((int)saturateTo)));
// Saturate.
context.Copy(result, Const(uint.MaxValue >> (32 - (int)saturateTo)));
Operand lblExit = Label();
context.Branch(lblExit);
context.MarkLabel(lblNoSat);
context.Copy(result, value);
context.MarkLabel(lblExit);
}
private static void EmitSaturateUqsub(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
{
Debug.Assert(saturateTo <= 32);
if (saturateTo == 32)
{
// No saturation possible for this case.
context.Copy(result, value);
return;
}
else if (saturateTo == 0)
{
// Result is always zero if we saturate 0 bits.
context.Copy(result, Const(0));
return;
}
// If the result is 0, the values are equal and we don't need saturation.
Operand lblNoSat = Label();
context.BranchIf(lblNoSat, value, Const(0), Comparison.GreaterOrEqual);
// Saturate.
// Assumes that the value can only underflow, since this is only used for unsigned subtraction.
context.Copy(result, Const(0));
Operand lblExit = Label();
context.Branch(lblExit);
context.MarkLabel(lblNoSat);
context.Copy(result, value);
context.MarkLabel(lblExit);
}
private static Operand EmitSigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
{
Operand tempD = context.AllocateLocal(OperandType.I32);
Operand tempN = context.SignExtend16(OperandType.I32, rn);
Operand tempM = context.SignExtend16(OperandType.I32, rm);
elementAction(tempD, tempN, tempM);
Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
tempN = context.ShiftRightSI(rn, Const(16));
tempM = context.ShiftRightSI(rm, Const(16));
elementAction(tempD, tempN, tempM);
return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
}
private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
{
Operand tempD = context.AllocateLocal(OperandType.I32);
Operand tempN = context.ZeroExtend16(OperandType.I32, rn);
Operand tempM = context.ZeroExtend16(OperandType.I32, rm);
elementAction(tempD, tempN, tempM);
Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
tempN = context.ShiftRightUI(rn, Const(16));
tempM = context.ShiftRightUI(rm, Const(16));
elementAction(tempD, tempN, tempM);
return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
}
private static Operand EmitSigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
{
return Emit8BitPair(context, rn, rm, elementAction, unsigned: false);
}
private static Operand EmitUnsigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction)
{
return Emit8BitPair(context, rn, rm, elementAction, unsigned: true);
}
private static Operand Emit8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action<Operand, Operand, Operand> elementAction, bool unsigned)
{
Operand tempD = context.AllocateLocal(OperandType.I32);
Operand result = default;
for (int b = 0; b < 4; b++)
{
Operand nByte = b != 0 ? context.ShiftRightUI(rn, Const(b * 8)) : rn;
Operand mByte = b != 0 ? context.ShiftRightUI(rm, Const(b * 8)) : rm;
if (unsigned)
{
nByte = context.ZeroExtend8(OperandType.I32, nByte);
mByte = context.ZeroExtend8(OperandType.I32, mByte);
}
else
{
nByte = context.SignExtend8(OperandType.I32, nByte);
mByte = context.SignExtend8(OperandType.I32, mByte);
}
elementAction(tempD, nByte, mByte);
if (b == 0)
{
result = context.ZeroExtend8(OperandType.I32, tempD);
}
else if (b < 3)
{
result = context.BitwiseOr(result, context.ShiftLeft(context.ZeroExtend8(OperandType.I32, tempD), Const(b * 8)));
}
else
{
result = context.BitwiseOr(result, context.ShiftLeft(tempD, Const(24)));
}
}
return result;
}
private static void EmitAluStore(ArmEmitterContext context, Operand value)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;

View File

@ -1246,6 +1246,33 @@ namespace ARMeilleure.Instructions
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
}
public static void Vqrdmulh(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
int eSize = 8 << op.Size;
EmitVectorBinaryOpI32(context, (op1, op2) =>
{
if (op.Size == 2)
{
op1 = context.SignExtend32(OperandType.I64, op1);
op2 = context.SignExtend32(OperandType.I64, op2);
}
Operand res = context.Multiply(op1, op2);
res = context.Add(res, Const(res.Type, 1L << (eSize - 2)));
res = context.ShiftRightSI(res, Const(eSize - 1));
res = EmitSatQ(context, res, eSize, signedSrc: true, signedDst: true);
if (op.Size == 2)
{
res = context.ConvertI64ToI32(res);
}
return res;
}, signed: true);
}
public static void Vqsub(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;

View File

@ -191,6 +191,26 @@ namespace ARMeilleure.Instructions
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vswp(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.Q)
{
Operand temp = context.Copy(GetVecA32(op.Qd));
context.Copy(GetVecA32(op.Qd), GetVecA32(op.Qm));
context.Copy(GetVecA32(op.Qm), temp);
}
else
{
Operand temp = ExtractScalar(context, OperandType.I64, op.Vd);
InsertScalar(context, op.Vd, ExtractScalar(context, OperandType.I64, op.Vm));
InsertScalar(context, op.Vm, temp);
}
}
public static void Vtbl(ArmEmitterContext context)
{
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;

View File

@ -106,6 +106,38 @@ namespace ARMeilleure.Instructions
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vshll2(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
Operand res = context.VectorZero();
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
if (op.Size == 2)
{
if (op.U)
{
me = context.ZeroExtend32(OperandType.I64, me);
}
else
{
me = context.SignExtend32(OperandType.I64, me);
}
}
me = context.ShiftLeft(me, Const(8 << op.Size));
res = EmitVectorInsert(context, res, me, index, op.Size + 1);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vshr(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
@ -130,6 +162,36 @@ namespace ARMeilleure.Instructions
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
}
public static void Vsli_I(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
int shift = op.Shift;
int eSize = 8 << op.Size;
ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0UL;
Operand res = GetVec(op.Qd);
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand me = EmitVectorExtractZx(context, op.Qm, op.Im + index, op.Size);
Operand neShifted = context.ShiftLeft(me, Const(shift));
Operand de = EmitVectorExtractZx(context, op.Qd, op.Id + index, op.Size);
Operand deMasked = context.BitwiseAnd(de, Const(mask));
Operand e = context.BitwiseOr(neShifted, deMasked);
res = EmitVectorInsert(context, res, e, op.Id + index, op.Size);
}
context.Copy(GetVec(op.Qd), res);
}
public static void Vsra(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;

View File

@ -527,6 +527,7 @@ namespace ARMeilleure.Instructions
Pld,
Pop,
Push,
Qadd16,
Rev,
Revsh,
Rsb,
@ -571,6 +572,10 @@ namespace ARMeilleure.Instructions
Umaal,
Umlal,
Umull,
Uqadd16,
Uqadd8,
Uqsub16,
Uqsub8,
Usat,
Usat16,
Usub8,
@ -645,6 +650,7 @@ namespace ARMeilleure.Instructions
Vqdmulh,
Vqmovn,
Vqmovun,
Vqrdmulh,
Vqrshrn,
Vqrshrun,
Vqshrn,
@ -666,6 +672,7 @@ namespace ARMeilleure.Instructions
Vshll,
Vshr,
Vshrn,
Vsli,
Vst1,
Vst2,
Vst3,
@ -682,6 +689,7 @@ namespace ARMeilleure.Instructions
Vsub,
Vsubl,
Vsubw,
Vswp,
Vtbl,
Vtrn,
Vtst,

View File

@ -1,5 +1,4 @@
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
@ -11,11 +10,10 @@ namespace ARMeilleure.Translation
public IntPtr FuncPtr { get; }
public DelegateInfo(Delegate dlg)
public DelegateInfo(Delegate dlg, IntPtr funcPtr)
{
_dlg = dlg;
FuncPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(dlg);
FuncPtr = funcPtr;
}
}
}

View File

@ -3,6 +3,7 @@ using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
@ -64,11 +65,11 @@ namespace ARMeilleure.Translation
return index;
}
private static void SetDelegateInfo(Delegate dlg)
private static void SetDelegateInfo(Delegate dlg, IntPtr funcPtr)
{
string key = GetKey(dlg.Method);
_delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
_delegates.Add(key, new DelegateInfo(dlg, funcPtr)); // ArgumentException (key).
}
private static string GetKey(MethodInfo info)
@ -82,179 +83,353 @@ namespace ARMeilleure.Translation
{
_delegates = new SortedList<string, DelegateInfo>();
SetDelegateInfo(new MathAbs(Math.Abs));
SetDelegateInfo(new MathCeiling(Math.Ceiling));
SetDelegateInfo(new MathFloor(Math.Floor));
SetDelegateInfo(new MathRound(Math.Round));
SetDelegateInfo(new MathTruncate(Math.Truncate));
var dlgMathAbs = new MathAbs(Math.Abs);
var dlgMathCeiling = new MathCeiling(Math.Ceiling);
var dlgMathFloor = new MathFloor(Math.Floor);
var dlgMathRound = new MathRound(Math.Round);
var dlgMathTruncate = new MathTruncate(Math.Truncate);
SetDelegateInfo(new MathFAbs(MathF.Abs));
SetDelegateInfo(new MathFCeiling(MathF.Ceiling));
SetDelegateInfo(new MathFFloor(MathF.Floor));
SetDelegateInfo(new MathFRound(MathF.Round));
SetDelegateInfo(new MathFTruncate(MathF.Truncate));
var dlgMathFAbs = new MathFAbs(MathF.Abs);
var dlgMathFCeiling = new MathFCeiling(MathF.Ceiling);
var dlgMathFFloor = new MathFFloor(MathF.Floor);
var dlgMathFRound = new MathFRound(MathF.Round);
var dlgMathFTruncate = new MathFTruncate(MathF.Truncate);
SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break));
SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization));
SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit));
SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0));
SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0));
SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0));
SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0));
SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0));
SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress));
SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine));
SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte));
SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16));
SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32));
SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64));
SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128));
SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking));
SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall));
SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess));
SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined));
SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte));
SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16));
SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32));
SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64));
SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128));
var dlgNativeInterfaceBreak = new NativeInterfaceBreak(NativeInterface.Break);
var dlgNativeInterfaceCheckSynchronization = new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization);
var dlgNativeInterfaceEnqueueForRejit = new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit);
var dlgNativeInterfaceGetCntfrqEl0 = new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0);
var dlgNativeInterfaceGetCntpctEl0 = new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0);
var dlgNativeInterfaceGetCntvctEl0 = new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0);
var dlgNativeInterfaceGetCtrEl0 = new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0);
var dlgNativeInterfaceGetDczidEl0 = new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0);
var dlgNativeInterfaceGetFunctionAddress = new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress);
var dlgNativeInterfaceInvalidateCacheLine = new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine);
var dlgNativeInterfaceReadByte = new NativeInterfaceReadByte(NativeInterface.ReadByte);
var dlgNativeInterfaceReadUInt16 = new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16);
var dlgNativeInterfaceReadUInt32 = new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32);
var dlgNativeInterfaceReadUInt64 = new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64);
var dlgNativeInterfaceReadVector128 = new NativeInterfaceReadVector128(NativeInterface.ReadVector128);
var dlgNativeInterfaceSignalMemoryTracking = new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking);
var dlgNativeInterfaceSupervisorCall = new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall);
var dlgNativeInterfaceThrowInvalidMemoryAccess = new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess);
var dlgNativeInterfaceUndefined = new NativeInterfaceUndefined(NativeInterface.Undefined);
var dlgNativeInterfaceWriteByte = new NativeInterfaceWriteByte(NativeInterface.WriteByte);
var dlgNativeInterfaceWriteUInt16 = new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16);
var dlgNativeInterfaceWriteUInt32 = new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32);
var dlgNativeInterfaceWriteUInt64 = new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64);
var dlgNativeInterfaceWriteVector128 = new NativeInterfaceWriteVector128(NativeInterface.WriteVector128);
SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns));
SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros));
SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b));
SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb));
SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch));
SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw));
SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx));
SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h));
SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w));
SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x));
SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt));
SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt));
SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate));
SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose));
SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower));
SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority));
SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity));
SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper));
SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns));
SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns));
SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128));
SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32));
SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64));
SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32));
SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64));
SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32));
SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64));
SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32));
SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64));
SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1));
SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2));
SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1));
SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2));
SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64));
SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1));
SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2));
SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3));
SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4));
SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1));
SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2));
SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3));
SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4));
SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64));
var dlgSoftFallbackCountLeadingSigns = new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns);
var dlgSoftFallbackCountLeadingZeros = new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros);
var dlgSoftFallbackCrc32b = new SoftFallbackCrc32b(SoftFallback.Crc32b);
var dlgSoftFallbackCrc32cb = new SoftFallbackCrc32cb(SoftFallback.Crc32cb);
var dlgSoftFallbackCrc32ch = new SoftFallbackCrc32ch(SoftFallback.Crc32ch);
var dlgSoftFallbackCrc32cw = new SoftFallbackCrc32cw(SoftFallback.Crc32cw);
var dlgSoftFallbackCrc32cx = new SoftFallbackCrc32cx(SoftFallback.Crc32cx);
var dlgSoftFallbackCrc32h = new SoftFallbackCrc32h(SoftFallback.Crc32h);
var dlgSoftFallbackCrc32w = new SoftFallbackCrc32w(SoftFallback.Crc32w);
var dlgSoftFallbackCrc32x = new SoftFallbackCrc32x(SoftFallback.Crc32x);
var dlgSoftFallbackDecrypt = new SoftFallbackDecrypt(SoftFallback.Decrypt);
var dlgSoftFallbackEncrypt = new SoftFallbackEncrypt(SoftFallback.Encrypt);
var dlgSoftFallbackFixedRotate = new SoftFallbackFixedRotate(SoftFallback.FixedRotate);
var dlgSoftFallbackHashChoose = new SoftFallbackHashChoose(SoftFallback.HashChoose);
var dlgSoftFallbackHashLower = new SoftFallbackHashLower(SoftFallback.HashLower);
var dlgSoftFallbackHashMajority = new SoftFallbackHashMajority(SoftFallback.HashMajority);
var dlgSoftFallbackHashParity = new SoftFallbackHashParity(SoftFallback.HashParity);
var dlgSoftFallbackHashUpper = new SoftFallbackHashUpper(SoftFallback.HashUpper);
var dlgSoftFallbackInverseMixColumns = new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns);
var dlgSoftFallbackMixColumns = new SoftFallbackMixColumns(SoftFallback.MixColumns);
var dlgSoftFallbackPolynomialMult64_128 = new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128);
var dlgSoftFallbackSatF32ToS32 = new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32);
var dlgSoftFallbackSatF32ToS64 = new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64);
var dlgSoftFallbackSatF32ToU32 = new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32);
var dlgSoftFallbackSatF32ToU64 = new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64);
var dlgSoftFallbackSatF64ToS32 = new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32);
var dlgSoftFallbackSatF64ToS64 = new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64);
var dlgSoftFallbackSatF64ToU32 = new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32);
var dlgSoftFallbackSatF64ToU64 = new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64);
var dlgSoftFallbackSha1SchedulePart1 = new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1);
var dlgSoftFallbackSha1SchedulePart2 = new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2);
var dlgSoftFallbackSha256SchedulePart1 = new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1);
var dlgSoftFallbackSha256SchedulePart2 = new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2);
var dlgSoftFallbackSignedShrImm64 = new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64);
var dlgSoftFallbackTbl1 = new SoftFallbackTbl1(SoftFallback.Tbl1);
var dlgSoftFallbackTbl2 = new SoftFallbackTbl2(SoftFallback.Tbl2);
var dlgSoftFallbackTbl3 = new SoftFallbackTbl3(SoftFallback.Tbl3);
var dlgSoftFallbackTbl4 = new SoftFallbackTbl4(SoftFallback.Tbl4);
var dlgSoftFallbackTbx1 = new SoftFallbackTbx1(SoftFallback.Tbx1);
var dlgSoftFallbackTbx2 = new SoftFallbackTbx2(SoftFallback.Tbx2);
var dlgSoftFallbackTbx3 = new SoftFallbackTbx3(SoftFallback.Tbx3);
var dlgSoftFallbackTbx4 = new SoftFallbackTbx4(SoftFallback.Tbx4);
var dlgSoftFallbackUnsignedShrImm64 = new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64);
SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert));
SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert));
var dlgSoftFloat16_32FPConvert = new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert);
var dlgSoftFloat16_64FPConvert = new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert);
SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd));
SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare));
SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ));
SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE));
SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT));
SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE));
SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT));
SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv));
SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax));
SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum));
SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin));
SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum));
SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul));
SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd));
SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub));
SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX));
SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd));
SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub));
SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate));
SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only.
SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused));
SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX));
SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate));
SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only.
SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused));
SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt));
SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub));
var dlgSoftFloat32FPAdd = new SoftFloat32FPAdd(SoftFloat32.FPAdd);
var dlgSoftFloat32FPAddFpscr = new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr); // A32 only.
var dlgSoftFloat32FPCompare = new SoftFloat32FPCompare(SoftFloat32.FPCompare);
var dlgSoftFloat32FPCompareEQ = new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ);
var dlgSoftFloat32FPCompareEQFpscr = new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr); // A32 only.
var dlgSoftFloat32FPCompareGE = new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE);
var dlgSoftFloat32FPCompareGEFpscr = new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr); // A32 only.
var dlgSoftFloat32FPCompareGT = new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT);
var dlgSoftFloat32FPCompareGTFpscr = new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr); // A32 only.
var dlgSoftFloat32FPCompareLE = new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE);
var dlgSoftFloat32FPCompareLEFpscr = new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr); // A32 only.
var dlgSoftFloat32FPCompareLT = new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT);
var dlgSoftFloat32FPCompareLTFpscr = new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr); // A32 only.
var dlgSoftFloat32FPDiv = new SoftFloat32FPDiv(SoftFloat32.FPDiv);
var dlgSoftFloat32FPMax = new SoftFloat32FPMax(SoftFloat32.FPMax);
var dlgSoftFloat32FPMaxFpscr = new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr); // A32 only.
var dlgSoftFloat32FPMaxNum = new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum);
var dlgSoftFloat32FPMaxNumFpscr = new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr); // A32 only.
var dlgSoftFloat32FPMin = new SoftFloat32FPMin(SoftFloat32.FPMin);
var dlgSoftFloat32FPMinFpscr = new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr); // A32 only.
var dlgSoftFloat32FPMinNum = new SoftFloat32FPMinNum(SoftFloat32.FPMinNum);
var dlgSoftFloat32FPMinNumFpscr = new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr); // A32 only.
var dlgSoftFloat32FPMul = new SoftFloat32FPMul(SoftFloat32.FPMul);
var dlgSoftFloat32FPMulFpscr = new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr); // A32 only.
var dlgSoftFloat32FPMulAdd = new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd);
var dlgSoftFloat32FPMulAddFpscr = new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr); // A32 only.
var dlgSoftFloat32FPMulSub = new SoftFloat32FPMulSub(SoftFloat32.FPMulSub);
var dlgSoftFloat32FPMulSubFpscr = new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr); // A32 only.
var dlgSoftFloat32FPMulX = new SoftFloat32FPMulX(SoftFloat32.FPMulX);
var dlgSoftFloat32FPNegMulAdd = new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd);
var dlgSoftFloat32FPNegMulSub = new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub);
var dlgSoftFloat32FPRecipEstimate = new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate);
var dlgSoftFloat32FPRecipEstimateFpscr = new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr); // A32 only.
var dlgSoftFloat32FPRecipStep = new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep); // A32 only.
var dlgSoftFloat32FPRecipStepFused = new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused);
var dlgSoftFloat32FPRecpX = new SoftFloat32FPRecpX(SoftFloat32.FPRecpX);
var dlgSoftFloat32FPRSqrtEstimate = new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate);
var dlgSoftFloat32FPRSqrtEstimateFpscr = new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr); // A32 only.
var dlgSoftFloat32FPRSqrtStep = new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep); // A32 only.
var dlgSoftFloat32FPRSqrtStepFused = new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused);
var dlgSoftFloat32FPSqrt = new SoftFloat32FPSqrt(SoftFloat32.FPSqrt);
var dlgSoftFloat32FPSub = new SoftFloat32FPSub(SoftFloat32.FPSub);
SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert));
var dlgSoftFloat32_16FPConvert = new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert);
SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd));
SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare));
SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ));
SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE));
SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT));
SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE));
SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT));
SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv));
SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax));
SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum));
SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin));
SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum));
SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul));
SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd));
SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub));
SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX));
SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd));
SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub));
SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate));
SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only.
SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused));
SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX));
SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate));
SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only.
SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused));
SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt));
SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub));
var dlgSoftFloat64FPAdd = new SoftFloat64FPAdd(SoftFloat64.FPAdd);
var dlgSoftFloat64FPAddFpscr = new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr); // A32 only.
var dlgSoftFloat64FPCompare = new SoftFloat64FPCompare(SoftFloat64.FPCompare);
var dlgSoftFloat64FPCompareEQ = new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ);
var dlgSoftFloat64FPCompareEQFpscr = new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr); // A32 only.
var dlgSoftFloat64FPCompareGE = new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE);
var dlgSoftFloat64FPCompareGEFpscr = new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr); // A32 only.
var dlgSoftFloat64FPCompareGT = new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT);
var dlgSoftFloat64FPCompareGTFpscr = new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr); // A32 only.
var dlgSoftFloat64FPCompareLE = new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE);
var dlgSoftFloat64FPCompareLEFpscr = new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr); // A32 only.
var dlgSoftFloat64FPCompareLT = new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT);
var dlgSoftFloat64FPCompareLTFpscr = new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr); // A32 only.
var dlgSoftFloat64FPDiv = new SoftFloat64FPDiv(SoftFloat64.FPDiv);
var dlgSoftFloat64FPMax = new SoftFloat64FPMax(SoftFloat64.FPMax);
var dlgSoftFloat64FPMaxFpscr = new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr); // A32 only.
var dlgSoftFloat64FPMaxNum = new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum);
var dlgSoftFloat64FPMaxNumFpscr = new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr); // A32 only.
var dlgSoftFloat64FPMin = new SoftFloat64FPMin(SoftFloat64.FPMin);
var dlgSoftFloat64FPMinFpscr = new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr); // A32 only.
var dlgSoftFloat64FPMinNum = new SoftFloat64FPMinNum(SoftFloat64.FPMinNum);
var dlgSoftFloat64FPMinNumFpscr = new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr); // A32 only.
var dlgSoftFloat64FPMul = new SoftFloat64FPMul(SoftFloat64.FPMul);
var dlgSoftFloat64FPMulFpscr = new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr); // A32 only.
var dlgSoftFloat64FPMulAdd = new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd);
var dlgSoftFloat64FPMulAddFpscr = new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr); // A32 only.
var dlgSoftFloat64FPMulSub = new SoftFloat64FPMulSub(SoftFloat64.FPMulSub);
var dlgSoftFloat64FPMulSubFpscr = new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr); // A32 only.
var dlgSoftFloat64FPMulX = new SoftFloat64FPMulX(SoftFloat64.FPMulX);
var dlgSoftFloat64FPNegMulAdd = new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd);
var dlgSoftFloat64FPNegMulSub = new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub);
var dlgSoftFloat64FPRecipEstimate = new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate);
var dlgSoftFloat64FPRecipEstimateFpscr = new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr); // A32 only.
var dlgSoftFloat64FPRecipStep = new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep); // A32 only.
var dlgSoftFloat64FPRecipStepFused = new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused);
var dlgSoftFloat64FPRecpX = new SoftFloat64FPRecpX(SoftFloat64.FPRecpX);
var dlgSoftFloat64FPRSqrtEstimate = new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate);
var dlgSoftFloat64FPRSqrtEstimateFpscr = new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr); // A32 only.
var dlgSoftFloat64FPRSqrtStep = new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep); // A32 only.
var dlgSoftFloat64FPRSqrtStepFused = new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused);
var dlgSoftFloat64FPSqrt = new SoftFloat64FPSqrt(SoftFloat64.FPSqrt);
var dlgSoftFloat64FPSub = new SoftFloat64FPSub(SoftFloat64.FPSub);
SetDelegateInfo(new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert));
var dlgSoftFloat64_16FPConvert = new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert);
SetDelegateInfo(dlgMathAbs, Marshal.GetFunctionPointerForDelegate<MathAbs>(dlgMathAbs));
SetDelegateInfo(dlgMathCeiling, Marshal.GetFunctionPointerForDelegate<MathCeiling>(dlgMathCeiling));
SetDelegateInfo(dlgMathFloor, Marshal.GetFunctionPointerForDelegate<MathFloor>(dlgMathFloor));
SetDelegateInfo(dlgMathRound, Marshal.GetFunctionPointerForDelegate<MathRound>(dlgMathRound));
SetDelegateInfo(dlgMathTruncate, Marshal.GetFunctionPointerForDelegate<MathTruncate>(dlgMathTruncate));
SetDelegateInfo(dlgMathFAbs, Marshal.GetFunctionPointerForDelegate<MathFAbs>(dlgMathFAbs));
SetDelegateInfo(dlgMathFCeiling, Marshal.GetFunctionPointerForDelegate<MathFCeiling>(dlgMathFCeiling));
SetDelegateInfo(dlgMathFFloor, Marshal.GetFunctionPointerForDelegate<MathFFloor>(dlgMathFFloor));
SetDelegateInfo(dlgMathFRound, Marshal.GetFunctionPointerForDelegate<MathFRound>(dlgMathFRound));
SetDelegateInfo(dlgMathFTruncate, Marshal.GetFunctionPointerForDelegate<MathFTruncate>(dlgMathFTruncate));
SetDelegateInfo(dlgNativeInterfaceBreak, Marshal.GetFunctionPointerForDelegate<NativeInterfaceBreak>(dlgNativeInterfaceBreak));
SetDelegateInfo(dlgNativeInterfaceCheckSynchronization, Marshal.GetFunctionPointerForDelegate<NativeInterfaceCheckSynchronization>(dlgNativeInterfaceCheckSynchronization));
SetDelegateInfo(dlgNativeInterfaceEnqueueForRejit, Marshal.GetFunctionPointerForDelegate<NativeInterfaceEnqueueForRejit>(dlgNativeInterfaceEnqueueForRejit));
SetDelegateInfo(dlgNativeInterfaceGetCntfrqEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntfrqEl0>(dlgNativeInterfaceGetCntfrqEl0));
SetDelegateInfo(dlgNativeInterfaceGetCntpctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntpctEl0>(dlgNativeInterfaceGetCntpctEl0));
SetDelegateInfo(dlgNativeInterfaceGetCntvctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntvctEl0>(dlgNativeInterfaceGetCntvctEl0));
SetDelegateInfo(dlgNativeInterfaceGetCtrEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCtrEl0>(dlgNativeInterfaceGetCtrEl0));
SetDelegateInfo(dlgNativeInterfaceGetDczidEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetDczidEl0>(dlgNativeInterfaceGetDczidEl0));
SetDelegateInfo(dlgNativeInterfaceGetFunctionAddress, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetFunctionAddress>(dlgNativeInterfaceGetFunctionAddress));
SetDelegateInfo(dlgNativeInterfaceInvalidateCacheLine, Marshal.GetFunctionPointerForDelegate<NativeInterfaceInvalidateCacheLine>(dlgNativeInterfaceInvalidateCacheLine));
SetDelegateInfo(dlgNativeInterfaceReadByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadByte>(dlgNativeInterfaceReadByte));
SetDelegateInfo(dlgNativeInterfaceReadUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt16>(dlgNativeInterfaceReadUInt16));
SetDelegateInfo(dlgNativeInterfaceReadUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt32>(dlgNativeInterfaceReadUInt32));
SetDelegateInfo(dlgNativeInterfaceReadUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt64>(dlgNativeInterfaceReadUInt64));
SetDelegateInfo(dlgNativeInterfaceReadVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadVector128>(dlgNativeInterfaceReadVector128));
SetDelegateInfo(dlgNativeInterfaceSignalMemoryTracking, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSignalMemoryTracking>(dlgNativeInterfaceSignalMemoryTracking));
SetDelegateInfo(dlgNativeInterfaceSupervisorCall, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSupervisorCall>(dlgNativeInterfaceSupervisorCall));
SetDelegateInfo(dlgNativeInterfaceThrowInvalidMemoryAccess, Marshal.GetFunctionPointerForDelegate<NativeInterfaceThrowInvalidMemoryAccess>(dlgNativeInterfaceThrowInvalidMemoryAccess));
SetDelegateInfo(dlgNativeInterfaceUndefined, Marshal.GetFunctionPointerForDelegate<NativeInterfaceUndefined>(dlgNativeInterfaceUndefined));
SetDelegateInfo(dlgNativeInterfaceWriteByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteByte>(dlgNativeInterfaceWriteByte));
SetDelegateInfo(dlgNativeInterfaceWriteUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt16>(dlgNativeInterfaceWriteUInt16));
SetDelegateInfo(dlgNativeInterfaceWriteUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt32>(dlgNativeInterfaceWriteUInt32));
SetDelegateInfo(dlgNativeInterfaceWriteUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt64>(dlgNativeInterfaceWriteUInt64));
SetDelegateInfo(dlgNativeInterfaceWriteVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteVector128>(dlgNativeInterfaceWriteVector128));
SetDelegateInfo(dlgSoftFallbackCountLeadingSigns, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingSigns>(dlgSoftFallbackCountLeadingSigns));
SetDelegateInfo(dlgSoftFallbackCountLeadingZeros, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingZeros>(dlgSoftFallbackCountLeadingZeros));
SetDelegateInfo(dlgSoftFallbackCrc32b, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32b>(dlgSoftFallbackCrc32b));
SetDelegateInfo(dlgSoftFallbackCrc32cb, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cb>(dlgSoftFallbackCrc32cb));
SetDelegateInfo(dlgSoftFallbackCrc32ch, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32ch>(dlgSoftFallbackCrc32ch));
SetDelegateInfo(dlgSoftFallbackCrc32cw, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cw>(dlgSoftFallbackCrc32cw));
SetDelegateInfo(dlgSoftFallbackCrc32cx, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cx>(dlgSoftFallbackCrc32cx));
SetDelegateInfo(dlgSoftFallbackCrc32h, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32h>(dlgSoftFallbackCrc32h));
SetDelegateInfo(dlgSoftFallbackCrc32w, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32w>(dlgSoftFallbackCrc32w));
SetDelegateInfo(dlgSoftFallbackCrc32x, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32x>(dlgSoftFallbackCrc32x));
SetDelegateInfo(dlgSoftFallbackDecrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackDecrypt>(dlgSoftFallbackDecrypt));
SetDelegateInfo(dlgSoftFallbackEncrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackEncrypt>(dlgSoftFallbackEncrypt));
SetDelegateInfo(dlgSoftFallbackFixedRotate, Marshal.GetFunctionPointerForDelegate<SoftFallbackFixedRotate>(dlgSoftFallbackFixedRotate));
SetDelegateInfo(dlgSoftFallbackHashChoose, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashChoose>(dlgSoftFallbackHashChoose));
SetDelegateInfo(dlgSoftFallbackHashLower, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashLower>(dlgSoftFallbackHashLower));
SetDelegateInfo(dlgSoftFallbackHashMajority, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashMajority>(dlgSoftFallbackHashMajority));
SetDelegateInfo(dlgSoftFallbackHashParity, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashParity>(dlgSoftFallbackHashParity));
SetDelegateInfo(dlgSoftFallbackHashUpper, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashUpper>(dlgSoftFallbackHashUpper));
SetDelegateInfo(dlgSoftFallbackInverseMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackInverseMixColumns>(dlgSoftFallbackInverseMixColumns));
SetDelegateInfo(dlgSoftFallbackMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackMixColumns>(dlgSoftFallbackMixColumns));
SetDelegateInfo(dlgSoftFallbackPolynomialMult64_128, Marshal.GetFunctionPointerForDelegate<SoftFallbackPolynomialMult64_128>(dlgSoftFallbackPolynomialMult64_128));
SetDelegateInfo(dlgSoftFallbackSatF32ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS32>(dlgSoftFallbackSatF32ToS32));
SetDelegateInfo(dlgSoftFallbackSatF32ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS64>(dlgSoftFallbackSatF32ToS64));
SetDelegateInfo(dlgSoftFallbackSatF32ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU32>(dlgSoftFallbackSatF32ToU32));
SetDelegateInfo(dlgSoftFallbackSatF32ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU64>(dlgSoftFallbackSatF32ToU64));
SetDelegateInfo(dlgSoftFallbackSatF64ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS32>(dlgSoftFallbackSatF64ToS32));
SetDelegateInfo(dlgSoftFallbackSatF64ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS64>(dlgSoftFallbackSatF64ToS64));
SetDelegateInfo(dlgSoftFallbackSatF64ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU32>(dlgSoftFallbackSatF64ToU32));
SetDelegateInfo(dlgSoftFallbackSatF64ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU64>(dlgSoftFallbackSatF64ToU64));
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart1>(dlgSoftFallbackSha1SchedulePart1));
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart2>(dlgSoftFallbackSha1SchedulePart2));
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart1>(dlgSoftFallbackSha256SchedulePart1));
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart2>(dlgSoftFallbackSha256SchedulePart2));
SetDelegateInfo(dlgSoftFallbackSignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSignedShrImm64>(dlgSoftFallbackSignedShrImm64));
SetDelegateInfo(dlgSoftFallbackTbl1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl1>(dlgSoftFallbackTbl1));
SetDelegateInfo(dlgSoftFallbackTbl2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl2>(dlgSoftFallbackTbl2));
SetDelegateInfo(dlgSoftFallbackTbl3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl3>(dlgSoftFallbackTbl3));
SetDelegateInfo(dlgSoftFallbackTbl4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl4>(dlgSoftFallbackTbl4));
SetDelegateInfo(dlgSoftFallbackTbx1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx1>(dlgSoftFallbackTbx1));
SetDelegateInfo(dlgSoftFallbackTbx2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx2>(dlgSoftFallbackTbx2));
SetDelegateInfo(dlgSoftFallbackTbx3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx3>(dlgSoftFallbackTbx3));
SetDelegateInfo(dlgSoftFallbackTbx4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx4>(dlgSoftFallbackTbx4));
SetDelegateInfo(dlgSoftFallbackUnsignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackUnsignedShrImm64>(dlgSoftFallbackUnsignedShrImm64));
SetDelegateInfo(dlgSoftFloat16_32FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_32FPConvert>(dlgSoftFloat16_32FPConvert));
SetDelegateInfo(dlgSoftFloat16_64FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_64FPConvert>(dlgSoftFloat16_64FPConvert));
SetDelegateInfo(dlgSoftFloat32FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAdd>(dlgSoftFloat32FPAdd));
SetDelegateInfo(dlgSoftFloat32FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAddFpscr>(dlgSoftFloat32FPAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompare>(dlgSoftFloat32FPCompare));
SetDelegateInfo(dlgSoftFloat32FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQ>(dlgSoftFloat32FPCompareEQ));
SetDelegateInfo(dlgSoftFloat32FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQFpscr>(dlgSoftFloat32FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGE>(dlgSoftFloat32FPCompareGE));
SetDelegateInfo(dlgSoftFloat32FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGEFpscr>(dlgSoftFloat32FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGT>(dlgSoftFloat32FPCompareGT));
SetDelegateInfo(dlgSoftFloat32FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGTFpscr>(dlgSoftFloat32FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLE>(dlgSoftFloat32FPCompareLE));
SetDelegateInfo(dlgSoftFloat32FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLEFpscr>(dlgSoftFloat32FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLT>(dlgSoftFloat32FPCompareLT));
SetDelegateInfo(dlgSoftFloat32FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLTFpscr>(dlgSoftFloat32FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPDiv>(dlgSoftFloat32FPDiv));
SetDelegateInfo(dlgSoftFloat32FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMax>(dlgSoftFloat32FPMax));
SetDelegateInfo(dlgSoftFloat32FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxFpscr>(dlgSoftFloat32FPMaxFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNum>(dlgSoftFloat32FPMaxNum));
SetDelegateInfo(dlgSoftFloat32FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNumFpscr>(dlgSoftFloat32FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMin>(dlgSoftFloat32FPMin));
SetDelegateInfo(dlgSoftFloat32FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinFpscr>(dlgSoftFloat32FPMinFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNum>(dlgSoftFloat32FPMinNum));
SetDelegateInfo(dlgSoftFloat32FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNumFpscr>(dlgSoftFloat32FPMinNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMul>(dlgSoftFloat32FPMul));
SetDelegateInfo(dlgSoftFloat32FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulFpscr>(dlgSoftFloat32FPMulFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAdd>(dlgSoftFloat32FPMulAdd));
SetDelegateInfo(dlgSoftFloat32FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAddFpscr>(dlgSoftFloat32FPMulAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSub>(dlgSoftFloat32FPMulSub));
SetDelegateInfo(dlgSoftFloat32FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSubFpscr>(dlgSoftFloat32FPMulSubFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulX>(dlgSoftFloat32FPMulX));
SetDelegateInfo(dlgSoftFloat32FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulAdd>(dlgSoftFloat32FPNegMulAdd));
SetDelegateInfo(dlgSoftFloat32FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulSub>(dlgSoftFloat32FPNegMulSub));
SetDelegateInfo(dlgSoftFloat32FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimate>(dlgSoftFloat32FPRecipEstimate));
SetDelegateInfo(dlgSoftFloat32FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimateFpscr>(dlgSoftFloat32FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStep>(dlgSoftFloat32FPRecipStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStepFused>(dlgSoftFloat32FPRecipStepFused));
SetDelegateInfo(dlgSoftFloat32FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecpX>(dlgSoftFloat32FPRecpX));
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimate>(dlgSoftFloat32FPRSqrtEstimate));
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimateFpscr>(dlgSoftFloat32FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStep>(dlgSoftFloat32FPRSqrtStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStepFused>(dlgSoftFloat32FPRSqrtStepFused));
SetDelegateInfo(dlgSoftFloat32FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSqrt>(dlgSoftFloat32FPSqrt));
SetDelegateInfo(dlgSoftFloat32FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSub>(dlgSoftFloat32FPSub));
SetDelegateInfo(dlgSoftFloat32_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat32_16FPConvert>(dlgSoftFloat32_16FPConvert));
SetDelegateInfo(dlgSoftFloat64FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAdd>(dlgSoftFloat64FPAdd));
SetDelegateInfo(dlgSoftFloat64FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAddFpscr>(dlgSoftFloat64FPAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompare>(dlgSoftFloat64FPCompare));
SetDelegateInfo(dlgSoftFloat64FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQ>(dlgSoftFloat64FPCompareEQ));
SetDelegateInfo(dlgSoftFloat64FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQFpscr>(dlgSoftFloat64FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGE>(dlgSoftFloat64FPCompareGE));
SetDelegateInfo(dlgSoftFloat64FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGEFpscr>(dlgSoftFloat64FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGT>(dlgSoftFloat64FPCompareGT));
SetDelegateInfo(dlgSoftFloat64FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGTFpscr>(dlgSoftFloat64FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLE>(dlgSoftFloat64FPCompareLE));
SetDelegateInfo(dlgSoftFloat64FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLEFpscr>(dlgSoftFloat64FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLT>(dlgSoftFloat64FPCompareLT));
SetDelegateInfo(dlgSoftFloat64FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLTFpscr>(dlgSoftFloat64FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPDiv>(dlgSoftFloat64FPDiv));
SetDelegateInfo(dlgSoftFloat64FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMax>(dlgSoftFloat64FPMax));
SetDelegateInfo(dlgSoftFloat64FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxFpscr>(dlgSoftFloat64FPMaxFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNum>(dlgSoftFloat64FPMaxNum));
SetDelegateInfo(dlgSoftFloat64FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNumFpscr>(dlgSoftFloat64FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMin>(dlgSoftFloat64FPMin));
SetDelegateInfo(dlgSoftFloat64FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinFpscr>(dlgSoftFloat64FPMinFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNum>(dlgSoftFloat64FPMinNum));
SetDelegateInfo(dlgSoftFloat64FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNumFpscr>(dlgSoftFloat64FPMinNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMul>(dlgSoftFloat64FPMul));
SetDelegateInfo(dlgSoftFloat64FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulFpscr>(dlgSoftFloat64FPMulFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAdd>(dlgSoftFloat64FPMulAdd));
SetDelegateInfo(dlgSoftFloat64FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAddFpscr>(dlgSoftFloat64FPMulAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSub>(dlgSoftFloat64FPMulSub));
SetDelegateInfo(dlgSoftFloat64FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSubFpscr>(dlgSoftFloat64FPMulSubFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulX>(dlgSoftFloat64FPMulX));
SetDelegateInfo(dlgSoftFloat64FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulAdd>(dlgSoftFloat64FPNegMulAdd));
SetDelegateInfo(dlgSoftFloat64FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulSub>(dlgSoftFloat64FPNegMulSub));
SetDelegateInfo(dlgSoftFloat64FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimate>(dlgSoftFloat64FPRecipEstimate));
SetDelegateInfo(dlgSoftFloat64FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimateFpscr>(dlgSoftFloat64FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStep>(dlgSoftFloat64FPRecipStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStepFused>(dlgSoftFloat64FPRecipStepFused));
SetDelegateInfo(dlgSoftFloat64FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecpX>(dlgSoftFloat64FPRecpX));
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimate>(dlgSoftFloat64FPRSqrtEstimate));
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimateFpscr>(dlgSoftFloat64FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStep>(dlgSoftFloat64FPRSqrtStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStepFused>(dlgSoftFloat64FPRSqrtStepFused));
SetDelegateInfo(dlgSoftFloat64FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSqrt>(dlgSoftFloat64FPSqrt));
SetDelegateInfo(dlgSoftFloat64FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSub>(dlgSoftFloat64FPSub));
SetDelegateInfo(dlgSoftFloat64_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat64_16FPConvert>(dlgSoftFloat64_16FPConvert));
}
private delegate double MathAbs(double value);

View File

@ -80,7 +80,10 @@ namespace ARMeilleure.Translation
return true;
}
Monitor.Wait(Sync);
if (!_disposed)
{
Monitor.Wait(Sync);
}
}
}

View File

@ -18,16 +18,12 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
if (version == 2)
{
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2,
PerformanceEntryVersion2,
PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
}
if (version == 1)
{
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1,
PerformanceEntryVersion1,
PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
}
throw new NotImplementedException($"Unknown Performance metrics data format version {version}");

View File

@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
{
performanceEntry = null;
if (_entryDetailIndex > MaxFrameDetailCount)
if (_entryDetailIndex >= MaxFrameDetailCount)
{
return false;
}
@ -245,7 +245,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
};
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _entryDetailIndex);
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<TEntryDetail>() * _entryDetailIndex);
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];

View File

@ -1,13 +1,33 @@
using Ryujinx.Common.Utilities;
using System;
namespace Ryujinx.Common.GraphicsDriver
{
public static class DriverUtilities
{
private static void AddMesaFlags(string envVar, string newFlags)
{
string existingFlags = Environment.GetEnvironmentVariable(envVar);
string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
}
public static void InitDriverConfig(bool oglThreading)
{
if (OperatingSystem.IsLinux())
{
AddMesaFlags("RADV_DEBUG", "nodcc");
}
ToggleOGLThreading(oglThreading);
}
public static void ToggleOGLThreading(bool enabled)
{
Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
try
{

View File

@ -1,51 +0,0 @@
using System;
using System.Buffers;
using System.Threading;
namespace Ryujinx.Common.Memory
{
public partial class ByteMemoryPool
{
/// <summary>
/// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
/// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/>
/// with a length of the requested size.
/// </summary>
private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte>
{
private byte[] _array;
private readonly int _length;
public ByteMemoryPoolBuffer(int length)
{
_array = ArrayPool<byte>.Shared.Rent(length);
_length = length;
}
/// <summary>
/// Returns a <see cref="Memory{Byte}"/> belonging to this owner.
/// </summary>
public Memory<byte> Memory
{
get
{
byte[] array = _array;
ObjectDisposedException.ThrowIf(array is null, this);
return new Memory<byte>(array, 0, _length);
}
}
public void Dispose()
{
var array = Interlocked.Exchange(ref _array, null);
if (array != null)
{
ArrayPool<byte>.Shared.Return(array);
}
}
}
}
}

View File

@ -1,106 +0,0 @@
using System;
using System.Buffers;
namespace Ryujinx.Common.Memory
{
/// <summary>
/// Provides a pool of re-usable byte array instances.
/// </summary>
public static partial class ByteMemoryPool
{
/// <summary>
/// Returns the maximum buffer size supported by this pool.
/// </summary>
public static int MaxBufferSize => Array.MaxLength;
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> Rent(long length)
=> RentImpl(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> Rent(ulong length)
=> RentImpl(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> Rent(int length)
=> RentImpl(length);
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> RentCleared(long length)
=> RentCleared(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> RentCleared(ulong length)
=> RentCleared(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> RentCleared(int length)
{
var buffer = RentImpl(length);
buffer.Memory.Span.Clear();
return buffer;
}
/// <summary>
/// Copies <paramref name="buffer"/> into a newly rented byte memory buffer.
/// </summary>
/// <param name="buffer">The byte buffer to copy</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory with <paramref name="buffer"/> copied to it</returns>
public static IMemoryOwner<byte> RentCopy(ReadOnlySpan<byte> buffer)
{
var copy = RentImpl(buffer.Length);
buffer.CopyTo(copy.Memory.Span);
return copy;
}
private static ByteMemoryPoolBuffer RentImpl(int length)
{
if ((uint)length > Array.MaxLength)
{
throw new ArgumentOutOfRangeException(nameof(length), length, null);
}
return new ByteMemoryPoolBuffer(length);
}
}
}

View File

@ -1,6 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using System;
using System.Buffers;
using System.IO;
using System.Linq;
using System.Reflection;
@ -42,14 +42,14 @@ namespace Ryujinx.Common
return StreamUtils.StreamToBytes(stream);
}
public static IMemoryOwner<byte> ReadFileToRentedMemory(string filename)
public static MemoryOwner<byte> ReadFileToRentedMemory(string filename)
{
var (assembly, path) = ResolveManifestPath(filename);
return ReadFileToRentedMemory(assembly, path);
}
public static IMemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
public static MemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
{
using var stream = GetStream(assembly, filename);

View File

@ -0,0 +1,24 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Ryujinx.Common.Utilities
{
public partial class OsUtils
{
[LibraryImport("libc", SetLastError = true)]
private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
public static void SetEnvironmentVariableNoCaching(string key, string value)
{
// Set the value in the cached environment variables, too.
Environment.SetEnvironmentVariable(key, value);
if (!OperatingSystem.IsWindows())
{
int res = setenv(key, value, 1);
Debug.Assert(res != -1);
}
}
}
}

View File

@ -1,6 +1,5 @@
using Microsoft.IO;
using Ryujinx.Common.Memory;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -16,7 +15,7 @@ namespace Ryujinx.Common.Utilities
return output.ToArray();
}
public static IMemoryOwner<byte> StreamToRentedMemory(Stream input)
public static MemoryOwner<byte> StreamToRentedMemory(Stream input)
{
if (input is MemoryStream inputMemoryStream)
{
@ -26,9 +25,9 @@ namespace Ryujinx.Common.Utilities
{
long bytesExpected = input.Length;
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(bytesExpected);
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
var destSpan = ownedMemory.Memory.Span;
var destSpan = ownedMemory.Span;
int totalBytesRead = 0;
@ -66,14 +65,14 @@ namespace Ryujinx.Common.Utilities
return stream.ToArray();
}
private static IMemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
private static MemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
{
input.Position = 0;
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(input.Length);
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)input.Length));
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
_ = input.Read(ownedMemory.Memory.Span);
_ = input.Read(ownedMemory.Span);
return ownedMemory;
}

View File

@ -303,9 +303,9 @@ namespace Ryujinx.Cpu.Jit
}
else
{
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
Read(va, memoryOwner.Memory.Span);
Read(va, memoryOwner.Span);
return new WritableRegion(this, va, memoryOwner);
}

View File

@ -1,6 +1,5 @@
using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using System.Diagnostics;
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
{

View File

@ -114,7 +114,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
{
context.Arm64Assembler.Add(d, n, m);
EmitSaturateUnsignedRange(context, d, 16);
EmitSaturateUqadd(context, d, 16);
});
}
@ -123,7 +123,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
{
context.Arm64Assembler.Add(d, n, m);
EmitSaturateUnsignedRange(context, d, 8);
EmitSaturateUqadd(context, d, 8);
});
}
@ -140,7 +140,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
context.Arm64Assembler.Add(d, n, m);
}
EmitSaturateUnsignedRange(context, d, 16);
EmitSaturateUq(context, d, 16, e == 0);
});
}
@ -157,25 +157,25 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
context.Arm64Assembler.Sub(d, n, m);
}
EmitSaturateUnsignedRange(context, d, 16);
EmitSaturateUq(context, d, 16, e != 0);
});
}
public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm)
{
InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) =>
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
{
context.Arm64Assembler.Sub(d, n, m);
EmitSaturateUnsignedRange(context, d, 16);
EmitSaturateUqsub(context, d, 16);
});
}
public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm)
{
InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) =>
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
{
context.Arm64Assembler.Sub(d, n, m);
EmitSaturateUnsignedRange(context, d, 8);
EmitSaturateUqsub(context, d, 8);
});
}
@ -358,7 +358,17 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
}
}
private static void EmitSaturateUnsignedRange(CodeGenContext context, Operand value, uint saturateTo)
private static void EmitSaturateUqadd(CodeGenContext context, Operand value, uint saturateTo)
{
EmitSaturateUq(context, value, saturateTo, isSub: false);
}
private static void EmitSaturateUqsub(CodeGenContext context, Operand value, uint saturateTo)
{
EmitSaturateUq(context, value, saturateTo, isSub: true);
}
private static void EmitSaturateUq(CodeGenContext context, Operand value, uint saturateTo, bool isSub)
{
Debug.Assert(saturateTo <= 32);
@ -379,7 +389,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
return;
}
context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const(32 - (int)saturateTo));
context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const((int)saturateTo));
int branchIndex = context.CodeWriter.InstructionPointer;
@ -387,7 +397,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
context.Arm64Assembler.Cbz(tempRegister.Operand, 0);
// Saturate.
context.Arm64Assembler.Mov(value, uint.MaxValue >> (32 - (int)saturateTo));
context.Arm64Assembler.Mov(value, isSub ? 0u : uint.MaxValue >> (32 - (int)saturateTo));
int delta = context.CodeWriter.InstructionPointer - branchIndex;
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));

View File

@ -1,7 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -145,9 +144,9 @@ namespace Ryujinx.Graphics.Device
}
else
{
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
ReadImpl(va, memoryOwner.Span);
return new WritableRegion(this, va, memoryOwner, tracked: true);
}

View File

@ -39,7 +39,10 @@ namespace Ryujinx.Graphics.Device
{
var field = fields[fieldIndex];
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
var currentFieldOffset = (int)Marshal.OffsetOf<TState>(field.Name);
var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf<TState>() : (int)Marshal.OffsetOf<TState>(fields[fieldIndex + 1].Name);
int sizeOfField = nextFieldOffset - currentFieldOffset;
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
{

View File

@ -1,63 +0,0 @@
using System;
using System.Reflection;
namespace Ryujinx.Graphics.Device
{
public static class SizeCalculator
{
public static int SizeOf(Type type)
{
// Is type a enum type?
if (type.IsEnum)
{
type = type.GetEnumUnderlyingType();
}
// Is type a pointer type?
if (type.IsPointer || type == typeof(IntPtr) || type == typeof(UIntPtr))
{
return IntPtr.Size;
}
// Is type a struct type?
if (type.IsValueType && !type.IsPrimitive)
{
// Check if the struct has a explicit size, if so, return that.
if (type.StructLayoutAttribute.Size != 0)
{
return type.StructLayoutAttribute.Size;
}
// Otherwise we calculate the sum of the sizes of all fields.
int size = 0;
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
{
size += SizeOf(fields[fieldIndex].FieldType);
}
return size;
}
// Primitive types.
return (Type.GetTypeCode(type)) switch
{
TypeCode.SByte => sizeof(sbyte),
TypeCode.Byte => sizeof(byte),
TypeCode.Int16 => sizeof(short),
TypeCode.UInt16 => sizeof(ushort),
TypeCode.Int32 => sizeof(int),
TypeCode.UInt32 => sizeof(uint),
TypeCode.Int64 => sizeof(long),
TypeCode.UInt64 => sizeof(ulong),
TypeCode.Char => sizeof(char),
TypeCode.Single => sizeof(float),
TypeCode.Double => sizeof(double),
TypeCode.Decimal => sizeof(decimal),
TypeCode.Boolean => sizeof(bool),
_ => throw new ArgumentException($"Length for type \"{type.Name}\" is unknown."),
};
}
}
}

View File

@ -74,13 +74,15 @@ namespace Ryujinx.Graphics.GAL
public int ArrayLength { get; }
public ResourceType Type { get; }
public ResourceStages Stages { get; }
public bool Write { get; }
public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages)
public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages, bool write)
{
Binding = binding;
ArrayLength = arrayLength;
Type = type;
Stages = stages;
Write = write;
}
public override int GetHashCode()

View File

@ -1,6 +1,5 @@
using Ryujinx.Common;
using System;
using System.Numerics;
namespace Ryujinx.Graphics.GAL
{
@ -113,25 +112,6 @@ namespace Ryujinx.Graphics.GAL
return 1;
}
public int GetLevelsClamped()
{
int maxSize = Width;
if (Target != Target.Texture1D &&
Target != Target.Texture1DArray)
{
maxSize = Math.Max(maxSize, Height);
}
if (Target == Target.Texture3D)
{
maxSize = Math.Max(maxSize, Depth);
}
int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
return Math.Min(Levels, maxLevels);
}
private static int GetLevelSize(int size, int level)
{
return Math.Max(1, size >> level);

View File

@ -276,8 +276,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
dstBaseOffset += dstStride * (yCount - 1);
}
ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true);
// If remapping is disabled, we always copy the components directly, in order.
// If it's enabled, but the mapping is just XYZW, we also copy them in order.
bool isIdentityRemap = !remap ||
@ -289,6 +287,52 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
bool completeSource = IsTextureCopyComplete(src, srcLinear, srcBpp, srcStride, xCount, yCount);
bool completeDest = IsTextureCopyComplete(dst, dstLinear, dstBpp, dstStride, xCount, yCount);
// Check if the source texture exists on the GPU, if it does, do a GPU side copy.
// Otherwise, we would need to flush the source texture which is costly.
// We don't expect the source to be linear in such cases, as linear source usually indicates buffer or CPU written data.
if (completeSource && completeDest && !srcLinear && isIdentityRemap)
{
var source = memoryManager.Physical.TextureCache.FindTexture(
memoryManager,
srcGpuVa,
srcBpp,
srcStride,
src.Height,
xCount,
yCount,
srcLinear,
src.MemoryLayout.UnpackGobBlocksInY(),
src.MemoryLayout.UnpackGobBlocksInZ());
if (source != null && source.Height == yCount)
{
source.SynchronizeMemory();
var target = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager,
source.Info.FormatInfo,
dstGpuVa,
xCount,
yCount,
dstStride,
dstLinear,
dst.MemoryLayout.UnpackGobBlocksInY(),
dst.MemoryLayout.UnpackGobBlocksInZ());
if (source.ScaleFactor != target.ScaleFactor)
{
target.PropagateScale(source);
}
source.HostTexture.CopyTo(target.HostTexture, 0, 0);
target.SignalModified();
return;
}
}
ReadOnlySpan<byte> srcSpan = memoryManager.GetSpan(srcGpuVa + (ulong)srcBaseOffset, srcSize, true);
// Try to set the texture data directly,
// but only if we are doing a complete copy,
// and not for block linear to linear copies, since those are typically accessed from the CPU.

View File

@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
if (target != null)
{
target.SynchronizeMemory();
var dataCopy = ByteMemoryPool.RentCopy(data);
var dataCopy = MemoryOwner<byte>.RentCopy(data);
target.SetData(dataCopy, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount));
target.SignalModified();

View File

@ -79,7 +79,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
var field = fields[fieldIndex];
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
var currentFieldOffset = (int)Marshal.OffsetOf<TState>(field.Name);
var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf<TState>() : (int)Marshal.OffsetOf<TState>(fields[fieldIndex + 1].Name);
int sizeOfField = nextFieldOffset - currentFieldOffset;
if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex))
{

View File

@ -415,7 +415,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
#pragma warning disable CS0649 // Field is never assigned to
public int Width;
public int Height;
public int Depth;
public ushort Depth;
public ushort Flags;
public readonly bool UnpackIsLayered()
{
return (Flags & 1) == 0;
}
#pragma warning restore CS0649
}

View File

@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
@ -805,7 +806,7 @@ namespace Ryujinx.Graphics.Gpu.Image
sliceDepth,
levels,
layers,
out IMemoryOwner<byte> decoded))
out MemoryOwner<byte> decoded))
{
string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";

View File

@ -340,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>True if any used entries of the pool might have been modified, false otherwise</returns>
public bool SamplerPoolModified()
{
return SamplerPool.WasModified(ref _samplerPoolSequence);
return SamplerPool != null && SamplerPool.WasModified(ref _samplerPoolSequence);
}
}
@ -516,12 +516,15 @@ namespace Ryujinx.Graphics.Gpu.Image
}
// Check if any of our cached samplers changed on the pool.
foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds)
if (SamplerPool != null)
{
if (SamplerPool.GetCachedItem(samplerId) != sampler ||
(sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor)))
foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds)
{
return true;
if (SamplerPool.GetCachedItem(samplerId) != sampler ||
(sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor)))
{
return true;
}
}
}
@ -899,13 +902,19 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
Sampler sampler = samplerPool?.Get(samplerId);
entry.TextureIds[textureId] = (texture, descriptor);
entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default);
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
ISampler hostSampler = sampler?.GetHostSampler(texture);
ISampler hostSampler = null;
if (!isImage && bindingInfo.Target != Target.TextureBuffer)
{
Sampler sampler = samplerPool?.Get(samplerId);
entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default);
hostSampler = sampler?.GetHostSampler(texture);
}
Format format = bindingInfo.Format;

View File

@ -347,6 +347,53 @@ namespace Ryujinx.Graphics.Gpu.Image
return texture;
}
/// <summary>
/// Tries to find an existing texture, or create a new one if not found.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="formatInfo">Format of the texture</param>
/// <param name="gpuAddress">GPU virtual address of the texture</param>
/// <param name="xCount">Texture width in bytes</param>
/// <param name="yCount">Texture height</param>
/// <param name="stride">Texture stride if linear, otherwise ignored</param>
/// <param name="isLinear">Indicates if the texture is linear or block linear</param>
/// <param name="gobBlocksInY">GOB blocks in Y for block linear textures</param>
/// <param name="gobBlocksInZ">GOB blocks in Z for 3D block linear textures</param>
/// <returns>The texture</returns>
public Texture FindOrCreateTexture(
MemoryManager memoryManager,
FormatInfo formatInfo,
ulong gpuAddress,
int xCount,
int yCount,
int stride,
bool isLinear,
int gobBlocksInY,
int gobBlocksInZ)
{
TextureInfo info = new(
gpuAddress,
xCount / formatInfo.BytesPerPixel,
yCount,
1,
1,
1,
1,
stride,
isLinear,
gobBlocksInY,
gobBlocksInZ,
1,
Target.Texture2D,
formatInfo);
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.ForCopy, info, 0, sizeHint: new Size(xCount, yCount, 1));
texture?.SynchronizeMemory();
return texture;
}
/// <summary>
/// Tries to find an existing texture, or create a new one if not found.
/// </summary>
@ -468,13 +515,11 @@ namespace Ryujinx.Graphics.Gpu.Image
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
layered &= size.UnpackIsLayered();
Target target;
if (dsState.MemoryLayout.UnpackIsTarget3D())
{
target = Target.Texture3D;
}
else if ((samplesInX | samplesInY) != 1)
if ((samplesInX | samplesInY) != 1)
{
target = size.Depth > 1 && layered
? Target.Texture2DMultisampleArray

View File

@ -6,6 +6,7 @@ using Ryujinx.Memory.Range;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Numerics;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Image
@ -490,6 +491,8 @@ namespace Ryujinx.Graphics.Gpu.Image
levels = (maxLod - minLod) + 1;
}
levels = ClampLevels(target, width, height, depthOrLayers, levels);
SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
@ -540,6 +543,34 @@ namespace Ryujinx.Graphics.Gpu.Image
swizzleA);
}
/// <summary>
/// Clamps the amount of mipmap levels to the maximum allowed for the given texture dimensions.
/// </summary>
/// <param name="target">Number of texture dimensions (1D, 2D, 3D, Cube, etc)</param>
/// <param name="width">Width of the texture</param>
/// <param name="height">Height of the texture, ignored for 1D textures</param>
/// <param name="depthOrLayers">Depth of the texture for 3D textures, otherwise ignored</param>
/// <param name="levels">Original amount of mipmap levels</param>
/// <returns>Clamped mipmap levels</returns>
private static int ClampLevels(Target target, int width, int height, int depthOrLayers, int levels)
{
int maxSize = width;
if (target != Target.Texture1D &&
target != Target.Texture1DArray)
{
maxSize = Math.Max(maxSize, height);
}
if (target == Target.Texture3D)
{
maxSize = Math.Max(maxSize, depthOrLayers);
}
int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
return Math.Min(levels, maxLevels);
}
/// <summary>
/// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
/// The depth-stencil mode is determined based on how the driver sets those parameters.

View File

@ -2,7 +2,6 @@ using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -242,9 +241,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
ReadImpl(va, memoryOwner.Span, tracked);
return new WritableRegion(this, va, memoryOwner, tracked);
}

View File

@ -192,9 +192,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(range.GetSize());
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(checked((int)range.GetSize()));
Memory<byte> memory = memoryOwner.Memory;
Span<byte> memorySpan = memoryOwner.Span;
int offset = 0;
for (int i = 0; i < range.Count; i++)
@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
int size = (int)currentRange.Size;
if (currentRange.Address != MemoryManager.PteUnmapped)
{
GetSpan(currentRange.Address, size).CopyTo(memory.Span.Slice(offset, size));
GetSpan(currentRange.Address, size).CopyTo(memorySpan.Slice(offset, size));
}
offset += size;
}

View File

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

View File

@ -78,9 +78,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers);
PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers, true);
PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages);
PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages, true);
}
/// <summary>
@ -91,10 +91,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="setIndex">Resource set index where the resources are used</param>
/// <param name="start">First binding number</param>
/// <param name="count">Amount of bindings</param>
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count)
/// <param name="write">True if the binding is written from the shader, false otherwise</param>
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
{
AddDescriptor(stages, type, setIndex, start, count);
AddUsage(stages, type, setIndex, start, count);
AddUsage(stages, type, setIndex, start, count, write);
}
/// <summary>
@ -216,11 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
/// <param name="binding">Binding number where the resource will be bound</param>
/// <param name="count">Number of resources bound at the binding location</param>
private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
/// <param name="write">True if the binding is written from the shader, false otherwise</param>
private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count, bool write = false)
{
for (int index = 0; index < count; index++)
{
_resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages));
_resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages, write));
}
}
@ -238,7 +240,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
buffer.Binding,
1,
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
stages));
stages,
buffer.Flags.HasFlag(BufferUsageFlags.Write)));
}
}
@ -254,7 +257,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ResourceType type = GetTextureResourceType(texture, isImage);
GetUsages(texture.Set).Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
GetUsages(texture.Set).Add(new ResourceUsage(
texture.Binding,
texture.ArrayLength,
type,
stages,
texture.Flags.HasFlag(TextureUsageFlags.ImageStore)));
}
}

View File

@ -1,6 +1,5 @@
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -10,11 +9,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
{
static class FormatConverter
{
public unsafe static IMemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
public unsafe static MemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
{
IMemoryOwner<byte> outputMemory = ByteMemoryPool.Rent(data.Length);
MemoryOwner<byte> outputMemory = MemoryOwner<byte>.Rent(data.Length);
Span<byte> output = outputMemory.Memory.Span;
Span<byte> output = outputMemory.Span;
int start = 0;

View File

@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
internalFormat = (SizedInternalFormat)format.PixelInternalFormat;
}
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
switch (Info.Target)
{

View File

@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
pixelInternalFormat = format.PixelInternalFormat;
}
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
GL.TextureView(
Handle,
@ -267,7 +267,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
public unsafe PinnedSpan<byte> GetData()
{
int size = 0;
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
for (int level = 0; level < levels; level++)
{
@ -426,7 +426,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
faces = 6;
}
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
for (int level = 0; level < levels; level++)
{
@ -716,7 +716,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
int width = Info.Width;
int height = Info.Height;
int depth = Info.Depth;
int levels = Info.GetLevelsClamped();
int levels = Info.Levels;
int offset = 0;

View File

@ -155,9 +155,14 @@ namespace Ryujinx.Graphics.Shader.Translation
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
}
if (operation.Dest != null && operation.Dest.Type == OperandType.Register)
for (int dstIndex = 0; dstIndex < operation.DestsCount; dstIndex++)
{
localOutputs[block.Index] |= GetMask(operation.Dest.GetRegister());
Operand dest = operation.GetDest(dstIndex);
if (dest != null && dest.Type == OperandType.Register)
{
localOutputs[block.Index] |= GetMask(dest.GetRegister());
}
}
}
}

View File

@ -1,7 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using System;
using System.Buffers;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
@ -293,9 +292,9 @@ namespace Ryujinx.Graphics.Texture.Astc
int depth,
int levels,
int layers,
out IMemoryOwner<byte> decoded)
out MemoryOwner<byte> decoded)
{
decoded = ByteMemoryPool.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
decoded = MemoryOwner<byte>.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers);

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -14,7 +13,7 @@ namespace Ryujinx.Graphics.Texture
private const int BlockWidth = 4;
private const int BlockHeight = 4;
public static IMemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
public static MemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
int size = 0;
@ -23,12 +22,12 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
@ -102,7 +101,7 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
public static MemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
int size = 0;
@ -111,12 +110,12 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
@ -197,7 +196,7 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
public static MemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
int size = 0;
@ -206,13 +205,13 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
Span<byte> rPal = stackalloc byte[8];
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
@ -294,7 +293,7 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
public static MemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
{
int size = 0;
@ -306,8 +305,8 @@ namespace Ryujinx.Graphics.Texture
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
int alignedWidth = BitUtils.AlignUp(width, 4);
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
Span<byte> outputSpan = output.Memory.Span;
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Span;
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
@ -402,7 +401,7 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
public static MemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
{
int size = 0;
@ -414,7 +413,7 @@ namespace Ryujinx.Graphics.Texture
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
int alignedWidth = BitUtils.AlignUp(width, 2);
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
@ -423,7 +422,7 @@ namespace Ryujinx.Graphics.Texture
Span<byte> rPal = stackalloc byte[8];
Span<byte> gPal = stackalloc byte[8];
Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Span);
Span<uint> rTileAsUint = MemoryMarshal.Cast<byte, uint>(rTile);
Span<uint> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
@ -527,7 +526,7 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
public static MemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
{
int size = 0;
@ -536,8 +535,8 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
Span<byte> outputSpan = output.Memory.Span;
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Span;
int inputOffset = 0;
int outputOffset = 0;
@ -566,7 +565,7 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
public static MemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
int size = 0;
@ -575,8 +574,8 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
Span<byte> outputSpan = output.Memory.Span;
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Span;
int inputOffset = 0;
int outputOffset = 0;

View File

@ -2,7 +2,6 @@ using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Texture.Encoders;
using System;
using System.Buffers;
namespace Ryujinx.Graphics.Texture
{
@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Texture
private const int BlockWidth = 4;
private const int BlockHeight = 4;
public static IMemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers)
public static MemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers)
{
int size = 0;
@ -23,7 +22,7 @@ namespace Ryujinx.Graphics.Texture
size += w * h * 16 * Math.Max(1, depth >> l) * layers;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Memory<byte> outputMemory = output.Memory;
int imageBaseIOffs = 0;

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
@ -51,15 +50,15 @@ namespace Ryujinx.Graphics.Texture
new int[] { -3, -5, -7, -9, 2, 4, 6, 8 },
};
public static IMemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
public static MemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;
@ -113,15 +112,15 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
public static MemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;
@ -170,15 +169,15 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
public static MemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Runtime.Intrinsics;
using static Ryujinx.Graphics.Texture.BlockLinearConstants;
@ -95,7 +94,7 @@ namespace Ryujinx.Graphics.Texture
};
}
public static IMemoryOwner<byte> ConvertBlockLinearToLinear(
public static MemoryOwner<byte> ConvertBlockLinearToLinear(
int width,
int height,
int depth,
@ -121,8 +120,8 @@ namespace Ryujinx.Graphics.Texture
blockHeight,
bytesPerPixel);
IMemoryOwner<byte> outputOwner = ByteMemoryPool.Rent(outSize);
Span<byte> output = outputOwner.Memory.Span;
MemoryOwner<byte> outputOwner = MemoryOwner<byte>.Rent(outSize);
Span<byte> output = outputOwner.Span;
int outOffs = 0;
@ -249,7 +248,7 @@ namespace Ryujinx.Graphics.Texture
return outputOwner;
}
public static IMemoryOwner<byte> ConvertLinearStridedToLinear(
public static MemoryOwner<byte> ConvertLinearStridedToLinear(
int width,
int height,
int blockWidth,
@ -265,8 +264,8 @@ namespace Ryujinx.Graphics.Texture
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
lineSize = Math.Min(lineSize, outStride);
IMemoryOwner<byte> output = ByteMemoryPool.Rent(h * outStride);
Span<byte> outSpan = output.Memory.Span;
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(h * outStride);
Span<byte> outSpan = output.Span;
int outOffs = 0;
int inOffs = 0;

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@ -21,13 +20,14 @@ namespace Ryujinx.Graphics.Texture
return (remainder, outRemainder, length / stride);
}
public unsafe static IMemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
public unsafe static MemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
{
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
Span<byte> outputSpan = output.Span;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
Span<ushort> outputSpanUInt16 = MemoryMarshal.Cast<byte, ushort>(outputSpan);
if (remainder == 0)
{
@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Texture
int sizeTrunc = data.Length & ~7;
start = sizeTrunc;
fixed (byte* inputPtr = data, outputPtr = output.Memory.Span)
fixed (byte* inputPtr = data, outputPtr = outputSpan)
{
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
{
@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Texture
for (int i = start; i < data.Length; i++)
{
outputSpan[i] = data[i];
outputSpanUInt16[i] = data[i];
}
}
else
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Texture
{
for (int x = 0; x < width; x++)
{
outputSpan[outOffset++] = data[offset++];
outputSpanUInt16[outOffset++] = data[offset++];
}
offset += remainder;
@ -72,16 +72,16 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
public static MemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
for (int y = 0; y < height; y++)
{
@ -109,16 +109,16 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
public static MemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
{
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
for (int y = 0; y < height; y++)
{
@ -146,16 +146,16 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
public static MemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
for (int y = 0; y < height; y++)
{
@ -183,16 +183,16 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static IMemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
public static MemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
for (int y = 0; y < height; y++)
{

View File

@ -29,7 +29,14 @@ namespace Ryujinx.Graphics.Vulkan
lock (queueLock)
{
_pool = new CommandBufferPool(_gd.Api, _device, queue, queueLock, _gd.QueueFamilyIndex, isLight: true);
_pool = new CommandBufferPool(
_gd.Api,
_device,
queue,
queueLock,
_gd.QueueFamilyIndex,
_gd.IsQualcommProprietary,
isLight: true);
}
}

View File

@ -1,6 +1,7 @@
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Vulkan
{
@ -8,22 +9,55 @@ namespace Ryujinx.Graphics.Vulkan
{
private const int MaxBarriersPerCall = 16;
private const AccessFlags BaseAccess = AccessFlags.ShaderReadBit | AccessFlags.ShaderWriteBit;
private const AccessFlags BufferAccess = AccessFlags.IndexReadBit | AccessFlags.VertexAttributeReadBit | AccessFlags.UniformReadBit;
private const AccessFlags CommandBufferAccess = AccessFlags.IndirectCommandReadBit;
private readonly VulkanRenderer _gd;
private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
private readonly List<BarrierWithStageFlags<MemoryBarrier>> _memoryBarriers = new();
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier>> _bufferBarriers = new();
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier>> _imageBarriers = new();
private readonly List<BarrierWithStageFlags<MemoryBarrier, int>> _memoryBarriers = new();
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier, int>> _bufferBarriers = new();
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage>> _imageBarriers = new();
private int _queuedBarrierCount;
private enum IncoherentBarrierType
{
None,
Texture,
All,
CommandBuffer
}
private bool _feedbackLoopActive;
private PipelineStageFlags _incoherentBufferWriteStages;
private PipelineStageFlags _incoherentTextureWriteStages;
private PipelineStageFlags _extraStages;
private IncoherentBarrierType _queuedIncoherentBarrier;
private bool _queuedFeedbackLoopBarrier;
public BarrierBatch(VulkanRenderer gd)
{
_gd = gd;
}
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
{
AccessFlags access = BufferAccess;
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
if (gd.TransformFeedbackApi != null)
{
access |= AccessFlags.TransformFeedbackWriteBitExt;
stages |= PipelineStageFlags.TransformFeedbackBitExt;
}
return (access, stages);
}
private readonly record struct StageFlags : IEquatable<StageFlags>
{
public readonly PipelineStageFlags Source;
@ -36,47 +70,150 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private readonly struct BarrierWithStageFlags<T> where T : unmanaged
private readonly struct BarrierWithStageFlags<T, T2> where T : unmanaged
{
public readonly StageFlags Flags;
public readonly T Barrier;
public readonly T2 Resource;
public BarrierWithStageFlags(StageFlags flags, T barrier)
{
Flags = flags;
Barrier = barrier;
Resource = default;
}
public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier)
public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier, T2 resource)
{
Flags = new StageFlags(srcStageFlags, dstStageFlags);
Barrier = barrier;
Resource = resource;
}
}
private void QueueBarrier<T>(List<BarrierWithStageFlags<T>> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
private void QueueBarrier<T, T2>(List<BarrierWithStageFlags<T, T2>> list, T barrier, T2 resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
{
list.Add(new BarrierWithStageFlags<T>(srcStageFlags, dstStageFlags, barrier));
list.Add(new BarrierWithStageFlags<T, T2>(srcStageFlags, dstStageFlags, barrier, resource));
_queuedBarrierCount++;
}
public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags);
QueueBarrier(_memoryBarriers, barrier, default, srcStageFlags, dstStageFlags);
}
public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags);
QueueBarrier(_bufferBarriers, barrier, default, srcStageFlags, dstStageFlags);
}
public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
public void QueueBarrier(ImageMemoryBarrier barrier, TextureStorage resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags);
QueueBarrier(_imageBarriers, barrier, resource, srcStageFlags, dstStageFlags);
}
public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void FlushMemoryBarrier(ShaderCollection program, bool inRenderPass)
{
if (_queuedIncoherentBarrier > IncoherentBarrierType.None)
{
// We should emit a memory barrier if there's a write access in the program (current program, or program since last barrier)
bool hasTextureWrite = _incoherentTextureWriteStages != PipelineStageFlags.None;
bool hasBufferWrite = _incoherentBufferWriteStages != PipelineStageFlags.None;
bool hasBufferBarrier = _queuedIncoherentBarrier > IncoherentBarrierType.Texture;
if (hasTextureWrite || (hasBufferBarrier && hasBufferWrite))
{
AccessFlags access = BaseAccess;
PipelineStageFlags stages = inRenderPass ? PipelineStageFlags.AllGraphicsBit : PipelineStageFlags.AllCommandsBit;
if (hasBufferBarrier && hasBufferWrite)
{
access |= BufferAccess;
if (_gd.TransformFeedbackApi != null)
{
access |= AccessFlags.TransformFeedbackWriteBitExt;
stages |= PipelineStageFlags.TransformFeedbackBitExt;
}
}
if (_queuedIncoherentBarrier == IncoherentBarrierType.CommandBuffer)
{
access |= CommandBufferAccess;
stages |= PipelineStageFlags.DrawIndirectBit;
}
MemoryBarrier barrier = new MemoryBarrier()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = access,
DstAccessMask = access
};
QueueBarrier(barrier, stages, stages);
_incoherentTextureWriteStages = program?.IncoherentTextureWriteStages ?? PipelineStageFlags.None;
if (_queuedIncoherentBarrier > IncoherentBarrierType.Texture)
{
if (program != null)
{
_incoherentBufferWriteStages = program.IncoherentBufferWriteStages | _extraStages;
}
else
{
_incoherentBufferWriteStages = PipelineStageFlags.None;
}
}
_queuedIncoherentBarrier = IncoherentBarrierType.None;
_queuedFeedbackLoopBarrier = false;
}
else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
{
// Feedback loop barrier.
MemoryBarrier barrier = new MemoryBarrier()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = AccessFlags.ShaderWriteBit,
DstAccessMask = AccessFlags.ShaderReadBit
};
QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit);
_queuedFeedbackLoopBarrier = false;
}
_feedbackLoopActive = false;
}
}
public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
{
Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass);
}
public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
{
if (program != null)
{
_incoherentBufferWriteStages |= program.IncoherentBufferWriteStages | _extraStages;
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
}
_feedbackLoopActive |= feedbackLoopActive;
FlushMemoryBarrier(program, inRenderPass);
if (!inRenderPass && rpHolder != null)
{
// Render pass is about to begin. Queue any fences that normally interrupt the pass.
rpHolder.InsertForcedFences(cbs);
}
while (_queuedBarrierCount > 0)
{
int memoryCount = 0;
@ -86,20 +223,20 @@ namespace Ryujinx.Graphics.Vulkan
bool hasBarrier = false;
StageFlags flags = default;
static void AddBarriers<T>(
static void AddBarriers<T, T2>(
Span<T> target,
ref int queuedBarrierCount,
ref bool hasBarrier,
ref StageFlags flags,
ref int count,
List<BarrierWithStageFlags<T>> list) where T : unmanaged
List<BarrierWithStageFlags<T, T2>> list) where T : unmanaged
{
int firstMatch = -1;
int end = list.Count;
for (int i = 0; i < list.Count; i++)
{
BarrierWithStageFlags<T> barrier = list[i];
BarrierWithStageFlags<T, T2> barrier = list[i];
if (!hasBarrier)
{
@ -162,21 +299,60 @@ namespace Ryujinx.Graphics.Vulkan
}
}
if (insideRenderPass)
if (inRenderPass && _imageBarriers.Count > 0)
{
// Image barriers queued in the batch are meant to be globally scoped,
// but inside a render pass they're scoped to just the range of the render pass.
// On MoltenVK, we just break the rules and always use image barrier.
// On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier.
// TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done,
// notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future.
// Generally, we want to avoid this from happening in the future, so flag the texture to immediately
// emit a barrier whenever the current render pass is bound again.
if (!_gd.IsMoltenVk)
bool anyIsNonAttachment = false;
foreach (BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage> barrier in _imageBarriers)
{
// If the binding is an attachment, don't add it as a forced fence.
bool isAttachment = rpHolder.ContainsAttachment(barrier.Resource);
if (!isAttachment)
{
rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest);
anyIsNonAttachment = true;
}
}
if (_gd.IsTBDR)
{
if (!_gd.IsMoltenVk)
{
if (!anyIsNonAttachment)
{
// This case is a feedback loop. To prevent this from causing an absolute performance disaster,
// remove the barriers entirely.
// If this is not here, there will be a lot of single draw render passes.
// TODO: explicit handling for feedback loops, likely outside this class.
_queuedBarrierCount -= _imageBarriers.Count;
_imageBarriers.Clear();
}
else
{
// TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available.
// Metal already has hazard tracking so MVK doesn't need this.
endRenderPass();
inRenderPass = false;
}
}
}
else
{
// Generic pipeline memory barriers will work for desktop GPUs.
// They do require a few more access flags on the subpass dependency, though.
foreach (var barrier in _imageBarriers)
{
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>(
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier, int>(
barrier.Flags,
new MemoryBarrier()
{
@ -190,6 +366,22 @@ namespace Ryujinx.Graphics.Vulkan
}
}
if (inRenderPass && _memoryBarriers.Count > 0)
{
PipelineStageFlags allFlags = PipelineStageFlags.None;
foreach (var barrier in _memoryBarriers)
{
allFlags |= barrier.Flags.Dest;
}
if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit) || !_gd.SupportsRenderPassBarrier(allFlags))
{
endRenderPass();
inRenderPass = false;
}
}
AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
@ -198,14 +390,14 @@ namespace Ryujinx.Graphics.Vulkan
{
PipelineStageFlags srcStageFlags = flags.Source;
if (insideRenderPass)
if (inRenderPass)
{
// Inside a render pass, barrier stages can only be from rasterization.
srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
}
_gd.Api.CmdPipelineBarrier(
cb,
cbs.CommandBuffer,
srcStageFlags,
flags.Dest,
0,
@ -219,6 +411,43 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private void QueueIncoherentBarrier(IncoherentBarrierType type)
{
if (type > _queuedIncoherentBarrier)
{
_queuedIncoherentBarrier = type;
}
_queuedFeedbackLoopBarrier = true;
}
public void QueueTextureBarrier()
{
QueueIncoherentBarrier(IncoherentBarrierType.Texture);
}
public void QueueMemoryBarrier()
{
QueueIncoherentBarrier(IncoherentBarrierType.All);
}
public void QueueCommandBufferBarrier()
{
QueueIncoherentBarrier(IncoherentBarrierType.CommandBuffer);
}
public void EnableTfbBarriers(bool enable)
{
if (enable)
{
_extraStages |= PipelineStageFlags.TransformFeedbackBitExt;
}
else
{
_extraStages &= ~PipelineStageFlags.TransformFeedbackBitExt;
}
}
public void Dispose()
{
_memoryBarrierBatch.Dispose();

View File

@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Vulkan
Range = (uint)size,
};
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
_gd.Api.CreateBufferView(_device, in bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer);
}
@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan
PipelineStageFlags.AllCommandsBit,
DependencyFlags.DeviceGroupBit,
1,
memoryBarrier,
in memoryBarrier,
0,
null,
0,
@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
null,
1,
memoryBarrier,
in memoryBarrier,
0,
null);
}

View File

@ -103,12 +103,19 @@ namespace Ryujinx.Graphics.Vulkan
usage |= BufferUsageFlags.IndirectBufferBit;
}
var externalMemoryBuffer = new ExternalMemoryBufferCreateInfo
{
SType = StructureType.ExternalMemoryBufferCreateInfo,
HandleTypes = ExternalMemoryHandleTypeFlags.HostAllocationBitExt,
};
var bufferCreateInfo = new BufferCreateInfo
{
SType = StructureType.BufferCreateInfo,
Size = (ulong)size,
Usage = usage,
SharingMode = SharingMode.Exclusive,
PNext = &externalMemoryBuffer,
};
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
@ -214,7 +221,7 @@ namespace Ryujinx.Graphics.Vulkan
PBufferBinds = &bufferBind
};
gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError();
gd.Api.QueueBindSparse(gd.Queue, 1, in bindSparseInfo, default).ThrowOnError();
}
var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations);

View File

@ -25,7 +25,10 @@ namespace Ryujinx.Graphics.Vulkan
{
var buffer = _buffer.Get(cbs, _offset, _size, true).Value;
gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size);
ulong offset = (ulong)_offset;
ulong size = (ulong)_size;
gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, in buffer, in offset, in size);
}
}

View File

@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device;
private readonly Queue _queue;
private readonly object _queueLock;
private readonly bool _concurrentFenceWaitUnsupported;
private readonly CommandPool _pool;
private readonly Thread _owner;
@ -30,11 +31,9 @@ namespace Ryujinx.Graphics.Vulkan
public int SubmissionCount;
public CommandBuffer CommandBuffer;
public FenceHolder Fence;
public SemaphoreHolder Semaphore;
public List<IAuto> Dependants;
public List<MultiFenceHolder> Waitables;
public HashSet<SemaphoreHolder> Dependencies;
public void Initialize(Vk api, Device device, CommandPool pool)
{
@ -46,11 +45,10 @@ namespace Ryujinx.Graphics.Vulkan
Level = CommandBufferLevel.Primary,
};
api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);
api.AllocateCommandBuffers(device, in allocateInfo, out CommandBuffer);
Dependants = new List<IAuto>();
Waitables = new List<MultiFenceHolder>();
Dependencies = new HashSet<SemaphoreHolder>();
}
}
@ -61,12 +59,20 @@ namespace Ryujinx.Graphics.Vulkan
private int _queuedCount;
private int _inUseCount;
public unsafe CommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex, bool isLight = false)
public unsafe CommandBufferPool(
Vk api,
Device device,
Queue queue,
object queueLock,
uint queueFamilyIndex,
bool concurrentFenceWaitUnsupported,
bool isLight = false)
{
_api = api;
_device = device;
_queue = queue;
_queueLock = queueLock;
_concurrentFenceWaitUnsupported = concurrentFenceWaitUnsupported;
_owner = Thread.CurrentThread;
var commandPoolCreateInfo = new CommandPoolCreateInfo
@ -77,7 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
CommandPoolCreateFlags.ResetCommandBufferBit,
};
api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError();
api.CreateCommandPool(device, in commandPoolCreateInfo, null, out _pool).ThrowOnError();
// We need at least 2 command buffers to get texture data in some cases.
_totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
@ -134,14 +140,6 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs)
{
Debug.Assert(_commandBuffers[cbIndex].InUse);
var semaphoreHolder = _commandBuffers[dependencyCbs.CommandBufferIndex].Semaphore;
semaphoreHolder.Get();
_commandBuffers[cbIndex].Dependencies.Add(semaphoreHolder);
}
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
{
ref var entry = ref _commandBuffers[cbIndex];
@ -255,7 +253,7 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.CommandBufferBeginInfo,
};
_api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo).ThrowOnError();
_api.BeginCommandBuffer(entry.CommandBuffer, in commandBufferBeginInfo).ThrowOnError();
return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
}
@ -313,7 +311,7 @@ namespace Ryujinx.Graphics.Vulkan
lock (_queueLock)
{
_api.QueueSubmit(_queue, 1, sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
_api.QueueSubmit(_queue, 1, in sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
}
}
}
@ -345,19 +343,13 @@ namespace Ryujinx.Graphics.Vulkan
waitable.RemoveBufferUses(cbIndex);
}
foreach (var dependency in entry.Dependencies)
{
dependency.Put();
}
entry.Dependants.Clear();
entry.Waitables.Clear();
entry.Dependencies.Clear();
entry.Fence?.Dispose();
if (refreshFence)
{
entry.Fence = new FenceHolder(_api, _device);
entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported);
}
else
{

View File

@ -26,11 +26,6 @@ namespace Ryujinx.Graphics.Vulkan
_pool.AddWaitable(CommandBufferIndex, waitable);
}
public void AddDependency(CommandBufferScoped dependencyCbs)
{
_pool.AddDependency(CommandBufferIndex, dependencyCbs);
}
public FenceHolder GetFence()
{
return _pool.GetFence(CommandBufferIndex);

View File

@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan
PBufferInfo = &bufferInfo,
};
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
PBufferInfo = pBufferInfo,
};
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Vulkan
PImageInfo = &imageInfo,
};
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
PImageInfo = pImageInfo,
};
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Vulkan
PImageInfo = pImageInfo,
};
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
i += count - 1;
}
@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
PTexelBufferView = &texelBufferView,
};
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Vulkan
PTexelBufferView = pTexelBufferView + i,
};
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
i += count;

View File

@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan
PPoolSizes = pPoolsSize,
};
Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
Api.CreateDescriptorPool(device, in descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
}
}

View File

@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
private record struct TextureRef
{
public ShaderStage Stage;
public TextureStorage Storage;
public Auto<DisposableImageView> View;
public TextureView View;
public Auto<DisposableImageView> ImageView;
public Auto<DisposableSampler> Sampler;
public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler)
{
Stage = stage;
Storage = storage;
View = view;
ImageView = imageView;
Sampler = sampler;
}
}
@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan
private record struct ImageRef
{
public ShaderStage Stage;
public TextureStorage Storage;
public Auto<DisposableImageView> View;
public TextureView View;
public Auto<DisposableImageView> ImageView;
public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView)
{
Stage = stage;
Storage = storage;
View = view;
ImageView = imageView;
}
}
@ -73,7 +74,6 @@ namespace Ryujinx.Graphics.Vulkan
private readonly VulkanRenderer _gd;
private readonly Device _device;
private readonly PipelineBase _pipeline;
private ShaderCollection _program;
private readonly BufferRef[] _uniformBufferRefs;
@ -125,11 +125,12 @@ namespace Ryujinx.Graphics.Vulkan
private readonly TextureView _dummyTexture;
private readonly SamplerHolder _dummySampler;
public DescriptorSetUpdater(VulkanRenderer gd, Device device, PipelineBase pipeline)
public List<TextureView> FeedbackLoopHazards { get; private set; }
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
{
_gd = gd;
_device = device;
_pipeline = pipeline;
// Some of the bindings counts needs to be multiplied by 2 because we have buffer and
// regular textures/images interleaved on the same descriptor set.
@ -211,10 +212,15 @@ namespace Ryujinx.Graphics.Vulkan
_templateUpdater = new();
}
public void Initialize()
public void Initialize(bool isMainPipeline)
{
IMemoryOwner<byte> dummyTextureData = ByteMemoryPool.RentCleared(4);
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
_dummyTexture.SetData(dummyTextureData);
if (isMainPipeline)
{
FeedbackLoopHazards = new();
}
}
private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
@ -277,6 +283,18 @@ namespace Ryujinx.Graphics.Vulkan
public void InsertBindingBarriers(CommandBufferScoped cbs)
{
if ((FeedbackLoopHazards?.Count ?? 0) > 0)
{
// Clear existing hazards - they will be rebuilt.
foreach (TextureView hazard in FeedbackLoopHazards)
{
hazard.DecrementHazardUses();
}
FeedbackLoopHazards.Clear();
}
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
{
if (segment.Type == ResourceType.TextureAndSampler)
@ -286,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < segment.Count; i++)
{
ref var texture = ref _textureRefs[segment.Binding + i];
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
}
}
else
@ -307,7 +325,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < segment.Count; i++)
{
ref var image = ref _imageRefs[segment.Binding + i];
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
}
}
else
@ -387,9 +405,12 @@ namespace Ryujinx.Graphics.Vulkan
}
else if (image is TextureView view)
{
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
ref ImageRef iRef = ref _imageRefs[binding];
_imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
iRef.View?.ClearUsage(FeedbackLoopHazards);
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
iRef = new(stage, view, view.GetView(imageFormat).GetIdentityImageView());
}
else
{
@ -488,9 +509,12 @@ namespace Ryujinx.Graphics.Vulkan
}
else if (texture is TextureView view)
{
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
ref TextureRef iRef = ref _textureRefs[binding];
_textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
iRef.View?.ClearUsage(FeedbackLoopHazards);
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
}
else
{
@ -512,7 +536,7 @@ namespace Ryujinx.Graphics.Vulkan
{
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
_textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
_textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
SignalDirty(DirtyFlags.Texture);
}
@ -684,7 +708,14 @@ namespace Ryujinx.Graphics.Vulkan
if (_dirty.HasFlag(DirtyFlags.Texture))
{
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
if (program.UpdateTexturesWithoutTemplate)
{
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
}
else
{
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
}
}
if (_dirty.HasFlag(DirtyFlags.Image))
@ -831,7 +862,7 @@ namespace Ryujinx.Graphics.Vulkan
ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
@ -881,7 +912,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++)
{
images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
}
tu.Push<DescriptorImageInfo>(images[..count]);
@ -918,31 +949,84 @@ namespace Ryujinx.Graphics.Vulkan
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
}
private unsafe void UpdateBuffers(
CommandBufferScoped cbs,
PipelineBindPoint pbp,
int baseBinding,
ReadOnlySpan<DescriptorBufferInfo> bufferInfo,
DescriptorType type)
private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
{
if (bufferInfo.Length == 0)
int setIndex = PipelineBase.TextureSetIndex;
var bindingSegments = program.BindingSegments[setIndex];
if (bindingSegments.Length == 0)
{
return;
}
fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
if (_updateDescriptorCacheCbIndex)
{
var writeDescriptorSet = new WriteDescriptorSet
{
SType = StructureType.WriteDescriptorSet,
DstBinding = (uint)baseBinding,
DescriptorType = type,
DescriptorCount = (uint)bufferInfo.Length,
PBufferInfo = pBufferInfo,
};
_gd.PushDescriptorApi.CmdPushDescriptorSet(cbs.CommandBuffer, pbp, _program.PipelineLayout, 0, 1, &writeDescriptorSet);
_updateDescriptorCacheCbIndex = false;
program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
}
var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs);
foreach (ResourceBindingSegment segment in bindingSegments)
{
int binding = segment.Binding;
int count = segment.Count;
if (!segment.IsArray)
{
if (segment.Type != ResourceType.BufferTexture)
{
Span<DescriptorImageInfo> textures = _textures;
for (int i = 0; i < count; i++)
{
ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
{
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
}
if (texture.Sampler.Handle == 0)
{
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
}
}
dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler);
}
else
{
Span<BufferView> bufferTextures = _bufferTextures;
for (int i = 0; i < count; i++)
{
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
}
dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer);
}
}
else
{
if (segment.Type != ResourceType.BufferTexture)
{
dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler);
}
else
{
dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer);
}
}
}
var sets = dsc.GetSets();
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -59,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var scalingResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var sharpeningResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View File

@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var resourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View File

@ -81,20 +81,20 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var edgeResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var blendResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var neighbourResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View File

@ -0,0 +1,12 @@
using System;
namespace Ryujinx.Graphics.Vulkan
{
[Flags]
internal enum FeedbackLoopAspects
{
None = 0,
Color = 1 << 0,
Depth = 1 << 1,
}
}

View File

@ -10,12 +10,15 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device;
private Fence _fence;
private int _referenceCount;
private int _lock;
private readonly bool _concurrentWaitUnsupported;
private bool _disposed;
public unsafe FenceHolder(Vk api, Device device)
public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported)
{
_api = api;
_device = device;
_concurrentWaitUnsupported = concurrentWaitUnsupported;
var fenceCreateInfo = new FenceCreateInfo
{
@ -47,6 +50,11 @@ namespace Ryujinx.Graphics.Vulkan
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
if (_concurrentWaitUnsupported)
{
AcquireLock();
}
fence = _fence;
return true;
}
@ -57,6 +65,16 @@ namespace Ryujinx.Graphics.Vulkan
return _fence;
}
public void PutLock()
{
Put();
if (_concurrentWaitUnsupported)
{
ReleaseLock();
}
}
public void Put()
{
if (Interlocked.Decrement(ref _referenceCount) == 0)
@ -66,24 +84,67 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private void AcquireLock()
{
while (!TryAcquireLock())
{
Thread.SpinWait(32);
}
}
private bool TryAcquireLock()
{
return Interlocked.Exchange(ref _lock, 1) == 0;
}
private void ReleaseLock()
{
Interlocked.Exchange(ref _lock, 0);
}
public void Wait()
{
Span<Fence> fences = stackalloc Fence[]
if (_concurrentWaitUnsupported)
{
_fence,
};
AcquireLock();
FenceHelper.WaitAllIndefinitely(_api, _device, fences);
try
{
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
}
finally
{
ReleaseLock();
}
}
else
{
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
}
}
public bool IsSignaled()
{
Span<Fence> fences = stackalloc Fence[]
if (_concurrentWaitUnsupported)
{
_fence,
};
if (!TryAcquireLock())
{
return false;
}
return FenceHelper.AllSignaled(_api, _device, fences);
try
{
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
}
finally
{
ReleaseLock();
}
}
else
{
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
}
}
public void Dispose()

View File

@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Vulkan
Layers = Layers,
};
api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
}
@ -286,10 +286,44 @@ namespace Ryujinx.Graphics.Vulkan
_depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
gd.Barriers.Flush(cbs.CommandBuffer, false, null);
gd.Barriers.Flush(cbs, false, null, null);
}
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
public void AddStoreOpUsage()
{
if (_colors != null)
{
foreach (var color in _colors)
{
color.Storage?.AddStoreOpUsage(false);
}
}
_depthStencil?.Storage?.AddStoreOpUsage(true);
}
public void ClearBindings()
{
_depthStencil?.Storage.ClearBindings();
for (int i = 0; i < _colorsCanonical.Length; i++)
{
_colorsCanonical[i]?.Storage.ClearBindings();
}
}
public void AddBindings()
{
_depthStencil?.Storage.AddBinding(_depthStencil);
for (int i = 0; i < _colorsCanonical.Length; i++)
{
TextureView color = _colorsCanonical[i];
color?.Storage.AddBinding(color);
}
}
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
CommandBufferScoped cbs)

View File

@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsViewportArray2;
public readonly bool SupportsHostImportedMemory;
public readonly bool SupportsDepthClipControl;
public readonly bool SupportsAttachmentFeedbackLoop;
public readonly bool SupportsDynamicAttachmentFeedbackLoop;
public readonly uint SubgroupSize;
public readonly SampleCountFlags SupportedSampleCounts;
public readonly PortabilitySubsetFlags PortabilitySubset;
@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsViewportArray2,
bool supportsHostImportedMemory,
bool supportsDepthClipControl,
bool supportsAttachmentFeedbackLoop,
bool supportsDynamicAttachmentFeedbackLoop,
uint subgroupSize,
SampleCountFlags supportedSampleCounts,
PortabilitySubsetFlags portabilitySubset,
@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
SupportsViewportArray2 = supportsViewportArray2;
SupportsHostImportedMemory = supportsHostImportedMemory;
SupportsDepthClipControl = supportsDepthClipControl;
SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
SubgroupSize = subgroupSize;
SupportedSampleCounts = supportedSampleCounts;
PortabilitySubset = portabilitySubset;

View File

@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
var strideChangeResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
{
@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
var colorCopyResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
{
@ -155,7 +155,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
{
@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
{
@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]

View File

@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = &importInfo,
};
Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory);
Result result = _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory);
if (result < Result.Success)
{

View File

@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_cachedCommandBufferIndex = -1;
_storages = null;
SetDirty(_gd);
SetDirty(_gd, isImage: true);
}
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)

View File

@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
MemoryTypeIndex = (uint)MemoryTypeIndex,
};
_api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
_api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
IntPtr hostPointer = IntPtr.Zero;

View File

@ -1,3 +1,4 @@
using Silk.NET.Core.Loader;
using Silk.NET.Vulkan;
using System;
using System.Runtime.InteropServices;
@ -8,6 +9,8 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
[SupportedOSPlatform("macos")]
public static partial class MVKInitialization
{
private const string VulkanLib = "libvulkan.dylib";
[LibraryImport("libMoltenVK.dylib")]
private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize);
@ -29,5 +32,20 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize);
}
private static string[] Resolver(string path)
{
if (path.EndsWith(VulkanLib))
{
path = path[..^VulkanLib.Length] + "libMoltenVK.dylib";
return [path];
}
return Array.Empty<string>();
}
public static void InitializeResolver()
{
((DefaultPathResolver)PathResolver.Default).Resolvers.Insert(0, Resolver);
}
}
}

View File

@ -196,18 +196,23 @@ namespace Ryujinx.Graphics.Vulkan
bool signaled = true;
if (hasTimeout)
try
{
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
if (hasTimeout)
{
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
}
else
{
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
}
}
else
finally
{
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
}
for (int i = 0; i < fenceCount; i++)
{
fenceHolders[i].Put();
for (int i = 0; i < fenceCount; i++)
{
fenceHolders[i].PutLock();
}
}
return signaled;

View File

@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly Action EndRenderPassDelegate;
protected PipelineDynamicState DynamicState;
protected bool IsMainPipeline;
private PipelineState _newState;
private bool _graphicsStateDirty;
private bool _computeStateDirty;
@ -55,6 +57,7 @@ namespace Ryujinx.Graphics.Vulkan
protected FramebufferParams FramebufferParams;
private Auto<DisposableFramebuffer> _framebuffer;
private RenderPassHolder _rpHolder;
private Auto<DisposableRenderPass> _renderPass;
private RenderPassHolder _nullRenderPass;
private int _writtenAttachmentCount;
@ -84,9 +87,10 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfEnabled;
private bool _tfActive;
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
private FeedbackLoopAspects _feedbackLoop;
private bool _passWritesDepthStencil;
private ulong _drawCountSinceBarrier;
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
public ulong DrawCount { get; private set; }
public bool RenderPassActive { get; private set; }
@ -103,9 +107,9 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PipelineCacheCreateInfo,
};
gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
gd.Api.CreatePipelineCache(device, in pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device, this);
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device);
_vertexBufferUpdater = new VertexBufferUpdater(gd);
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
@ -127,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
public void Initialize()
{
_descriptorSetUpdater.Initialize();
_descriptorSetUpdater.Initialize(IsMainPipeline);
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
@ -135,48 +139,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void Barrier()
{
if (_drawCountSinceBarrier != DrawCount)
{
_drawCountSinceBarrier = DrawCount;
// Barriers are not supported inside a render pass on Apple GPUs.
// As a workaround, end the render pass.
if (Gd.Vendor == Vendor.Apple)
{
EndRenderPass();
}
}
MemoryBarrier memoryBarrier = new()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
};
PipelineStageFlags pipelineStageFlags = PipelineStageFlags.VertexShaderBit | PipelineStageFlags.FragmentShaderBit;
if (Gd.Capabilities.SupportsGeometryShader)
{
pipelineStageFlags |= PipelineStageFlags.GeometryShaderBit;
}
if (Gd.Capabilities.SupportsTessellationShader)
{
pipelineStageFlags |= PipelineStageFlags.TessellationControlShaderBit | PipelineStageFlags.TessellationEvaluationShaderBit;
}
Gd.Api.CmdPipelineBarrier(
CommandBuffer,
pipelineStageFlags,
pipelineStageFlags,
0,
1,
memoryBarrier,
0,
null,
0,
null);
Gd.Barriers.QueueMemoryBarrier();
}
public void ComputeBarrier()
@ -203,6 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
public void BeginTransformFeedback(PrimitiveTopology topology)
{
Gd.Barriers.EnableTfbBarriers(true);
_tfEnabled = true;
}
@ -249,7 +213,7 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
BeginRenderPass();
@ -287,7 +251,7 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
BeginRenderPass();
@ -299,24 +263,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void CommandBufferBarrier()
{
MemoryBarrier memoryBarrier = new()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = BufferHolder.DefaultAccessFlags,
DstAccessMask = AccessFlags.IndirectCommandReadBit,
};
Gd.Api.CmdPipelineBarrier(
CommandBuffer,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.DrawIndirectBit,
0,
1,
memoryBarrier,
0,
null,
0,
null);
Gd.Barriers.QueueCommandBufferBarrier();
}
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
@ -722,6 +669,7 @@ namespace Ryujinx.Graphics.Vulkan
public void EndTransformFeedback()
{
Gd.Barriers.EnableTfbBarriers(false);
PauseTransformFeedbackInternal();
_tfEnabled = false;
}
@ -871,6 +819,8 @@ namespace Ryujinx.Graphics.Vulkan
_newState.DepthTestEnable = depthTest.TestEnable;
_newState.DepthWriteEnable = depthTest.WriteEnable;
_newState.DepthCompareOp = depthTest.Func.Convert();
UpdatePassDepthStencil();
SignalStateChange();
}
@ -1020,6 +970,13 @@ namespace Ryujinx.Graphics.Vulkan
{
_newState.RasterizerDiscardEnable = discard;
SignalStateChange();
if (!discard && Gd.IsQualcommProprietary)
{
// On Adreno, enabling rasterizer discard somehow corrupts the viewport state.
// Force it to be updated on next use to work around this bug.
DynamicState.ForceAllDirty();
}
}
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
@ -1129,6 +1086,8 @@ namespace Ryujinx.Graphics.Vulkan
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
UpdatePassDepthStencil();
SignalStateChange();
}
@ -1401,24 +1360,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void TextureBarrier()
{
MemoryBarrier memoryBarrier = new()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
};
Gd.Api.CmdPipelineBarrier(
CommandBuffer,
PipelineStageFlags.FragmentShaderBit,
PipelineStageFlags.FragmentShaderBit,
0,
1,
memoryBarrier,
0,
null,
0,
null);
Gd.Barriers.QueueTextureBarrier();
}
public void TextureBarrierTiled()
@ -1493,7 +1435,23 @@ namespace Ryujinx.Graphics.Vulkan
}
}
if (IsMainPipeline)
{
FramebufferParams?.ClearBindings();
}
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
if (IsMainPipeline)
{
FramebufferParams.AddBindings();
_newState.FeedbackLoopAspects = FeedbackLoopAspects.None;
_bindingBarriersDirty = true;
}
_passWritesDepthStencil = false;
UpdatePassDepthStencil();
UpdatePipelineAttachmentFormats();
}
@ -1525,12 +1483,15 @@ namespace Ryujinx.Graphics.Vulkan
// Use the null framebuffer.
_nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
_rpHolder = _nullRenderPass;
_renderPass = _nullRenderPass.GetRenderPass();
_framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
}
else
{
(_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
(_rpHolder, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
_renderPass = _rpHolder.GetRenderPass();
}
}
@ -1557,11 +1518,82 @@ namespace Ryujinx.Graphics.Vulkan
}
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
}
private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects)
{
if (_feedbackLoop != aspects)
{
if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{
DynamicState.SetFeedbackLoop(aspects);
}
else
{
_newState.FeedbackLoopAspects = aspects;
}
_feedbackLoop = aspects;
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool UpdateFeedbackLoop()
{
List<TextureView> hazards = _descriptorSetUpdater.FeedbackLoopHazards;
if ((hazards?.Count ?? 0) > 0)
{
FeedbackLoopAspects aspects = 0;
foreach (TextureView view in hazards)
{
// May need to enforce feedback loop layout here in the future.
// Though technically, it should always work with the general layout.
if (view.Info.Format.IsDepthOrStencil())
{
if (_passWritesDepthStencil)
{
// If depth/stencil isn't written in the pass, it doesn't count as a feedback loop.
aspects |= FeedbackLoopAspects.Depth;
}
}
else
{
aspects |= FeedbackLoopAspects.Color;
}
}
return ChangeFeedbackLoop(aspects);
}
else if (_feedbackLoop != 0)
{
return ChangeFeedbackLoop(FeedbackLoopAspects.None);
}
return false;
}
private void UpdatePassDepthStencil()
{
if (!RenderPassActive)
{
_passWritesDepthStencil = false;
}
// Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
_passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
}
private bool RecreateGraphicsPipelineIfNeeded()
{
if (AutoFlush.ShouldFlushDraw(DrawCount))
@ -1569,7 +1601,7 @@ namespace Ryujinx.Graphics.Vulkan
Gd.FlushAllCommands();
}
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
DynamicState.ReplayIfDirty(Gd, CommandBuffer);
if (_needsIndexBufferRebind && _indexBufferPattern == null)
{
@ -1603,7 +1635,15 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBufferUpdater.Commit(Cbs);
}
if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
if (_bindingBarriersDirty)
{
// Stale barriers may have been activated by switching program. Emit any that are relevant.
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
_bindingBarriersDirty = false;
}
if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
{
if (!CreatePipeline(PipelineBindPoint.Graphics))
{
@ -1612,17 +1652,9 @@ namespace Ryujinx.Graphics.Vulkan
_graphicsStateDirty = false;
Pbp = PipelineBindPoint.Graphics;
if (_bindingBarriersDirty)
{
// Stale barriers may have been activated by switching program. Emit any that are relevant.
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
_bindingBarriersDirty = false;
}
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
@ -1692,7 +1724,7 @@ namespace Ryujinx.Graphics.Vulkan
ClearValueCount = 1,
};
Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
Gd.Api.CmdBeginRenderPass(CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
RenderPassActive = true;
}
}
@ -1701,6 +1733,8 @@ namespace Ryujinx.Graphics.Vulkan
{
if (RenderPassActive)
{
FramebufferParams.AddStoreOpUsage();
PauseTransformFeedbackInternal();
Gd.Api.CmdEndRenderPass(CommandBuffer);
SignalRenderPassEnd();

View File

@ -9,13 +9,6 @@ namespace Ryujinx.Graphics.Vulkan
{
static class PipelineConverter
{
private const AccessFlags SubpassAccessMask =
AccessFlags.MemoryReadBit |
AccessFlags.MemoryWriteBit |
AccessFlags.ShaderReadBit |
AccessFlags.ColorAttachmentWriteBit |
AccessFlags.DepthStencilAttachmentWriteBit;
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
{
const int MaxAttachments = Constants.MaxRenderTargets + 1;
@ -108,7 +101,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
var subpassDependency = CreateSubpassDependency();
var subpassDependency = CreateSubpassDependency(gd);
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{
@ -123,35 +116,39 @@ namespace Ryujinx.Graphics.Vulkan
DependencyCount = 1,
};
gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
return new DisposableRenderPass(gd.Api, device, renderPass);
}
}
public static SubpassDependency CreateSubpassDependency()
public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd)
{
var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
return new SubpassDependency(
0,
0,
PipelineStageFlags.AllGraphicsBit,
PipelineStageFlags.AllGraphicsBit,
SubpassAccessMask,
SubpassAccessMask,
stages,
stages,
access,
access,
0);
}
public unsafe static SubpassDependency2 CreateSubpassDependency2()
public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd)
{
var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
return new SubpassDependency2(
StructureType.SubpassDependency2,
null,
0,
0,
PipelineStageFlags.AllGraphicsBit,
PipelineStageFlags.AllGraphicsBit,
SubpassAccessMask,
SubpassAccessMask,
stages,
stages,
access,
access,
0);
}

View File

@ -1,5 +1,6 @@
using Ryujinx.Common.Memory;
using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT;
namespace Ryujinx.Graphics.Vulkan
{
@ -21,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
private Array4<float> _blendConstants;
private FeedbackLoopAspects _feedbackLoopAspects;
public uint ViewportsCount;
public Array16<Viewport> Viewports;
@ -32,7 +35,8 @@ namespace Ryujinx.Graphics.Vulkan
Scissor = 1 << 2,
Stencil = 1 << 3,
Viewport = 1 << 4,
All = Blend | DepthBias | Scissor | Stencil | Viewport,
FeedbackLoop = 1 << 5,
All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
}
private DirtyFlags _dirty;
@ -99,13 +103,22 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void SetFeedbackLoop(FeedbackLoopAspects aspects)
{
_feedbackLoopAspects = aspects;
_dirty |= DirtyFlags.FeedbackLoop;
}
public void ForceAllDirty()
{
_dirty = DirtyFlags.All;
}
public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer)
public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
{
Vk api = gd.Api;
if (_dirty.HasFlag(DirtyFlags.Blend))
{
RecordBlend(api, commandBuffer);
@ -131,6 +144,11 @@ namespace Ryujinx.Graphics.Vulkan
RecordViewport(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{
RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
}
_dirty = DirtyFlags.None;
}
@ -169,5 +187,17 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
}
private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)
{
ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0;
if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0)
{
aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
}
api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects);
}
}
}

View File

@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan
_activeBufferMirrors = new();
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
IsMainPipeline = true;
}
private void CopyPendingQuery()
@ -47,10 +49,11 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
if (componentMask != 0xf)
if (componentMask != 0xf || Gd.IsQualcommProprietary)
{
// We can't use CmdClearAttachments if not writing all components,
// because on Vulkan, the pipeline state does not affect clears.
// On proprietary Adreno drivers, CmdClearAttachments appears to execute out of order, so it's better to not use it at all.
var dstTexture = FramebufferParams.GetColorView(index);
if (dstTexture == null)
{
@ -87,10 +90,11 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
if (stencilMask != 0 && stencilMask != 0xff)
if ((stencilMask != 0 && stencilMask != 0xff) || Gd.IsQualcommProprietary)
{
// We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits,
// because on Vulkan, the pipeline state does not affect clears.
// On proprietary Adreno drivers, CmdClearAttachments appears to execute out of order, so it's better to not use it at all.
var dstTexture = FramebufferParams.GetDepthStencilView();
if (dstTexture == null)
{
@ -233,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan
if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
{
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
DynamicState.ReplayIfDirty(Gd, CommandBuffer);
}
}
@ -255,7 +259,7 @@ namespace Ryujinx.Graphics.Vulkan
PreloadCbs = null;
}
Gd.Barriers.Flush(Cbs.CommandBuffer, false, null);
Gd.Barriers.Flush(Cbs, false, null, null);
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
Gd.RegisterFlush();

View File

@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
Flags = flags,
};
gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
gd.Api.CreateDescriptorSetLayout(device, in descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
}
}

View File

@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
struct PipelineState : IDisposable
{
private const int RequiredSubgroupSize = 32;
private const int MaxDynamicStatesCount = 9;
public PipelineUid Internal;
@ -299,6 +300,12 @@ namespace Ryujinx.Graphics.Vulkan
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
}
public FeedbackLoopAspects FeedbackLoopAspects
{
readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3);
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
}
public bool HasTessellationControlShader;
public NativeArray<PipelineShaderStageCreateInfo> Stages;
public PipelineLayout PipelineLayout;
@ -439,7 +446,7 @@ namespace Ryujinx.Graphics.Vulkan
{
SType = StructureType.PipelineInputAssemblyStateCreateInfo,
PrimitiveRestartEnable = primitiveRestartEnable,
Topology = Topology,
Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology,
};
var tessellationState = new PipelineTessellationStateCreateInfo
@ -564,9 +571,11 @@ namespace Ryujinx.Graphics.Vulkan
}
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
int dynamicStatesCount = supportsExtDynamicState ? 8 : 7;
bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
int dynamicStatesCount = 7;
dynamicStates[0] = DynamicState.Viewport;
dynamicStates[1] = DynamicState.Scissor;
@ -578,7 +587,12 @@ namespace Ryujinx.Graphics.Vulkan
if (supportsExtDynamicState)
{
dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
}
if (supportsFeedbackLoopDynamicState)
{
dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
}
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
@ -588,9 +602,27 @@ namespace Ryujinx.Graphics.Vulkan
PDynamicStates = dynamicStates,
};
PipelineCreateFlags flags = 0;
if (gd.Capabilities.SupportsAttachmentFeedbackLoop)
{
FeedbackLoopAspects aspects = FeedbackLoopAspects;
if ((aspects & FeedbackLoopAspects.Color) != 0)
{
flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
}
if ((aspects & FeedbackLoopAspects.Depth) != 0)
{
flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
}
}
var pipelineCreateInfo = new GraphicsPipelineCreateInfo
{
SType = StructureType.GraphicsPipelineCreateInfo,
Flags = flags,
StageCount = StagesCount,
PStages = Stages.Pointer,
PVertexInputState = &vertexInputState,

View File

@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
PipelineStatistics = flags,
};
gd.Api.CreateQueryPool(device, queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
gd.Api.CreateQueryPool(device, in queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
}
var buffer = gd.BufferManager.Create(gd, sizeof(long), forConditionalRendering: true);

View File

@ -1,5 +1,7 @@
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan
{
@ -29,10 +31,13 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private readonly record struct ForcedFence(TextureStorage Texture, PipelineStageFlags StageFlags);
private readonly TextureView[] _textures;
private readonly Auto<DisposableRenderPass> _renderPass;
private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
private readonly RenderPassCacheKey _key;
private readonly List<ForcedFence> _forcedFences;
public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb)
{
@ -105,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
var subpassDependency = PipelineConverter.CreateSubpassDependency();
var subpassDependency = PipelineConverter.CreateSubpassDependency(gd);
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{
@ -120,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
DependencyCount = 1,
};
gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
_renderPass = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
}
@ -138,6 +143,8 @@ namespace Ryujinx.Graphics.Vulkan
_textures = textures;
_key = key;
_forcedFences = new List<ForcedFence>();
}
public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
@ -159,6 +166,37 @@ namespace Ryujinx.Graphics.Vulkan
return _renderPass;
}
public void AddForcedFence(TextureStorage storage, PipelineStageFlags stageFlags)
{
if (!_forcedFences.Any(fence => fence.Texture == storage))
{
_forcedFences.Add(new ForcedFence(storage, stageFlags));
}
}
public void InsertForcedFences(CommandBufferScoped cbs)
{
if (_forcedFences.Count > 0)
{
_forcedFences.RemoveAll((entry) =>
{
if (entry.Texture.Disposed)
{
return true;
}
entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
return false;
});
}
}
public bool ContainsAttachment(TextureStorage storage)
{
return _textures.Any(view => view.Storage == storage);
}
public void Dispose()
{
// Dispose all framebuffers.

View File

@ -14,13 +14,20 @@ namespace Ryujinx.Graphics.Vulkan
private int _bindCount;
protected void SetDirty(VulkanRenderer gd)
protected void SetDirty(VulkanRenderer gd, bool isImage)
{
ReleaseDescriptorSet();
if (_bindCount != 0)
{
gd.PipelineInternal.ForceTextureDirty();
if (isImage)
{
gd.PipelineInternal.ForceImageDirty();
}
else
{
gd.PipelineInternal.ForceTextureDirty();
}
}
}

View File

@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false)
{
int setIndex = type switch
{
@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
};
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages));
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write));
return this;
}

View File

@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt;
}
gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError();
gd.Api.CreateSampler(device, in samplerCreateInfo, null, out var sampler).ThrowOnError();
_sampler = new Auto<DisposableSampler>(new DisposableSampler(gd.Api, device, sampler));
}

View File

@ -1,60 +0,0 @@
using Silk.NET.Vulkan;
using System;
using System.Threading;
using VkSemaphore = Silk.NET.Vulkan.Semaphore;
namespace Ryujinx.Graphics.Vulkan
{
class SemaphoreHolder : IDisposable
{
private readonly Vk _api;
private readonly Device _device;
private VkSemaphore _semaphore;
private int _referenceCount;
private bool _disposed;
public unsafe SemaphoreHolder(Vk api, Device device)
{
_api = api;
_device = device;
var semaphoreCreateInfo = new SemaphoreCreateInfo
{
SType = StructureType.SemaphoreCreateInfo,
};
api.CreateSemaphore(device, in semaphoreCreateInfo, null, out _semaphore).ThrowOnError();
_referenceCount = 1;
}
public VkSemaphore GetUnsafe()
{
return _semaphore;
}
public VkSemaphore Get()
{
Interlocked.Increment(ref _referenceCount);
return _semaphore;
}
public unsafe void Put()
{
if (Interlocked.Decrement(ref _referenceCount) == 0)
{
_api.DestroySemaphore(_device, _semaphore, null);
_semaphore = default;
}
}
public void Dispose()
{
if (!_disposed)
{
Put();
_disposed = true;
}
}
}
}

View File

@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
PCode = (uint*)pCode,
};
api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError();
api.CreateShaderModule(device, in shaderModuleCreateInfo, null, out _module).ThrowOnError();
}
CompileStatus = ProgramLinkStatus.Success;

View File

@ -23,8 +23,13 @@ namespace Ryujinx.Graphics.Vulkan
public bool IsCompute { get; }
public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0;
public bool UpdateTexturesWithoutTemplate { get; }
public uint Stages { get; }
public PipelineStageFlags IncoherentBufferWriteStages { get; }
public PipelineStageFlags IncoherentTextureWriteStages { get; }
public ResourceBindingSegment[][] ClearSegments { get; }
public ResourceBindingSegment[][] BindingSegments { get; }
public DescriptorSetTemplate[] Templates { get; }
@ -127,8 +132,12 @@ namespace Ryujinx.Graphics.Vulkan
Stages = stages;
ClearSegments = BuildClearSegments(sets);
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
Templates = BuildTemplates(usePushDescriptors);
(IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
_compileTask = Task.CompletedTask;
_firstBackgroundUse = false;
@ -280,8 +289,10 @@ namespace Ryujinx.Graphics.Vulkan
return segments;
}
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages, out bool usesBufferTextures)
{
usesBufferTextures = false;
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
@ -295,6 +306,11 @@ namespace Ryujinx.Graphics.Vulkan
{
ResourceUsage usage = setUsages[setIndex].Usages[index];
if (usage.Type == ResourceType.BufferTexture)
{
usesBufferTextures = true;
}
if (currentUsage.Binding + currentCount != usage.Binding ||
currentUsage.Type != usage.Type ||
currentUsage.Stages != usage.Stages ||
@ -365,6 +381,73 @@ namespace Ryujinx.Graphics.Vulkan
return templates;
}
private PipelineStageFlags GetPipelineStages(ResourceStages stages)
{
PipelineStageFlags result = 0;
if ((stages & ResourceStages.Compute) != 0)
{
result |= PipelineStageFlags.ComputeShaderBit;
}
if ((stages & ResourceStages.Vertex) != 0)
{
result |= PipelineStageFlags.VertexShaderBit;
}
if ((stages & ResourceStages.Fragment) != 0)
{
result |= PipelineStageFlags.FragmentShaderBit;
}
if ((stages & ResourceStages.Geometry) != 0)
{
result |= PipelineStageFlags.GeometryShaderBit;
}
if ((stages & ResourceStages.TessellationControl) != 0)
{
result |= PipelineStageFlags.TessellationControlShaderBit;
}
if ((stages & ResourceStages.TessellationEvaluation) != 0)
{
result |= PipelineStageFlags.TessellationEvaluationShaderBit;
}
return result;
}
private (PipelineStageFlags Buffer, PipelineStageFlags Texture) BuildIncoherentStages(ReadOnlyCollection<ResourceUsageCollection> setUsages)
{
PipelineStageFlags buffer = PipelineStageFlags.None;
PipelineStageFlags texture = PipelineStageFlags.None;
foreach (var set in setUsages)
{
foreach (var range in set.Usages)
{
if (range.Write)
{
PipelineStageFlags stages = GetPipelineStages(range.Stages);
switch (range.Type)
{
case ResourceType.Image:
texture |= stages;
break;
case ResourceType.StorageBuffer:
case ResourceType.BufferImage:
buffer |= stages;
break;
}
}
}
}
return (buffer, texture);
}
private async Task BackgroundCompilation()
{
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));

View File

@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_cachedCommandBufferIndex = -1;
_storages = null;
SetDirty(_gd);
SetDirty(_gd, isImage: false);
}
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)

View File

@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan
DstOffsets = dstOffsets,
};
api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region, filter);
api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region, filter);
copySrcLevel++;
copyDstLevel++;
@ -320,13 +320,13 @@ namespace Ryujinx.Graphics.Vulkan
{
var region = new ImageResolve(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
}
else
{
var region = new ImageCopy(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
}
width = Math.Max(1, width >> 1);
@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Vulkan
ImageLayout.General,
ImageLayout.General);
var subpassDependency = PipelineConverter.CreateSubpassDependency2();
var subpassDependency = PipelineConverter.CreateSubpassDependency2(gd);
fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs)
{
@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan
DependencyCount = 1,
};
gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
gd.Api.CreateRenderPass2(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
using var rp = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Vulkan
Layers = (uint)src.Layers,
};
gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
gd.Api.CreateFramebuffer(device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
using var fb = new Auto<DisposableFramebuffer>(new DisposableFramebuffer(gd.Api, device, framebuffer), null, srcView, dstView);
var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height));
@ -465,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
// to resolve the depth-stencil texture.
// TODO: Do speculative resolve and part of the same render pass as the draw to avoid
// ending the current render pass?
gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
gd.Api.CmdEndRenderPass(cbs.CommandBuffer);
}
}

View File

@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Format = Ryujinx.Graphics.GAL.Format;
using VkBuffer = Silk.NET.Vulkan.Buffer;
using VkFormat = Silk.NET.Vulkan.Format;
@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan
{
class TextureStorage : IDisposable
{
private struct TextureSliceInfo
{
public int BindCount;
}
private const MemoryPropertyFlags DefaultImageMemoryFlags =
MemoryPropertyFlags.DeviceLocalBit;
@ -38,9 +44,12 @@ namespace Ryujinx.Graphics.Vulkan
public TextureCreateInfo Info => _info;
public bool Disposed { get; private set; }
private readonly Image _image;
private readonly Auto<DisposableImage> _imageAuto;
private readonly Auto<MemoryAllocation> _allocationAuto;
private readonly int _depthOrLayers;
private Auto<MemoryAllocation> _foreignAllocationAuto;
private Dictionary<Format, TextureStorage> _aliasedStorages;
@ -53,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan
private int _viewsCount;
private readonly ulong _size;
private int _bindCount;
private readonly TextureSliceInfo[] _slices;
public VkFormat VkFormat { get; }
public unsafe TextureStorage(
@ -71,6 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
VkFormat = format;
_depthOrLayers = info.GetDepthOrLayers();
var type = info.Target.Convert();
@ -78,9 +91,9 @@ namespace Ryujinx.Graphics.Vulkan
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
var flags = ImageCreateFlags.CreateMutableFormatBit;
var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
// This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube.
bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray;
@ -112,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
Flags = flags,
};
gd.Api.CreateImage(device, imageCreateInfo, null, out _image).ThrowOnError();
gd.Api.CreateImage(device, in imageCreateInfo, null, out _image).ThrowOnError();
if (foreignAllocation == null)
{
@ -146,6 +159,8 @@ namespace Ryujinx.Graphics.Vulkan
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
}
_slices = new TextureSliceInfo[levels * _depthOrLayers];
}
public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
@ -282,7 +297,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
null,
1,
barrier);
in barrier);
if (useTempCbs)
{
@ -290,7 +305,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage)
public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
{
var usage = DefaultUsageFlags;
@ -303,11 +318,19 @@ namespace Ryujinx.Graphics.Vulkan
usage |= ImageUsageFlags.ColorAttachmentBit;
}
bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample;
if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
{
usage |= ImageUsageFlags.StorageBit;
}
if (capabilities.SupportsAttachmentFeedbackLoop &&
(usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0)
{
usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt;
}
return usage;
}
@ -399,11 +422,11 @@ namespace Ryujinx.Graphics.Vulkan
if (to)
{
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
}
else
{
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
}
offset += mipSize;
@ -433,6 +456,17 @@ namespace Ryujinx.Graphics.Vulkan
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
}
public void AddStoreOpUsage(bool depthStencil)
{
_lastModificationStage = depthStencil ?
PipelineStageFlags.LateFragmentTestsBit :
PipelineStageFlags.ColorAttachmentOutputBit;
_lastModificationAccess = depthStencil ?
AccessFlags.DepthStencilAttachmentWriteBit :
AccessFlags.ColorAttachmentWriteBit;
}
public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{
PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
@ -458,7 +492,7 @@ namespace Ryujinx.Graphics.Vulkan
_info.GetLayers(),
_info.Levels);
_gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags);
_gd.Barriers.QueueBarrier(barrier, this, srcStageFlags, dstStageFlags);
_lastReadStage = PipelineStageFlags.None;
_lastReadAccess = AccessFlags.None;
@ -491,12 +525,61 @@ namespace Ryujinx.Graphics.Vulkan
_info.GetLayers(),
_info.Levels);
_gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
_gd.Barriers.QueueBarrier(barrier, this, _lastModificationStage, dstStageFlags);
_lastModificationAccess = AccessFlags.None;
}
}
public void AddBinding(TextureView view)
{
// Assumes a view only has a first level.
int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
int layers = view.Layers;
for (int i = 0; i < layers; i++)
{
ref TextureSliceInfo info = ref _slices[index++];
info.BindCount++;
}
_bindCount++;
}
public void ClearBindings()
{
if (_bindCount != 0)
{
Array.Clear(_slices, 0, _slices.Length);
_bindCount = 0;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsBound(TextureView view)
{
if (_bindCount != 0)
{
int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
int layers = view.Layers;
for (int i = 0; i < layers; i++)
{
ref TextureSliceInfo info = ref _slices[index++];
if (info.BindCount != 0)
{
return true;
}
}
}
return false;
}
public void IncrementViewsCount()
{
_viewsCount++;
@ -514,6 +597,8 @@ namespace Ryujinx.Graphics.Vulkan
public void Dispose()
{
Disposed = true;
if (_aliasedStorages != null)
{
foreach (var storage in _aliasedStorages.Values)

View File

@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Auto<DisposableImageView> _imageView2dArray;
private Dictionary<Format, TextureView> _selfManagedViews;
private int _hazardUses;
private readonly TextureCreateInfo _info;
private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
@ -60,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.Textures.Add(this);
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
var levels = (uint)info.Levels;
var layers = (uint)info.GetLayers();
@ -100,7 +102,7 @@ namespace Ryujinx.Graphics.Vulkan
unsafe Auto<DisposableImageView> CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags)
{
var usage = new ImageViewUsageCreateInfo
var imageViewUsage = new ImageViewUsageCreateInfo
{
SType = StructureType.ImageViewUsageCreateInfo,
Usage = usageFlags,
@ -114,16 +116,16 @@ namespace Ryujinx.Graphics.Vulkan
Format = format,
Components = cm,
SubresourceRange = sr,
PNext = &usage,
PNext = &imageViewUsage,
};
gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
gd.Api.CreateImageView(device, in imageCreateInfo, null, out var imageView).ThrowOnError();
return new Auto<DisposableImageView>(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage());
}
ImageUsageFlags shaderUsage = ImageUsageFlags.SampledBit;
if (info.Format.IsImageCompatible())
if (info.Format.IsImageCompatible() && (_gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample()))
{
shaderUsage |= ImageUsageFlags.StorageBit;
}
@ -154,7 +156,7 @@ namespace Ryujinx.Graphics.Vulkan
}
else
{
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth);
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, 1, (uint)firstLayer, (uint)info.Depth);
_imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray, usage);
}
@ -492,7 +494,7 @@ namespace Ryujinx.Graphics.Vulkan
dstStageMask,
DependencyFlags.None,
1,
memoryBarrier,
in memoryBarrier,
0,
null,
0,
@ -557,7 +559,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
null,
1,
memoryBarrier);
in memoryBarrier);
}
public TextureView GetView(Format format)
@ -667,8 +669,36 @@ namespace Ryujinx.Graphics.Vulkan
if (PrepareOutputBuffer(cbs, hostSize, buffer, out VkBuffer copyToBuffer, out BufferHolder tempCopyHolder))
{
// No barrier necessary, as this is a temporary copy buffer.
offset = 0;
}
else
{
BufferHolder.InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
copyToBuffer,
BufferHolder.DefaultAccessFlags,
AccessFlags.TransferWriteBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.TransferBit,
offset,
outSize);
}
InsertImageBarrier(
_gd.Api,
cbs.CommandBuffer,
image,
TextureStorage.DefaultAccessMask,
AccessFlags.TransferReadBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.TransferBit,
Info.Format.ConvertAspectFlags(),
FirstLayer + layer,
FirstLevel + level,
1,
1);
CopyFromOrToBuffer(cbs.CommandBuffer, copyToBuffer, image, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride);
@ -677,6 +707,19 @@ namespace Ryujinx.Graphics.Vulkan
CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset);
tempCopyHolder.Dispose();
}
else
{
BufferHolder.InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
copyToBuffer,
AccessFlags.TransferWriteBit,
BufferHolder.DefaultAccessFlags,
PipelineStageFlags.TransferBit,
PipelineStageFlags.AllCommandsBit,
offset,
outSize);
}
}
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)
@ -908,11 +951,11 @@ namespace Ryujinx.Graphics.Vulkan
if (to)
{
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
}
else
{
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
}
offset += mipSize;
@ -969,11 +1012,11 @@ namespace Ryujinx.Graphics.Vulkan
if (to)
{
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
_gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
}
else
{
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
_gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
}
}
@ -993,7 +1036,35 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotImplementedException();
}
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List<TextureView> feedbackLoopHazards)
{
Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
if (feedbackLoopHazards != null && Storage.IsBound(this))
{
feedbackLoopHazards.Add(this);
_hazardUses++;
}
}
public void ClearUsage(List<TextureView> feedbackLoopHazards)
{
if (_hazardUses != 0 && feedbackLoopHazards != null)
{
feedbackLoopHazards.Remove(this);
_hazardUses--;
}
}
public void DecrementHazardUses()
{
if (_hazardUses != 0)
{
_hazardUses--;
}
}
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
CommandBufferScoped cbs,
@ -1006,7 +1077,7 @@ namespace Ryujinx.Graphics.Vulkan
rpHolder = new RenderPassHolder(gd, device, key, fb);
}
return (rpHolder.GetRenderPass(), rpHolder.GetFramebuffer(gd, cbs, fb));
return (rpHolder, rpHolder.GetFramebuffer(gd, cbs, fb));
}
public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass)

View File

@ -69,27 +69,30 @@ namespace Ryujinx.Graphics.Vulkan
{
DriverId.AmdProprietary => "AMD",
DriverId.AmdOpenSource => "AMD (Open)",
DriverId.ArmProprietary => "ARM",
DriverId.BroadcomProprietary => "Broadcom",
DriverId.CoreaviProprietary => "CoreAVI",
DriverId.GgpProprietary => "GGP",
DriverId.GoogleSwiftshader => "SwiftShader",
DriverId.ImaginationProprietary => "Imagination",
DriverId.IntelOpenSourceMesa => "Intel (Open)",
DriverId.IntelProprietaryWindows => "Intel",
DriverId.JuiceProprietary => "Juice",
DriverId.MesaDozen => "Dozen",
DriverId.MesaLlvmpipe => "LLVMpipe",
DriverId.MesaPanvk => "PanVK",
DriverId.MesaRadv => "RADV",
DriverId.NvidiaProprietary => "NVIDIA",
DriverId.IntelProprietaryWindows => "Intel",
DriverId.IntelOpenSourceMesa => "Intel (Open)",
DriverId.ImaginationProprietary => "Imagination",
DriverId.QualcommProprietary => "Qualcomm",
DriverId.ArmProprietary => "ARM",
DriverId.GoogleSwiftshader => "SwiftShader",
DriverId.GgpProprietary => "GGP",
DriverId.BroadcomProprietary => "Broadcom",
DriverId.MesaLlvmpipe => "LLVMpipe",
DriverId.Moltenvk => "MoltenVK",
DriverId.CoreaviProprietary => "CoreAVI",
DriverId.JuiceProprietary => "Juice",
DriverId.VerisiliconProprietary => "Verisilicon",
DriverId.MesaTurnip => "Turnip",
DriverId.MesaV3DV => "V3DV",
DriverId.MesaVenus => "Venus",
DriverId.Moltenvk => "MoltenVK",
DriverId.NvidiaProprietary => "NVIDIA",
DriverId.QualcommProprietary => "Qualcomm",
DriverId.MesaPanvk => "PanVK",
DriverId.SamsungProprietary => "Samsung",
DriverId.VerisiliconProprietary => "Verisilicon",
DriverId.MesaVenus => "Venus",
DriverId.MesaDozen => "Dozen",
DriverId.MesaNvk => "NVK",
DriverId.ImaginationOpenSourceMesa => "Imagination (Open)",
DriverId.MesaAgxv => "Honeykrisp",
_ => id.ToString(),
};
}

View File

@ -42,6 +42,10 @@ namespace Ryujinx.Graphics.Vulkan
"VK_EXT_depth_clip_control",
"VK_KHR_portability_subset", // As per spec, we should enable this if present.
"VK_EXT_4444_formats",
"VK_KHR_8bit_storage",
"VK_KHR_maintenance2",
"VK_EXT_attachment_feedback_loop_layout",
"VK_EXT_attachment_feedback_loop_dynamic_state",
};
private static readonly string[] _requiredExtensions = {
@ -355,6 +359,36 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &supportedFeaturesDepthClipControl;
}
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
PNext = features2.PNext,
};
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"))
{
features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout;
}
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
PNext = features2.PNext,
};
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"))
{
features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout;
}
PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
{
SType = StructureType.PhysicalDeviceVulkan12Features,
PNext = features2.PNext,
};
features2.PNext = &supportedPhysicalDeviceVulkan12Features;
api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
var supportedFeatures = features2.Features;
@ -382,6 +416,7 @@ namespace Ryujinx.Graphics.Vulkan
TessellationShader = supportedFeatures.TessellationShader,
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
RobustBufferAccess = useRobustBufferAccess,
SampleRateShading = supportedFeatures.SampleRateShading,
};
void* pExtendedFeatures = null;
@ -451,9 +486,11 @@ namespace Ryujinx.Graphics.Vulkan
{
SType = StructureType.PhysicalDeviceVulkan12Features,
PNext = pExtendedFeatures,
DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"),
DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout"),
DescriptorIndexing = supportedPhysicalDeviceVulkan12Features.DescriptorIndexing,
DrawIndirectCount = supportedPhysicalDeviceVulkan12Features.DrawIndirectCount,
UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout,
UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess,
StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess,
};
pExtendedFeatures = &featuresVk12;
@ -518,6 +555,36 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresDepthClipControl;
}
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout;
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") &&
supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout)
{
featuresAttachmentFeedbackLoopLayout = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
PNext = pExtendedFeatures,
AttachmentFeedbackLoopLayout = true,
};
pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout;
}
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout;
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") &&
supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState)
{
featuresDynamicAttachmentFeedbackLoopLayout = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
PNext = pExtendedFeatures,
AttachmentFeedbackLoopDynamicState = true,
};
pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout;
}
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];

View File

@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; }
internal uint QueueFamilyIndex { get; private set; }
internal Queue Queue { get; private set; }
@ -87,9 +88,11 @@ namespace Ryujinx.Graphics.Vulkan
internal bool IsAmdGcn { get; private set; }
internal bool IsNvidiaPreTuring { get; private set; }
internal bool IsIntelArc { get; private set; }
internal bool IsQualcommProprietary { get; private set; }
internal bool IsMoltenVk { get; private set; }
internal bool IsTBDR { get; private set; }
internal bool IsSharedMemory { get; private set; }
public string GpuVendor { get; private set; }
public string GpuDriver { get; private set; }
public string GpuRenderer { get; private set; }
@ -147,6 +150,11 @@ namespace Ryujinx.Graphics.Vulkan
DrawIndirectCountApi = drawIndirectCountApi;
}
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi))
{
DynamicFeedbackLoopApi = dynamicFeedbackLoopApi;
}
if (maxQueueCount >= 2)
{
Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
@ -241,6 +249,16 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
};
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
};
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new()
{
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
};
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
{
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
@ -277,6 +295,22 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresDepthClipControl;
}
bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout");
if (supportsAttachmentFeedbackLoop)
{
featuresAttachmentFeedbackLoop.PNext = features2.PNext;
features2.PNext = &featuresAttachmentFeedbackLoop;
}
bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state");
if (supportsDynamicAttachmentFeedbackLoop)
{
featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext;
features2.PNext = &featuresDynamicAttachmentFeedbackLoop;
}
bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
if (usePortability)
@ -344,7 +378,7 @@ namespace Ryujinx.Graphics.Vulkan
{
IsNvidiaPreTuring = gpuNumber < 2000;
}
else if (GpuDriver.Contains("TITAN") && !GpuDriver.Contains("RTX"))
else if (GpuRenderer.Contains("TITAN") && !GpuRenderer.Contains("RTX"))
{
IsNvidiaPreTuring = true;
}
@ -354,6 +388,8 @@ namespace Ryujinx.Graphics.Vulkan
IsIntelArc = GpuRenderer.StartsWith("Intel(R) Arc(TM)");
}
IsQualcommProprietary = hasDriverProperties && driverProperties.DriverID == DriverId.QualcommProprietary;
ulong minResourceAlignment = Math.Max(
Math.Max(
properties.Limits.MinStorageBufferOffsetAlignment,
@ -397,6 +433,8 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
propertiesSubgroup.SubgroupSize,
supportedSampleCounts,
portabilityFlags,
@ -411,7 +449,7 @@ namespace Ryujinx.Graphics.Vulkan
Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, IsQualcommProprietary);
PipelineLayoutCache = new PipelineLayoutCache();
@ -688,7 +726,7 @@ namespace Ryujinx.Graphics.Vulkan
GpuVendor,
memoryType: memoryType,
hasFrontFacingBug: IsIntelWindows,
hasVectorIndexingBug: Vendor == Vendor.Qualcomm,
hasVectorIndexingBug: IsQualcommProprietary,
needsFragmentOutputSpecialization: IsMoltenVk,
reduceShaderPrecision: IsMoltenVk,
supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats,
@ -935,6 +973,11 @@ namespace Ryujinx.Graphics.Vulkan
ScreenCaptured?.Invoke(this, bitmap);
}
public bool SupportsRenderPassBarrier(PipelineStageFlags flags)
{
return !(IsMoltenVk || IsQualcommProprietary);
}
public unsafe void Dispose()
{
if (!_initialized)

View File

@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Vulkan
SwizzleComponent.Blue,
SwizzleComponent.Alpha);
_gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
_gd.SwapchainApi.CreateSwapchain(_device, in swapchainCreateInfo, null, out _swapchain).ThrowOnError();
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
@ -187,14 +187,14 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
{
_gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
_gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
}
_renderFinishedSemaphores = new Semaphore[imageCount];
for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
{
_gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
_gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
}
}
@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
SubresourceRange = subresourceRange,
};
_gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
_gd.Api.CreateImageView(_device, in imageCreateInfo, null, out var imageView).ThrowOnError();
return new TextureView(_gd, _device, new DisposableImageView(_gd.Api, _device, imageView), info, format);
}
@ -479,7 +479,7 @@ namespace Ryujinx.Graphics.Vulkan
lock (_gd.QueueLock)
{
_gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
_gd.SwapchainApi.QueuePresent(_gd.Queue, in presentInfo);
}
}
@ -611,7 +611,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
null,
1,
barrier);
in barrier);
}
private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
@ -623,7 +623,8 @@ namespace Ryujinx.Graphics.Vulkan
public override void SetSize(int width, int height)
{
// Not needed as we can get the size from the surface.
// We don't need to use width and height as we can get the size from the surface.
_swapchainIsDirty = true;
}
public override void ChangeVSyncMode(bool vsyncEnabled)

View File

@ -4,15 +4,17 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Modules;
using Ryujinx.SDL2.Common;
using Ryujinx.UI;
using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Common.SystemInfo;
using Ryujinx.UI.Widgets;
using SixLabors.ImageSharp.Formats.Jpeg;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -40,9 +42,6 @@ namespace Ryujinx
[LibraryImport("user32.dll", SetLastError = true)]
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
[LibraryImport("libc", SetLastError = true)]
private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
private const uint MbIconWarning = 0x30;
static Program()
@ -104,12 +103,13 @@ namespace Ryujinx
throw new NotSupportedException("Failed to initialize multi-threading support.");
}
Environment.SetEnvironmentVariable("GDK_BACKEND", "x11");
setenv("GDK_BACKEND", "x11", 1);
OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11");
}
if (OperatingSystem.IsMacOS())
{
MVKInitialization.InitializeResolver();
string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
string resourcesDataDir;
@ -122,19 +122,13 @@ namespace Ryujinx
resourcesDataDir = baseDirectory;
}
static void SetEnvironmentVariableNoCaching(string key, string value)
{
int res = setenv(key, value, 1);
Debug.Assert(res != -1);
}
// On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories.
SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
// On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories.
SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
}
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
@ -161,12 +155,6 @@ namespace Ryujinx
});
};
// Sets ImageSharp Jpeg Encoder Quality.
SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()
{
Quality = 100,
});
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
@ -236,9 +224,9 @@ namespace Ryujinx
// Logging system information.
PrintSystemInfo();
// Enable OGL multithreading on the driver, when available.
// Enable OGL multithreading on the driver, and some other flags.
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off);
DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off);
// Initialize Gtk.
Application.Init();
@ -255,6 +243,12 @@ namespace Ryujinx
MainWindow mainWindow = new();
mainWindow.Show();
// Load the game table if no application was requested by the command line
if (CommandLineState.LaunchPathArg == null)
{
mainWindow.UpdateGameTable();
}
if (OperatingSystem.IsLinux())
{
int currentVmMaxMapCount = LinuxHelper.VmMaxMapCount;
@ -322,7 +316,35 @@ namespace Ryujinx
if (CommandLineState.LaunchPathArg != null)
{
mainWindow.RunApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
if (mainWindow.ApplicationLibrary.TryGetApplicationsFromFile(CommandLineState.LaunchPathArg, out List<ApplicationData> applications))
{
ApplicationData applicationData;
if (CommandLineState.LaunchApplicationId != null)
{
applicationData = applications.Find(application => application.IdString == CommandLineState.LaunchApplicationId);
if (applicationData != null)
{
mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg);
}
else
{
Logger.Error?.Print(LogClass.Application, $"Couldn't find requested application id '{CommandLineState.LaunchApplicationId}' in '{CommandLineState.LaunchPathArg}'.");
UserErrorDialog.CreateUserErrorDialog(UserError.ApplicationNotFound);
}
}
else
{
applicationData = applications[0];
mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg);
}
}
else
{
Logger.Error?.Print(LogClass.Application, $"Couldn't find any application in '{CommandLineState.LaunchPathArg}'.");
UserErrorDialog.CreateUserErrorDialog(UserError.ApplicationNotFound);
}
}
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))

View File

@ -30,7 +30,6 @@
<PackageReference Include="OpenTK.Graphics" />
<PackageReference Include="SPB" />
<PackageReference Include="SharpZipLib" />
<PackageReference Include="SixLabors.ImageSharp" />
</ItemGroup>
<ItemGroup>

View File

@ -37,7 +37,9 @@ using Ryujinx.UI.Windows;
using Silk.NET.Vulkan;
using SPB.Graphics.Vulkan;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Threading;
@ -60,7 +62,6 @@ namespace Ryujinx.UI
private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
private readonly ApplicationLibrary _applicationLibrary;
private readonly GtkHostUIHandler _uiHandler;
private readonly AutoResetEvent _deviceExitStatus;
private readonly ListStore _tableStore;
@ -69,11 +70,12 @@ namespace Ryujinx.UI
private bool _gameLoaded;
private bool _ending;
private string _currentEmulatedGamePath = null;
private ApplicationData _currentApplicationData = null;
private string _lastScannedAmiiboId = "";
private bool _lastScannedAmiiboShowAll = false;
public readonly ApplicationLibrary ApplicationLibrary;
public RendererWidgetBase RendererWidget;
public InputManager InputManager;
@ -180,8 +182,15 @@ namespace Ryujinx.UI
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile);
_userChannelPersistence = new UserChannelPersistence();
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
// Instantiate GUI objects.
_applicationLibrary = new ApplicationLibrary(_virtualFileSystem);
ApplicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel)
{
DesiredLanguage = ConfigurationState.Instance.System.Language,
};
_uiHandler = new GtkHostUIHandler(this);
_deviceExitStatus = new AutoResetEvent(false);
@ -190,8 +199,8 @@ namespace Ryujinx.UI
FocusInEvent += MainWindow_FocusInEvent;
FocusOutEvent += MainWindow_FocusOutEvent;
_applicationLibrary.ApplicationAdded += Application_Added;
_applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated;
ApplicationLibrary.ApplicationAdded += Application_Added;
ApplicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated;
_fileMenu.StateChanged += FileMenu_StateChanged;
_actionMenu.StateChanged += ActionMenu_StateChanged;
@ -319,7 +328,6 @@ namespace Ryujinx.UI
_hideUI.Label = _hideUI.Label.Replace("SHOWUIKEY", ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI.ToString());
UpdateColumns();
UpdateGameTable();
ConfigurationState.Instance.UI.GameDirs.Event += (sender, args) =>
{
@ -732,7 +740,8 @@ namespace Ryujinx.UI
Thread applicationLibraryThread = new(() =>
{
_applicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language);
ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language;
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs);
_updatingGameTable = false;
})
@ -783,7 +792,7 @@ namespace Ryujinx.UI
}
}
private bool LoadApplication(string path, bool isFirmwareTitle)
private bool LoadApplication(string path, ulong applicationId, bool isFirmwareTitle)
{
SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion();
@ -857,7 +866,7 @@ namespace Ryujinx.UI
case ".xci":
Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
return _emulationContext.LoadXci(path);
return _emulationContext.LoadXci(path, applicationId);
case ".nca":
Logger.Info?.Print(LogClass.Application, "Loading as NCA.");
@ -866,7 +875,7 @@ namespace Ryujinx.UI
case ".pfs0":
Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
return _emulationContext.LoadNsp(path);
return _emulationContext.LoadNsp(path, applicationId);
default:
Logger.Info?.Print(LogClass.Application, "Loading as Homebrew.");
try
@ -887,7 +896,7 @@ namespace Ryujinx.UI
return false;
}
public void RunApplication(string path, bool startFullscreen = false)
public void RunApplication(ApplicationData application, bool startFullscreen = false)
{
if (_gameLoaded)
{
@ -909,14 +918,14 @@ namespace Ryujinx.UI
bool isFirmwareTitle = false;
if (path.StartsWith("@SystemContent"))
if (application.Path.StartsWith("@SystemContent"))
{
path = VirtualFileSystem.SwitchPathToSystemPath(path);
application.Path = VirtualFileSystem.SwitchPathToSystemPath(application.Path);
isFirmwareTitle = true;
}
if (!LoadApplication(path, isFirmwareTitle))
if (!LoadApplication(application.Path, application.Id, isFirmwareTitle))
{
_emulationContext.Dispose();
SwitchToGameTable();
@ -926,7 +935,7 @@ namespace Ryujinx.UI
SetupProgressUIHandlers();
_currentEmulatedGamePath = path;
_currentApplicationData = application;
_deviceExitStatus.Reset();
@ -1165,7 +1174,7 @@ namespace Ryujinx.UI
_tableStore.AppendValues(
args.AppData.Favorite,
new Gdk.Pixbuf(args.AppData.Icon, 75, 75),
$"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}",
$"{args.AppData.Name}\n{args.AppData.IdString.ToUpper()}",
args.AppData.Developer,
args.AppData.Version,
args.AppData.TimePlayedString,
@ -1253,9 +1262,22 @@ namespace Ryujinx.UI
{
_gameTableSelection.GetSelected(out TreeIter treeIter);
string path = (string)_tableStore.GetValue(treeIter, 9);
ApplicationData application = new()
{
Favorite = (bool)_tableStore.GetValue(treeIter, 0),
Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0],
Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber),
Developer = (string)_tableStore.GetValue(treeIter, 3),
Version = (string)_tableStore.GetValue(treeIter, 4),
TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)),
LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)),
FileExtension = (string)_tableStore.GetValue(treeIter, 7),
FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)),
Path = (string)_tableStore.GetValue(treeIter, 9),
ControlHolder = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10),
};
RunApplication(path);
RunApplication(application);
}
private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args)
@ -1313,13 +1335,22 @@ namespace Ryujinx.UI
return;
}
string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString();
string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0];
string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
ApplicationData application = new()
{
Favorite = (bool)_tableStore.GetValue(treeIter, 0),
Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0],
Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber),
Developer = (string)_tableStore.GetValue(treeIter, 3),
Version = (string)_tableStore.GetValue(treeIter, 4),
TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)),
LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)),
FileExtension = (string)_tableStore.GetValue(treeIter, 7),
FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)),
Path = (string)_tableStore.GetValue(treeIter, 9),
ControlHolder = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10),
};
BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10);
_ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, titleFilePath, titleName, titleId, controlData);
_ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, application);
}
private void Load_Application_File(object sender, EventArgs args)
@ -1341,7 +1372,15 @@ namespace Ryujinx.UI
if (fileChooser.Run() == (int)ResponseType.Accept)
{
RunApplication(fileChooser.Filename);
if (ApplicationLibrary.TryGetApplicationsFromFile(fileChooser.Filename,
out List<ApplicationData> applications))
{
RunApplication(applications[0]);
}
else
{
GtkDialog.CreateErrorDialog("No applications found in selected file.");
}
}
}
@ -1351,7 +1390,13 @@ namespace Ryujinx.UI
if (fileChooser.Run() == (int)ResponseType.Accept)
{
RunApplication(fileChooser.Filename);
ApplicationData applicationData = new()
{
Name = System.IO.Path.GetFileNameWithoutExtension(fileChooser.Filename),
Path = fileChooser.Filename,
};
RunApplication(applicationData);
}
}
@ -1366,7 +1411,14 @@ namespace Ryujinx.UI
{
string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
RunApplication(contentPath);
ApplicationData applicationData = new()
{
Name = "miiEdit",
Id = 0x0100000000001009ul,
Path = contentPath,
};
RunApplication(applicationData);
}
private void Open_Ryu_Folder(object sender, EventArgs args)
@ -1646,13 +1698,13 @@ namespace Ryujinx.UI
{
_userChannelPersistence.ShouldRestart = false;
RunApplication(_currentEmulatedGamePath);
RunApplication(_currentApplicationData);
}
else
{
// otherwise, clear state.
_userChannelPersistence = new UserChannelPersistence();
_currentEmulatedGamePath = null;
_currentApplicationData = null;
_actionMenu.Sensitive = false;
_firmwareInstallFile.Sensitive = true;
_firmwareInstallDirectory.Sensitive = true;
@ -1714,7 +1766,7 @@ namespace Ryujinx.UI
_emulationContext.Processes.ActiveApplication.ProgramId,
_emulationContext.Processes.ActiveApplication.ApplicationControlProperties
.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(),
_currentEmulatedGamePath);
_currentApplicationData.Path);
window.Destroyed += CheatWindow_Destroyed;
window.Show();

View File

@ -13,16 +13,13 @@ using Ryujinx.Input.HLE;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Widgets;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SkiaSharp;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Image = SixLabors.ImageSharp.Image;
using Key = Ryujinx.Input.Key;
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
using Switch = Ryujinx.HLE.Switch;
@ -404,23 +401,31 @@ namespace Ryujinx.UI
return;
}
Image image = e.IsBgra ? Image.LoadPixelData<Bgra32>(e.Data, e.Width, e.Height)
: Image.LoadPixelData<Rgba32>(e.Data, e.Width, e.Height);
var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
using var image = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
if (e.FlipX)
Marshal.Copy(e.Data, 0, image.GetPixels(), e.Data.Length);
using var surface = SKSurface.Create(image.Info);
var canvas = surface.Canvas;
if (e.FlipX || e.FlipY)
{
image.Mutate(x => x.Flip(FlipMode.Horizontal));
canvas.Clear(SKColors.Transparent);
float scaleX = e.FlipX ? -1 : 1;
float scaleY = e.FlipY ? -1 : 1;
var matrix = SKMatrix.CreateScale(scaleX, scaleY, image.Width / 2f, image.Height / 2f);
canvas.SetMatrix(matrix);
}
canvas.DrawBitmap(image, new SKPoint());
if (e.FlipY)
{
image.Mutate(x => x.Flip(FlipMode.Vertical));
}
image.SaveAsPng(path, new PngEncoder()
{
ColorType = PngColorType.Rgb,
});
surface.Flush();
using var snapshot = surface.Snapshot();
using var encoded = snapshot.Encode(SKEncodedImageFormat.Png, 80);
using var file = File.OpenWrite(path);
encoded.SaveTo(file);
image.Dispose();

View File

@ -16,6 +16,8 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.HLE.Utilities;
using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
@ -23,7 +25,6 @@ using Ryujinx.UI.Windows;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Threading;
@ -36,17 +37,13 @@ namespace Ryujinx.UI.Widgets
private readonly VirtualFileSystem _virtualFileSystem;
private readonly AccountManager _accountManager;
private readonly HorizonClient _horizonClient;
private readonly BlitStruct<ApplicationControlProperty> _controlData;
private readonly string _titleFilePath;
private readonly string _titleName;
private readonly string _titleIdText;
private readonly ulong _titleId;
private readonly ApplicationData _applicationData;
private MessageDialog _dialog;
private bool _cancel;
public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData)
public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, ApplicationData applicationData)
{
_parent = parent;
@ -55,23 +52,22 @@ namespace Ryujinx.UI.Widgets
_virtualFileSystem = virtualFileSystem;
_accountManager = accountManager;
_horizonClient = horizonClient;
_titleFilePath = titleFilePath;
_titleName = titleName;
_titleIdText = titleId;
_controlData = controlData;
_applicationData = applicationData;
if (!ulong.TryParse(_titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _titleId))
if (!_applicationData.ControlHolder.ByteSpan.IsZeros())
{
GtkDialog.CreateErrorDialog("The selected game did not have a valid Title Id");
return;
_openSaveUserDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.UserAccountSaveDataSize > 0;
_openSaveDeviceDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.DeviceSaveDataSize > 0;
_openSaveBcatDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
}
else
{
_openSaveUserDirMenuItem.Sensitive = false;
_openSaveDeviceDirMenuItem.Sensitive = false;
_openSaveBcatDirMenuItem.Sensitive = false;
}
_openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0;
_openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0;
_openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0;
string fileExt = System.IO.Path.GetExtension(_titleFilePath).ToLower();
string fileExt = System.IO.Path.GetExtension(_applicationData.Path).ToLower();
bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci";
_extractRomFsMenuItem.Sensitive = hasNca;
@ -137,7 +133,7 @@ namespace Ryujinx.UI.Widgets
private void OpenSaveDir(in SaveDataFilter saveDataFilter)
{
if (!TryFindSaveData(_titleName, _titleId, _controlData, in saveDataFilter, out ulong saveDataId))
if (!TryFindSaveData(_applicationData.Name, _applicationData.Id, _applicationData.ControlHolder, in saveDataFilter, out ulong saveDataId))
{
return;
}
@ -190,7 +186,7 @@ namespace Ryujinx.UI.Widgets
{
Title = "Ryujinx - NCA Section Extractor",
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Gtk3.UI.Common.Resources.Logo_Ryujinx.png"),
SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...",
SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_applicationData.Path)}...",
WindowPosition = WindowPosition.Center,
};
@ -202,29 +198,16 @@ namespace Ryujinx.UI.Widgets
}
});
using FileStream file = new(_titleFilePath, FileMode.Open, FileAccess.Read);
using FileStream file = new(_applicationData.Path, FileMode.Open, FileAccess.Read);
Nca mainNca = null;
Nca patchNca = null;
if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") ||
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") ||
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci"))
if ((System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".nsp") ||
(System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".pfs0") ||
(System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".xci"))
{
IFileSystem pfs;
if (System.IO.Path.GetExtension(_titleFilePath) == ".xci")
{
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
pfs = xci.OpenPartition(XciPartitionType.Secure);
}
else
{
var pfsTemp = new PartitionFileSystem();
pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
pfs = pfsTemp;
}
IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(_applicationData.Path, _virtualFileSystem);
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
@ -249,7 +232,7 @@ namespace Ryujinx.UI.Widgets
}
}
}
else if (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nca")
else if (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".nca")
{
mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
}
@ -266,7 +249,11 @@ namespace Ryujinx.UI.Widgets
return;
}
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
(Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _);
if (updatePatchNca != null)
{
@ -460,44 +447,44 @@ namespace Ryujinx.UI.Widgets
private void OpenSaveUserDir_Clicked(object sender, EventArgs args)
{
var userId = new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
var saveDataFilter = SaveDataFilter.Make(_titleId, saveType: default, userId, saveDataId: default, index: default);
var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, saveType: default, userId, saveDataId: default, index: default);
OpenSaveDir(in saveDataFilter);
}
private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args)
{
var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Device, userId: default, saveDataId: default, index: default);
var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, SaveDataType.Device, userId: default, saveDataId: default, index: default);
OpenSaveDir(in saveDataFilter);
}
private void OpenSaveBcatDir_Clicked(object sender, EventArgs args)
{
var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
OpenSaveDir(in saveDataFilter);
}
private void ManageTitleUpdates_Clicked(object sender, EventArgs args)
{
new TitleUpdateWindow(_parent, _virtualFileSystem, _titleIdText, _titleName).Show();
new TitleUpdateWindow(_parent, _virtualFileSystem, _applicationData).Show();
}
private void ManageDlc_Clicked(object sender, EventArgs args)
{
new DlcWindow(_virtualFileSystem, _titleIdText, _titleName).Show();
new DlcWindow(_virtualFileSystem, _applicationData.IdBaseString, _applicationData).Show();
}
private void ManageCheats_Clicked(object sender, EventArgs args)
{
new CheatWindow(_virtualFileSystem, _titleId, _titleName, _titleFilePath).Show();
new CheatWindow(_virtualFileSystem, _applicationData.Id, _applicationData.Name, _applicationData.Path).Show();
}
private void OpenTitleModDir_Clicked(object sender, EventArgs args)
{
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, _titleIdText);
string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, _applicationData.IdString);
OpenHelper.OpenFolder(titleModsPath);
}
@ -505,7 +492,7 @@ namespace Ryujinx.UI.Widgets
private void OpenTitleSdModDir_Clicked(object sender, EventArgs args)
{
string sdModsBasePath = ModLoader.GetSdModsBasePath();
string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, _titleIdText);
string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, _applicationData.IdString);
OpenHelper.OpenFolder(titleModsPath);
}
@ -527,7 +514,7 @@ namespace Ryujinx.UI.Widgets
private void OpenPtcDir_Clicked(object sender, EventArgs args)
{
string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu");
string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu");
string mainPath = System.IO.Path.Combine(ptcDir, "0");
string backupPath = System.IO.Path.Combine(ptcDir, "1");
@ -544,7 +531,7 @@ namespace Ryujinx.UI.Widgets
private void OpenShaderCacheDir_Clicked(object sender, EventArgs args)
{
string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader");
string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "shader");
if (!Directory.Exists(shaderCacheDir))
{
@ -556,10 +543,10 @@ namespace Ryujinx.UI.Widgets
private void PurgePtcCache_Clicked(object sender, EventArgs args)
{
DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0"));
DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1"));
DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu", "0"));
DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu", "1"));
MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_applicationData.Name}</b>\n\nAre you sure you want to proceed?");
List<FileInfo> cacheFiles = new();
@ -593,9 +580,9 @@ namespace Ryujinx.UI.Widgets
private void PurgeShaderCache_Clicked(object sender, EventArgs args)
{
DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader"));
DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "shader"));
using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n<b>{_applicationData.Name}</b>\n\nAre you sure you want to proceed?");
List<DirectoryInfo> oldCacheDirectories = new();
List<FileInfo> newCacheFiles = new();
@ -637,8 +624,11 @@ namespace Ryujinx.UI.Widgets
private void CreateShortcut_Clicked(object sender, EventArgs args)
{
byte[] appIcon = new ApplicationLibrary(_virtualFileSystem).GetApplicationIcon(_titleFilePath, ConfigurationState.Instance.System.Language);
ShortcutHelper.CreateAppShortcut(_titleFilePath, _titleName, _titleIdText, appIcon);
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
byte[] appIcon = new ApplicationLibrary(_virtualFileSystem, checkLevel).GetApplicationIcon(_applicationData.Path, ConfigurationState.Instance.System.Language, _applicationData.Id);
ShortcutHelper.CreateAppShortcut(_applicationData.Path, _applicationData.Name, _applicationData.IdString, appIcon);
}
}
}

View File

@ -9,16 +9,13 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.FileSystem;
using Ryujinx.UI.Common.Configuration;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SkiaSharp;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Image = SixLabors.ImageSharp.Image;
using System.Runtime.InteropServices;
namespace Ryujinx.UI.Windows
{
@ -144,9 +141,11 @@ namespace Ryujinx.UI.Windows
stream.Position = 0;
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
using var avatarImage = new SKBitmap(new SKImageInfo(256, 256, SKColorType.Rgba8888));
var data = DecompressYaz0(stream);
Marshal.Copy(data, 0, avatarImage.GetPixels(), data.Length);
avatarImage.SaveAsPng(streamPng);
avatarImage.Encode(streamPng, SKEncodedImageFormat.Png, 80);
_avatarDict.Add(item.FullPath, streamPng.ToArray());
}
@ -170,15 +169,23 @@ namespace Ryujinx.UI.Windows
{
using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
Image avatarImage = Image.Load(data, new PngDecoder());
using var avatarImage = SKBitmap.Decode(data);
using var surface = SKSurface.Create(avatarImage.Info);
avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
var background = new SKColor(
(byte)(_backgroundColor.Red * 255),
(byte)(_backgroundColor.Green * 255),
(byte)(_backgroundColor.Blue * 255),
(byte)(_backgroundColor.Alpha * 255)
)));
avatarImage.SaveAsJpeg(streamJpg);
);
var canvas = surface.Canvas;
canvas.Clear(background);
canvas.DrawBitmap(avatarImage, new SKPoint());
surface.Flush();
using var snapshot = surface.Snapshot();
using var encoded = snapshot.Encode(SKEncodedImageFormat.Jpeg, 80);
encoded.SaveTo(streamJpg);
return streamJpg.ToArray();
}

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