Compare commits

...

18 Commits

Author SHA1 Message Date
Isaac Marovitz
f07ae7d53f Fix Title Update Manager not selecting right update (#4452) 2023-02-22 17:58:32 -03:00
dependabot[bot]
c308f09722 nuget: bump Microsoft.NET.Test.Sdk from 17.4.1 to 17.5.0 (#4458)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.1 to 17.5.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.1...v17.5.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-22 16:08:25 +01:00
dependabot[bot]
f1eef29409 nuget: bump UnicornEngine.Unicorn (#4459)
Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-9c9356d to 2.0.2-rc1-a913199.
- [Release notes](https://github.com/unicorn-engine/unicorn/releases)
- [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog)
- [Commits](https://github.com/unicorn-engine/unicorn/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-22 13:30:53 +01:00
TSRBerry
1f8d66db7c Ava: Fix Updater crashing on Linux (#4457) 2023-02-22 09:13:50 +01:00
gdkchan
c3a5716a95 Add copy dependency for some incompatible texture formats (#4380)
* Add copy dependency for some incompatible texture formats

* Simplify compatibility check
2023-02-21 19:21:57 -03:00
Mary
1f1e2a7f03 misc: changes base application directory behaviour (#4460)
This allows changing base application directory behavior at build time via FORCE_EXTERNAL_BASE_DIR.

This is intended to be used by nixpkgs and flathub builds.

I also added the missing patch for macOS that we have on macos1 to avoid invalidating code signature.
2023-02-21 22:38:34 +01:00
Andrew Glaze
e54f9dc4b4 Move Ryujinx Folder from ~/.config to ~/Library/Application Support on macOS (#4296)
* Move Ryujinx folder to Application Support on macOS

* Create a symlink to preserve back compat

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

* Remove extra whitespace

* Don’t create a symlink

* Update Ryujinx.Common/Configuration/AppDataManager.cs

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

* Revert "Don’t create a symlink"

This reverts commit 31752fe8ab.

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-02-21 12:14:31 +01:00
Logan Stromberg
edfd4d70c0 Use SIMD acceleration for audio upsampler (#4410)
* Use SIMD acceleration for audio upsampler filter kernel for a moderate speedup

* Address formatting. Implement AVX2 fast path for high quality resampling in ResamplerHelper

* now really, are we really getting the benefit of inlining 50+ line methods?

* adding unit tests for resampler + upsampler. The upsampler ones fail for some reason

* Fixing upsampler test. Apparently this algo only works at specific ratios

---------

Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
2023-02-21 11:44:57 +01:00
riperiperi
fc43aecbbd Memory: Faster Split for NonOverlappingRangeList (#4451)
I noticed that in Xenoblade 2, the game can end up spending a lot of time adding and removing tracking handles. One of the main causes of this is actually splitting existing handles, which does the following:

- Remove existing handle from list
- Update existing handle to end at split address, create new handle starting at split address
- Add updated handle (left) to list
- Add new handle (right) to list

This costs 1 deletion and 2 insertions. When there are more handles, this gets a lot more expensive, as insertions are done by copying all values to the right, and deletions by copying values to the left.

This PR simply allows it to look up the handle being split, and replace its entry with the new end address without insertion or deletion. This makes a split only cost one insertion and a binary search lookup (very cheap). This isn't all of the cost on Xenoblade 2, but it does significantly reduce it.

There might be something else to this - we could find a way to reduce the handle count for the game (merging on deletion? buffer deletion?), we could use a different structure for virtual regions, as the current one is optimal for buffer lookups which nearly always read, memory tracking has more of a balance between read/write. That's for a later date though, this was an easy improvment.
2023-02-21 10:53:38 +01:00
gdkchan
58d7a1fe97 Mark texture as modified and sync on I2M fast path (#4449) 2023-02-21 10:40:23 +01:00
gdkchan
7aa430f1a5 Add support for advanced blend (part 1/2) (#2801)
* Add blend microcode registers

* Add advanced blend support using host extension

* Remove debug message

* Use pre-generated table for blend functions

* XML docs

* Rename AdvancedBlendMode to AdvancedBlendOp for consistency

* Remove redundant code

* Fix some advanced blend related issues on Vulkan

* Formatting
2023-02-19 22:37:37 -03:00
dependabot[bot]
6bf460e104 nuget: bump System.IdentityModel.Tokens.Jwt from 6.26.1 to 6.27.0 (#4441)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.26.1 to 6.27.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-18 02:35:02 +01:00
gdkchan
efb135b74c Clear CPU side data on GPU buffer clears (#4125)
* Clear CPU side data on GPU buffer clears

* Implement tracked fill operation that can signal other resource types except buffer

* Fix tests, add missing XML doc

* PR feedback
2023-02-16 18:28:49 -03:00
gdkchan
a707842e14 Validate dimensions before creating texture (#4430) 2023-02-16 11:16:31 -03:00
TSRBerry
a5a9b9bc8b GUI: Small Updater refactor & Set correct permissions on Linux when extracting files (#4315)
* ava: Refactor Updater.cs

Fix typos
Remove unused usings
Rename variables to follow naming scheme

* ava: Set file permissions when extracting update files

* gtk: Apply the same refactor to Updater.cs

* updater: Replace assert with if statement

* updater: Remove await usings again
2023-02-15 22:36:35 +00:00
Mary
17078ad929 vulkan: Respect VK_KHR_portability_subset vertex stride alignment (#4419)
* vulkan: Respect VK_KHR_portability_subset vertex stride alignment

We were hardcoding alignment to 4, but by specs it can be any values that
is a power of 2.

This also enable VK_KHR_portability_subset if present as per specs
requirements.

* address gdkchan's comment

* Make NeedsVertexBufferAlignment internal
2023-02-15 08:41:48 +00:00
Mary
32450d45de vulkan: Clean up MemoryAllocator (#4418)
This started as an attempt to remove vkGetPhysicalDeviceMemoryProperties
in FindSuitableMemoryTypeIndex (As this could have some overhead and
shouldn't change at runtime) and turned in a little bigger cleanup.
2023-02-15 07:50:26 +01:00
Ac_K
ed7a0474c6 Infra: Issues template cleanup (#4421)
* Infra: Issues template cleanup

* applied
2023-02-14 15:58:57 +01:00
82 changed files with 7277 additions and 571 deletions

View File

@@ -1,26 +1,27 @@
name: Bug Report name: Bug Report
description: File a bug report description: File a bug report
title: "[Bug] <title>" title: "[Bug]"
labels: bug labels: bug
body: body:
- type: textarea - type: textarea
id: issue id: issue
attributes: attributes:
label: Description of Issue label: Description of the issue
description: What's the issue you encountered? description: What's the issue you encountered?
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: repro id: repro
attributes: attributes:
label: Reproduction Steps label: Reproduction steps
description: How can the issue be reproduced? description: How can the issue be reproduced?
placeholder: Describe each step as precisely as possible
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: log id: log
attributes: attributes:
label: Log File label: Log file
description: A log file will help our developers to better diagnose and fix the issue. description: A log file will help our developers to better diagnose and fix the issue.
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
validations: validations:
@@ -29,55 +30,44 @@ body:
id: os id: os
attributes: attributes:
label: OS label: OS
placeholder: "Example: Windows 10" placeholder: "e.g. Windows 10"
validations: validations:
required: true required: true
- type: input - type: input
id: ryujinx-version id: ryujinx-version
attributes: attributes:
label: Ryujinx version label: Ryujinx version
placeholder: | placeholder: "e.g. 1.0.470"
- *(e.g. 1.0.470)*
validations: validations:
required: true required: true
- type: input - type: input
id: game-version id: game-version
attributes: attributes:
label: Game version label: Game version
placeholder: | placeholder: "e.g. 1.1.1"
- *(e.g. 1.1.1)*
validations: validations:
required: false required: false
- type: input - type: input
id: cpu id: cpu
attributes: attributes:
label: CPU label: CPU
placeholder: | placeholder: "e.g. i7-6700"
- *(e.g. i7-6700)*
validations: validations:
required: false required: false
- type: input - type: input
id: gpu id: gpu
attributes: attributes:
label: GPU label: GPU
placeholder: | placeholder: "e.g. NVIDIA RTX 2070"
- *(e.g. NVIDIA RTX 2070)*
validations: validations:
required: false required: false
- type: input - type: input
id: ram id: ram
attributes: attributes:
label: RAM label: RAM
placeholder: | placeholder: "e.g. 16GB"
- *(e.g. 16GB)*
validations: validations:
required: false required: false
- type: checkboxes
attributes:
label: Applied Mods?
options:
- label: "Yes"
required: false
- type: textarea - type: textarea
id: mods id: mods
attributes: attributes:
@@ -93,4 +83,4 @@ body:
- Additional info about your environment: - Additional info about your environment:
- Any other information relevant to your issue. - Any other information relevant to your issue.
validations: validations:
required: false required: false

View File

@@ -1,6 +1,6 @@
name: Feature Request name: Feature Request
description: Suggest a new feature for Ryujinx. description: Suggest a new feature for Ryujinx.
title: "[Feature Request] <title>" title: "[Feature Request]"
body: body:
- type: textarea - type: textarea
id: overview id: overview
@@ -12,14 +12,14 @@ body:
- type: textarea - type: textarea
id: details id: details
attributes: attributes:
label: Smaller Details label: Smaller details
description: These may include specific methods of implementation etc. description: These may include specific methods of implementation etc.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: request id: request
attributes: attributes:
label: Nature of Request label: Nature of request
validations: validations:
required: true required: true
- type: textarea - type: textarea

View File

@@ -1,6 +1,6 @@
name: Missing CPU Instruction name: Missing CPU Instruction
description: CPU Instruction is missing in Ryujinx. description: CPU Instruction is missing in Ryujinx.
title: "[CPU] <title>" title: "[CPU]"
labels: [cpu, not-implemented] labels: [cpu, not-implemented]
body: body:
- type: textarea - type: textarea

View File

@@ -5,7 +5,7 @@ body:
- type: textarea - type: textarea
id: instruction id: instruction
attributes: attributes:
label: Service Call label: Service call
description: What service call is missing? description: What service call is missing?
validations: validations:
required: true required: true

View File

@@ -71,6 +71,7 @@ namespace ARMeilleure.Memory
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="write">True if the region was written, false if read</param> /// <param name="write">True if the region was written, false if read</param>
/// <param name="precise">True if the access is precise, false otherwise</param> /// <param name="precise">True if the access is precise, false otherwise</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false); /// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
} }
} }

View File

@@ -222,7 +222,7 @@ namespace ARMeilleure.Signal
// Tracking action should be non-null to call it, otherwise assume false return. // Tracking action should be non-null to call it, otherwise assume false return.
context.BranchIfFalse(skipActionLabel, trackingActionPtr); context.BranchIfFalse(skipActionLabel, trackingActionPtr);
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite, Const(0)); Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite);
context.Copy(inRegionLocal, result); context.Copy(inRegionLocal, result);
context.MarkLabel(skipActionLabel); context.MarkLabel(skipActionLabel);

View File

@@ -22,7 +22,7 @@
<PackageVersion Include="LibHac" Version="0.17.0" /> <PackageVersion Include="LibHac" Version="0.17.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NUnit" Version="3.13.3" /> <PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" /> <PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
@@ -44,12 +44,12 @@
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.26.1" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" /> <PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" />
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-9c9356d" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-a913199" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
@@ -380,7 +381,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
return _normalCurveLut2F; return _normalCurveLut2F;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static void ResampleDefaultQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount, bool needPitch) private unsafe static void ResampleDefaultQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount, bool needPitch)
{ {
ReadOnlySpan<float> parameters = GetDefaultParameter(ratio); ReadOnlySpan<float> parameters = GetDefaultParameter(ratio);
@@ -394,35 +394,33 @@ namespace Ryujinx.Audio.Renderer.Dsp
if (ratio == 1f) if (ratio == 1f)
{ {
fixed (short* pInput = inputBuffer) fixed (short* pInput = inputBuffer)
fixed (float* pOutput = outputBuffer, pParameters = parameters)
{ {
fixed (float* pOutput = outputBuffer, pParameters = parameters) Vector128<float> parameter = Sse.LoadVector128(pParameters);
for (; i < (sampleCount & ~3); i += 4)
{ {
Vector128<float> parameter = Sse.LoadVector128(pParameters); Vector128<int> intInput0 = Sse41.ConvertToVector128Int32(pInput + (uint)i);
Vector128<int> intInput1 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 1);
Vector128<int> intInput2 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 2);
Vector128<int> intInput3 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 3);
for (; i < (sampleCount & ~3); i += 4) Vector128<float> input0 = Sse2.ConvertToVector128Single(intInput0);
{ Vector128<float> input1 = Sse2.ConvertToVector128Single(intInput1);
Vector128<int> intInput0 = Sse41.ConvertToVector128Int32(pInput + (uint)i); Vector128<float> input2 = Sse2.ConvertToVector128Single(intInput2);
Vector128<int> intInput1 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 1); Vector128<float> input3 = Sse2.ConvertToVector128Single(intInput3);
Vector128<int> intInput2 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 2);
Vector128<int> intInput3 = Sse41.ConvertToVector128Int32(pInput + (uint)i + 3);
Vector128<float> input0 = Sse2.ConvertToVector128Single(intInput0); Vector128<float> mix0 = Sse.Multiply(input0, parameter);
Vector128<float> input1 = Sse2.ConvertToVector128Single(intInput1); Vector128<float> mix1 = Sse.Multiply(input1, parameter);
Vector128<float> input2 = Sse2.ConvertToVector128Single(intInput2); Vector128<float> mix2 = Sse.Multiply(input2, parameter);
Vector128<float> input3 = Sse2.ConvertToVector128Single(intInput3); Vector128<float> mix3 = Sse.Multiply(input3, parameter);
Vector128<float> mix0 = Sse.Multiply(input0, parameter); Vector128<float> mix01 = Sse3.HorizontalAdd(mix0, mix1);
Vector128<float> mix1 = Sse.Multiply(input1, parameter); Vector128<float> mix23 = Sse3.HorizontalAdd(mix2, mix3);
Vector128<float> mix2 = Sse.Multiply(input2, parameter);
Vector128<float> mix3 = Sse.Multiply(input3, parameter);
Vector128<float> mix01 = Sse3.HorizontalAdd(mix0, mix1); Vector128<float> mix0123 = Sse3.HorizontalAdd(mix01, mix23);
Vector128<float> mix23 = Sse3.HorizontalAdd(mix2, mix3);
Vector128<float> mix0123 = Sse3.HorizontalAdd(mix01, mix23); Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123));
Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123));
}
} }
} }
@@ -431,62 +429,60 @@ namespace Ryujinx.Audio.Renderer.Dsp
else else
{ {
fixed (short* pInput = inputBuffer) fixed (short* pInput = inputBuffer)
fixed (float* pOutput = outputBuffer, pParameters = parameters)
{ {
fixed (float* pOutput = outputBuffer, pParameters = parameters) for (; i < (sampleCount & ~3); i += 4)
{ {
for (; i < (sampleCount & ~3); i += 4) uint baseIndex0 = (uint)(fraction * 128) * 4;
{ uint inputIndex0 = (uint)inputBufferIndex;
uint baseIndex0 = (uint)(fraction * 128) * 4;
uint inputIndex0 = (uint)inputBufferIndex;
fraction += ratio; fraction += ratio;
uint baseIndex1 = ((uint)(fraction * 128) & 127) * 4; uint baseIndex1 = ((uint)(fraction * 128) & 127) * 4;
uint inputIndex1 = (uint)inputBufferIndex + (uint)fraction; uint inputIndex1 = (uint)inputBufferIndex + (uint)fraction;
fraction += ratio; fraction += ratio;
uint baseIndex2 = ((uint)(fraction * 128) & 127) * 4; uint baseIndex2 = ((uint)(fraction * 128) & 127) * 4;
uint inputIndex2 = (uint)inputBufferIndex + (uint)fraction; uint inputIndex2 = (uint)inputBufferIndex + (uint)fraction;
fraction += ratio; fraction += ratio;
uint baseIndex3 = ((uint)(fraction * 128) & 127) * 4; uint baseIndex3 = ((uint)(fraction * 128) & 127) * 4;
uint inputIndex3 = (uint)inputBufferIndex + (uint)fraction; uint inputIndex3 = (uint)inputBufferIndex + (uint)fraction;
fraction += ratio; fraction += ratio;
inputBufferIndex += (int)fraction; inputBufferIndex += (int)fraction;
// Only keep lower part (safe as fraction isn't supposed to be negative) // Only keep lower part (safe as fraction isn't supposed to be negative)
fraction -= (int)fraction; fraction -= (int)fraction;
Vector128<float> parameter0 = Sse.LoadVector128(pParameters + baseIndex0); Vector128<float> parameter0 = Sse.LoadVector128(pParameters + baseIndex0);
Vector128<float> parameter1 = Sse.LoadVector128(pParameters + baseIndex1); Vector128<float> parameter1 = Sse.LoadVector128(pParameters + baseIndex1);
Vector128<float> parameter2 = Sse.LoadVector128(pParameters + baseIndex2); Vector128<float> parameter2 = Sse.LoadVector128(pParameters + baseIndex2);
Vector128<float> parameter3 = Sse.LoadVector128(pParameters + baseIndex3); Vector128<float> parameter3 = Sse.LoadVector128(pParameters + baseIndex3);
Vector128<int> intInput0 = Sse41.ConvertToVector128Int32(pInput + inputIndex0); Vector128<int> intInput0 = Sse41.ConvertToVector128Int32(pInput + inputIndex0);
Vector128<int> intInput1 = Sse41.ConvertToVector128Int32(pInput + inputIndex1); Vector128<int> intInput1 = Sse41.ConvertToVector128Int32(pInput + inputIndex1);
Vector128<int> intInput2 = Sse41.ConvertToVector128Int32(pInput + inputIndex2); Vector128<int> intInput2 = Sse41.ConvertToVector128Int32(pInput + inputIndex2);
Vector128<int> intInput3 = Sse41.ConvertToVector128Int32(pInput + inputIndex3); Vector128<int> intInput3 = Sse41.ConvertToVector128Int32(pInput + inputIndex3);
Vector128<float> input0 = Sse2.ConvertToVector128Single(intInput0); Vector128<float> input0 = Sse2.ConvertToVector128Single(intInput0);
Vector128<float> input1 = Sse2.ConvertToVector128Single(intInput1); Vector128<float> input1 = Sse2.ConvertToVector128Single(intInput1);
Vector128<float> input2 = Sse2.ConvertToVector128Single(intInput2); Vector128<float> input2 = Sse2.ConvertToVector128Single(intInput2);
Vector128<float> input3 = Sse2.ConvertToVector128Single(intInput3); Vector128<float> input3 = Sse2.ConvertToVector128Single(intInput3);
Vector128<float> mix0 = Sse.Multiply(input0, parameter0); Vector128<float> mix0 = Sse.Multiply(input0, parameter0);
Vector128<float> mix1 = Sse.Multiply(input1, parameter1); Vector128<float> mix1 = Sse.Multiply(input1, parameter1);
Vector128<float> mix2 = Sse.Multiply(input2, parameter2); Vector128<float> mix2 = Sse.Multiply(input2, parameter2);
Vector128<float> mix3 = Sse.Multiply(input3, parameter3); Vector128<float> mix3 = Sse.Multiply(input3, parameter3);
Vector128<float> mix01 = Sse3.HorizontalAdd(mix0, mix1); Vector128<float> mix01 = Sse3.HorizontalAdd(mix0, mix1);
Vector128<float> mix23 = Sse3.HorizontalAdd(mix2, mix3); Vector128<float> mix23 = Sse3.HorizontalAdd(mix2, mix3);
Vector128<float> mix0123 = Sse3.HorizontalAdd(mix01, mix23); Vector128<float> mix0123 = Sse3.HorizontalAdd(mix01, mix23);
Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123)); Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123));
}
} }
} }
} }
@@ -526,34 +522,59 @@ namespace Ryujinx.Audio.Renderer.Dsp
return _highCurveLut2F; return _highCurveLut2F;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe void ResampleHighQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount)
private static void ResampleHighQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount)
{ {
ReadOnlySpan<float> parameters = GetHighParameter(ratio); ReadOnlySpan<float> parameters = GetHighParameter(ratio);
int inputBufferIndex = 0; int inputBufferIndex = 0;
// TODO: fast path if (Avx2.IsSupported)
for (int i = 0; i < sampleCount; i++)
{ {
int baseIndex = (int)(fraction * 128) * 8; // Fast path; assumes 256-bit vectors for simplicity because the filter is 8 taps
ReadOnlySpan<float> parameter = parameters.Slice(baseIndex, 8); fixed (short* pInput = inputBuffer)
ReadOnlySpan<short> currentInput = inputBuffer.Slice(inputBufferIndex, 8); fixed (float* pParameters = parameters)
{
for (int i = 0; i < sampleCount; i++)
{
int baseIndex = (int)(fraction * 128) * 8;
outputBuffer[i] = (float)Math.Round(currentInput[0] * parameter[0] + Vector256<int> intInput = Avx2.ConvertToVector256Int32(pInput + inputBufferIndex);
currentInput[1] * parameter[1] + Vector256<float> floatInput = Avx.ConvertToVector256Single(intInput);
currentInput[2] * parameter[2] + Vector256<float> parameter = Avx.LoadVector256(pParameters + baseIndex);
currentInput[3] * parameter[3] + Vector256<float> dp = Avx.DotProduct(floatInput, parameter, control: 0xFF);
currentInput[4] * parameter[4] +
currentInput[5] * parameter[5] +
currentInput[6] * parameter[6] +
currentInput[7] * parameter[7]);
fraction += ratio; // avx2 does an 8-element dot product piecewise so we have to sum up 2 intermediate results
inputBufferIndex += (int)MathF.Truncate(fraction); outputBuffer[i] = (float)Math.Round(dp[0] + dp[4]);
fraction -= (int)fraction; fraction += ratio;
inputBufferIndex += (int)MathF.Truncate(fraction);
fraction -= (int)fraction;
}
}
}
else
{
for (int i = 0; i < sampleCount; i++)
{
int baseIndex = (int)(fraction * 128) * 8;
ReadOnlySpan<float> parameter = parameters.Slice(baseIndex, 8);
ReadOnlySpan<short> currentInput = inputBuffer.Slice(inputBufferIndex, 8);
outputBuffer[i] = (float)Math.Round(currentInput[0] * parameter[0] +
currentInput[1] * parameter[1] +
currentInput[2] * parameter[2] +
currentInput[3] * parameter[3] +
currentInput[4] * parameter[4] +
currentInput[5] * parameter[5] +
currentInput[6] * parameter[6] +
currentInput[7] * parameter[7]);
fraction += ratio;
inputBufferIndex += (int)MathF.Truncate(fraction);
fraction -= (int)fraction;
}
} }
} }

View File

@@ -2,6 +2,7 @@ using Ryujinx.Audio.Renderer.Server.Upsampler;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Dsp namespace Ryujinx.Audio.Renderer.Dsp
@@ -70,16 +71,32 @@ namespace Ryujinx.Audio.Renderer.Dsp
return; return;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
float DoFilterBank(ref UpsamplerBufferState state, in Array20<float> bank) float DoFilterBank(ref UpsamplerBufferState state, in Array20<float> bank)
{ {
float result = 0.0f; float result = 0.0f;
Debug.Assert(state.History.Length == HistoryLength); Debug.Assert(state.History.Length == HistoryLength);
Debug.Assert(bank.Length == FilterBankLength); Debug.Assert(bank.Length == FilterBankLength);
for (int j = 0; j < FilterBankLength; j++)
int curIdx = 0;
if (Vector.IsHardwareAccelerated)
{ {
result += bank[j] * state.History[j]; // Do SIMD-accelerated block operations where possible.
// Only about a 2x speedup since filter bank length is short
int stopIdx = FilterBankLength - (FilterBankLength % Vector<float>.Count);
while (curIdx < stopIdx)
{
result += Vector.Dot(
new Vector<float>(bank.AsSpan().Slice(curIdx, Vector<float>.Count)),
new Vector<float>(state.History.AsSpan().Slice(curIdx, Vector<float>.Count)));
curIdx += Vector<float>.Count;
}
}
while (curIdx < FilterBankLength)
{
result += bank[curIdx] * state.History[curIdx];
curIdx++;
} }
return result; return result;

View File

@@ -132,8 +132,8 @@ namespace Ryujinx.Modules
} }
} }
// If build not done, assume no new update are availaible. // If build not done, assume no new update are available.
if (_buildUrl == null) if (_buildUrl is null)
{ {
if (showVersionUpToDate) if (showVersionUpToDate)
{ {
@@ -240,13 +240,13 @@ namespace Ryujinx.Modules
{ {
HttpClient result = new(); HttpClient result = new();
// Required by GitHub to interract with APIs. // Required by GitHub to interact with APIs.
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
return result; return result;
} }
public static async void UpdateRyujinx(Window parent, string downloadUrl) private static async void UpdateRyujinx(Window parent, string downloadUrl)
{ {
_updateSuccessful = false; _updateSuccessful = false;
@@ -300,8 +300,6 @@ namespace Ryujinx.Modules
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
} }
SetFileExecutable(ryuExe);
Process.Start(ryuExe, CommandLineState.Arguments); Process.Start(ryuExe, CommandLineState.Arguments);
Environment.Exit(0); Environment.Exit(0);
@@ -408,9 +406,9 @@ namespace Ryujinx.Modules
Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, ex.Message);
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
for (int j = 0; j < webClients.Count; j++) foreach (WebClient webClient in webClients)
{ {
webClients[j].CancelAsync(); webClient.CancelAsync();
} }
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
@@ -472,22 +470,6 @@ namespace Ryujinx.Modules
worker.Start(); worker.Start();
} }
private static void SetFileExecutable(string path)
{
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
UnixFileMode.UserWrite |
UnixFileMode.UserRead |
UnixFileMode.GroupRead |
UnixFileMode.GroupWrite |
UnixFileMode.OtherRead |
UnixFileMode.OtherWrite;
if (!OperatingSystem.IsWindows() && File.Exists(path))
{
File.SetUnixFileMode(path, ExecutableFileMode);
}
}
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
{ {
// Extract Update // Extract Update
@@ -503,27 +485,35 @@ namespace Ryujinx.Modules
await Task.Run(() => await Task.Run(() =>
{ {
TarEntry tarEntry; TarEntry tarEntry;
while ((tarEntry = tarStream.GetNextEntry()) != null)
if (!OperatingSystem.IsWindows())
{ {
if (tarEntry.IsDirectory) continue; while ((tarEntry = tarStream.GetNextEntry()) is not null)
string outPath = Path.Combine(UpdateDir, tarEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (FileStream outStream = File.OpenWrite(outPath))
{ {
tarStream.CopyEntryContents(outStream); if (tarEntry.IsDirectory) continue;
string outPath = Path.Combine(UpdateDir, tarEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (FileStream outStream = File.OpenWrite(outPath))
{
tarStream.CopyEntryContents(outStream);
}
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
Dispatcher.UIThread.Post(() =>
{
if (tarEntry is null)
{
return;
}
taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal);
});
} }
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
TarEntry entry = tarEntry;
Dispatcher.UIThread.Post(() =>
{
taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal);
});
} }
}); });
@@ -603,8 +593,6 @@ namespace Ryujinx.Modules
Directory.Delete(UpdateDir, true); Directory.Delete(UpdateDir, true);
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
_updateSuccessful = true; _updateSuccessful = true;
taskDialog.Hide(); taskDialog.Hide();

View File

@@ -105,13 +105,13 @@ public class TitleUpdateViewModel : BaseModel
AddUpdate(path); AddUpdate(path);
} }
// NOTE: Save the list again to remove leftovers.
Save();
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
SelectedUpdate = selected; SelectedUpdate = selected;
// NOTE: Save the list again to remove leftovers.
Save();
SortUpdates(); SortUpdates();
} }

View File

@@ -45,7 +45,15 @@ namespace Ryujinx.Common.Configuration
public static void Initialize(string baseDirPath) public static void Initialize(string baseDirPath)
{ {
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string appDataPath;
if (OperatingSystem.IsMacOS())
{
appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support");
}
else
{
appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
}
if (appDataPath.Length == 0) if (appDataPath.Length == 0)
{ {
@@ -81,6 +89,21 @@ namespace Ryujinx.Common.Configuration
BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths
// NOTE: Moves the Ryujinx folder in `~/.config` to `~/Library/Application Support` if one is found
// and a Ryujinx folder does not already exist in Application Support.
// Also creates a symlink from `~/.config/Ryujinx` to `~/Library/Application Support/Ryujinx` to preserve backwards compatibility.
// This should be removed in the future.
if (OperatingSystem.IsMacOS() && Mode == LaunchMode.UserProfile)
{
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
if (Path.Exists(oldConfigPath) && !Path.Exists(BaseDirPath))
{
CopyDirectory(oldConfigPath, BaseDirPath);
Directory.Delete(oldConfigPath, true);
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
}
}
SetupBasePaths(); SetupBasePaths();
} }
@@ -92,6 +115,34 @@ namespace Ryujinx.Common.Configuration
Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir)); Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir));
} }
private static void CopyDirectory(string sourceDir, string destinationDir)
{
var dir = new DirectoryInfo(sourceDir);
if (!dir.Exists)
{
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
}
DirectoryInfo[] subDirs = dir.GetDirectories();
Directory.CreateDirectory(destinationDir);
foreach (FileInfo file in dir.GetFiles())
{
if (file.Name == ".DS_Store")
{
continue;
}
file.CopyTo(Path.Combine(destinationDir, file.Name));
}
foreach (DirectoryInfo subDir in subDirs)
{
CopyDirectory(subDir.FullName, Path.Combine(destinationDir, subDir.Name));
}
}
public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName; public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName; public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName;
} }

View File

@@ -40,14 +40,21 @@ namespace Ryujinx.Common
} }
} }
#if FORCE_EXTERNAL_BASE_DIR
public static string GetBaseApplicationDirectory() public static string GetBaseApplicationDirectory()
{ {
if (IsFlatHubBuild()) return AppDataManager.BaseDirPath;
}
#else
public static string GetBaseApplicationDirectory()
{
if (IsFlatHubBuild() || OperatingSystem.IsMacOS())
{ {
return AppDataManager.BaseDirPath; return AppDataManager.BaseDirPath;
} }
return AppDomain.CurrentDomain.BaseDirectory; return AppDomain.CurrentDomain.BaseDirectory;
} }
#endif
} }
} }

View File

@@ -634,13 +634,13 @@ namespace Ryujinx.Cpu.AppleHv
/// <remarks> /// <remarks>
/// This function also validates that the given range is both valid and mapped, and will throw if it is not. /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
/// </remarks> /// </remarks>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
AssertValidAddressAndSize(va, size); AssertValidAddressAndSize(va, size);
if (precise) if (precise)
{ {
Tracking.VirtualMemoryEvent(va, size, write, precise: true); Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
return; return;
} }
@@ -663,7 +663,7 @@ namespace Ryujinx.Cpu.AppleHv
if (state >= tag) if (state >= tag)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
return; return;
} }
else if (state == 0) else if (state == 0)
@@ -706,7 +706,7 @@ namespace Ryujinx.Cpu.AppleHv
// Only trigger tracking from reads if both bits are set on any page. // Only trigger tracking from reads if both bits are set on any page.
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
break; break;
} }
} }
@@ -822,21 +822,21 @@ namespace Ryujinx.Cpu.AppleHv
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size) public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size)); return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
} }
/// <summary> /// <summary>

View File

@@ -28,8 +28,9 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuRegionHandle BeginTracking(ulong address, ulong size); CpuRegionHandle BeginTracking(ulong address, ulong size, int id);
/// <summary> /// <summary>
/// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. /// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
@@ -38,8 +39,9 @@ namespace Ryujinx.Cpu
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param> /// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity); CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id);
/// <summary> /// <summary>
/// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. /// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
@@ -47,7 +49,8 @@ namespace Ryujinx.Cpu
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity); CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id);
} }
} }

View File

@@ -629,31 +629,31 @@ namespace Ryujinx.Cpu.Jit
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size) public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size)); return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
AssertValidAddressAndSize(va, size); AssertValidAddressAndSize(va, size);
if (precise) if (precise)
{ {
Tracking.VirtualMemoryEvent(va, size, write, precise: true); Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
return; return;
} }
@@ -676,7 +676,7 @@ namespace Ryujinx.Cpu.Jit
if ((pte & tag) != 0) if ((pte & tag) != 0)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
break; break;
} }

View File

@@ -518,13 +518,13 @@ namespace Ryujinx.Cpu.Jit
/// <remarks> /// <remarks>
/// This function also validates that the given range is both valid and mapped, and will throw if it is not. /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
/// </remarks> /// </remarks>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
AssertValidAddressAndSize(va, size); AssertValidAddressAndSize(va, size);
if (precise) if (precise)
{ {
Tracking.VirtualMemoryEvent(va, size, write, precise: true); Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
return; return;
} }
@@ -547,7 +547,7 @@ namespace Ryujinx.Cpu.Jit
if (state >= tag) if (state >= tag)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
return; return;
} }
else if (state == 0) else if (state == 0)
@@ -590,7 +590,7 @@ namespace Ryujinx.Cpu.Jit
// Only trigger tracking from reads if both bits are set on any page. // Only trigger tracking from reads if both bits are set on any page.
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
break; break;
} }
} }
@@ -706,21 +706,21 @@ namespace Ryujinx.Cpu.Jit
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size) public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size)); return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
} }
/// <summary> /// <summary>

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Cpu
{ {
public class MemoryEhMeilleure : IDisposable public class MemoryEhMeilleure : IDisposable
{ {
private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write, bool precise = false); private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write);
private readonly MemoryTracking _tracking; private readonly MemoryTracking _tracking;
private readonly TrackingEventDelegate _trackingEvent; private readonly TrackingEventDelegate _trackingEvent;

View File

@@ -0,0 +1,16 @@
namespace Ryujinx.Graphics.GAL
{
public struct AdvancedBlendDescriptor
{
public AdvancedBlendOp Op { get; }
public AdvancedBlendOverlap Overlap { get; }
public bool SrcPreMultiplied { get; }
public AdvancedBlendDescriptor(AdvancedBlendOp op, AdvancedBlendOverlap overlap, bool srcPreMultiplied)
{
Op = op;
Overlap = overlap;
SrcPreMultiplied = srcPreMultiplied;
}
}
}

View File

@@ -0,0 +1,52 @@
namespace Ryujinx.Graphics.GAL
{
public enum AdvancedBlendOp
{
Zero,
Src,
Dst,
SrcOver,
DstOver,
SrcIn,
DstIn,
SrcOut,
DstOut,
SrcAtop,
DstAtop,
Xor,
Plus,
PlusClamped,
PlusClampedAlpha,
PlusDarker,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Minus,
MinusClamped,
Exclusion,
Contrast,
Invert,
InvertRGB,
InvertOvg,
LinearDodge,
LinearBurn,
VividLight,
LinearLight,
PinLight,
HardMix,
Red,
Green,
Blue,
HslHue,
HslSaturation,
HslColor,
HslLuminosity
}
}

View File

@@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.GAL
{
public enum AdvancedBlendOverlap
{
Uncorrelated,
Disjoint,
Conjoint
}
}

View File

@@ -23,6 +23,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsR4G4B4A4Format; public readonly bool SupportsR4G4B4A4Format;
public readonly bool SupportsSnormBufferTextureFormat; public readonly bool SupportsSnormBufferTextureFormat;
public readonly bool Supports5BitComponentFormat; public readonly bool Supports5BitComponentFormat;
public readonly bool SupportsBlendEquationAdvanced;
public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel; public readonly bool SupportsFragmentShaderOrderingIntel;
public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsGeometryShaderPassthrough;
@@ -64,6 +65,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsR4G4B4A4Format, bool supportsR4G4B4A4Format,
bool supportsSnormBufferTextureFormat, bool supportsSnormBufferTextureFormat,
bool supports5BitComponentFormat, bool supports5BitComponentFormat,
bool supportsBlendEquationAdvanced,
bool supportsFragmentShaderInterlock, bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel, bool supportsFragmentShaderOrderingIntel,
bool supportsGeometryShaderPassthrough, bool supportsGeometryShaderPassthrough,
@@ -102,6 +104,7 @@ namespace Ryujinx.Graphics.GAL
SupportsR4G4B4A4Format = supportsR4G4B4A4Format; SupportsR4G4B4A4Format = supportsR4G4B4A4Format;
SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat; SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
Supports5BitComponentFormat = supports5BitComponentFormat; Supports5BitComponentFormat = supports5BitComponentFormat;
SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel; SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;

View File

@@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.GAL
void SetAlphaTest(bool enable, float reference, CompareOp op); void SetAlphaTest(bool enable, float reference, CompareOp op);
void SetBlendState(AdvancedBlendDescriptor blend);
void SetBlendState(int index, BlendDescriptor blend); void SetBlendState(int index, BlendDescriptor blend);
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp); void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);

View File

@@ -98,6 +98,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<EndHostConditionalRenderingCommand>(CommandType.EndHostConditionalRendering); Register<EndHostConditionalRenderingCommand>(CommandType.EndHostConditionalRendering);
Register<EndTransformFeedbackCommand>(CommandType.EndTransformFeedback); Register<EndTransformFeedbackCommand>(CommandType.EndTransformFeedback);
Register<SetAlphaTestCommand>(CommandType.SetAlphaTest); Register<SetAlphaTestCommand>(CommandType.SetAlphaTest);
Register<SetBlendStateAdvancedCommand>(CommandType.SetBlendStateAdvanced);
Register<SetBlendStateCommand>(CommandType.SetBlendState); Register<SetBlendStateCommand>(CommandType.SetBlendState);
Register<SetDepthBiasCommand>(CommandType.SetDepthBias); Register<SetDepthBiasCommand>(CommandType.SetDepthBias);
Register<SetDepthClampCommand>(CommandType.SetDepthClamp); Register<SetDepthClampCommand>(CommandType.SetDepthClamp);

View File

@@ -60,6 +60,7 @@
EndHostConditionalRendering, EndHostConditionalRendering,
EndTransformFeedback, EndTransformFeedback,
SetAlphaTest, SetAlphaTest,
SetBlendStateAdvanced,
SetBlendState, SetBlendState,
SetDepthBias, SetDepthBias,
SetDepthClamp, SetDepthClamp,

View File

@@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct SetBlendStateAdvancedCommand : IGALCommand, IGALCommand<SetBlendStateAdvancedCommand>
{
public CommandType CommandType => CommandType.SetBlendStateAdvanced;
private AdvancedBlendDescriptor _blend;
public void Set(AdvancedBlendDescriptor blend)
{
_blend = blend;
}
public static void Run(ref SetBlendStateAdvancedCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.SetBlendState(command._blend);
}
}
}

View File

@@ -131,6 +131,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetBlendState(AdvancedBlendDescriptor blend)
{
_renderer.New<SetBlendStateAdvancedCommand>().Set(blend);
_renderer.QueueCommand();
}
public void SetBlendState(int index, BlendDescriptor blend) public void SetBlendState(int index, BlendDescriptor blend)
{ {
_renderer.New<SetBlendStateCommand>().Set(index, blend); _renderer.New<SetBlendStateCommand>().Set(index, blend);

View File

@@ -197,7 +197,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
if (target != null) if (target != null)
{ {
target.SynchronizeMemory();
target.SetData(data, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount)); target.SetData(data, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount));
target.SignalModified();
return; return;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
{
/// <summary>
/// Advanced blend manager.
/// </summary>
class AdvancedBlendManager
{
private const int InstructionRamSize = 128;
private const int InstructionRamSizeMask = InstructionRamSize - 1;
private readonly DeviceStateWithShadow<ThreedClassState> _state;
private readonly uint[] _code;
private int _ip;
/// <summary>
/// Creates a new instance of the advanced blend manager.
/// </summary>
/// <param name="state">GPU state of the channel owning this manager</param>
public AdvancedBlendManager(DeviceStateWithShadow<ThreedClassState> state)
{
_state = state;
_code = new uint[InstructionRamSize];
}
/// <summary>
/// Sets the start offset of the blend microcode in memory.
/// </summary>
/// <param name="argument">Method call argument</param>
public void LoadBlendUcodeStart(int argument)
{
_ip = argument;
}
/// <summary>
/// Pushes one word of blend microcode.
/// </summary>
/// <param name="argument">Method call argument</param>
public void LoadBlendUcodeInstruction(int argument)
{
_code[_ip++ & InstructionRamSizeMask] = (uint)argument;
}
/// <summary>
/// Tries to identify the current advanced blend function being used,
/// given the current state and microcode that was uploaded.
/// </summary>
/// <param name="descriptor">Advanced blend descriptor</param>
/// <returns>True if the function was found, false otherwise</returns>
public bool TryGetAdvancedBlend(out AdvancedBlendDescriptor descriptor)
{
Span<uint> currentCode = new Span<uint>(_code);
byte codeLength = (byte)_state.State.BlendUcodeSize;
if (currentCode.Length > codeLength)
{
currentCode = currentCode.Slice(0, codeLength);
}
Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast<uint, byte>(currentCode));
descriptor = default;
if (!AdvancedBlendPreGenTable.Entries.TryGetValue(hash, out var entry))
{
return false;
}
if (entry.Constants != null)
{
bool constantsMatch = true;
for (int i = 0; i < entry.Constants.Length; i++)
{
RgbFloat constant = entry.Constants[i];
RgbHalf constant2 = _state.State.BlendUcodeConstants[i];
if ((Half)constant.R != constant2.UnpackR() ||
(Half)constant.G != constant2.UnpackG() ||
(Half)constant.B != constant2.UnpackB())
{
constantsMatch = false;
break;
}
}
if (!constantsMatch)
{
return false;
}
}
if (entry.Alpha.Enable != _state.State.BlendUcodeEnable)
{
return false;
}
if (entry.Alpha.Enable == BlendUcodeEnable.EnableRGBA &&
(entry.Alpha.AlphaOp != _state.State.BlendStateCommon.AlphaOp ||
entry.Alpha.AlphaSrcFactor != _state.State.BlendStateCommon.AlphaSrcFactor ||
entry.Alpha.AlphaDstFactor != _state.State.BlendStateCommon.AlphaDstFactor))
{
return false;
}
descriptor = new AdvancedBlendDescriptor(entry.Op, entry.Overlap, entry.SrcPreMultiplied);
return true;
}
}
}

View File

@@ -0,0 +1,273 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
{
/// <summary>
/// Advanced blend function entry.
/// </summary>
struct AdvancedBlendEntry
{
/// <summary>
/// Advanced blend operation.
/// </summary>
public AdvancedBlendOp Op { get; }
/// <summary>
/// Advanced blend overlap mode.
/// </summary>
public AdvancedBlendOverlap Overlap { get; }
/// <summary>
/// Whenever the source input is pre-multiplied.
/// </summary>
public bool SrcPreMultiplied { get; }
/// <summary>
/// Constants used by the microcode.
/// </summary>
public RgbFloat[] Constants { get; }
/// <summary>
/// Fixed function alpha state.
/// </summary>
public FixedFunctionAlpha Alpha { get; }
/// <summary>
/// Creates a new advanced blend function entry.
/// </summary>
/// <param name="op">Advanced blend operation</param>
/// <param name="overlap">Advanced blend overlap mode</param>
/// <param name="srcPreMultiplied">Whenever the source input is pre-multiplied</param>
/// <param name="constants">Constants used by the microcode</param>
/// <param name="alpha">Fixed function alpha state</param>
public AdvancedBlendEntry(
AdvancedBlendOp op,
AdvancedBlendOverlap overlap,
bool srcPreMultiplied,
RgbFloat[] constants,
FixedFunctionAlpha alpha)
{
Op = op;
Overlap = overlap;
SrcPreMultiplied = srcPreMultiplied;
Constants = constants;
Alpha = alpha;
}
}
/// <summary>
/// Pre-generated hash table with advanced blend functions used by the driver.
/// </summary>
static class AdvancedBlendPreGenTable
{
/// <summary>
/// Advanced blend functions dictionary.
/// </summary>
public static readonly IReadOnlyDictionary<Hash128, AdvancedBlendEntry> Entries = new Dictionary<Hash128, AdvancedBlendEntry>()
{
{ new Hash128(0x19ECF57B83DE31F7, 0x5BAE759246F264C0), new AdvancedBlendEntry(AdvancedBlendOp.PlusClamped, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xDE1B14A356A1A9ED, 0x59D803593C607C1D), new AdvancedBlendEntry(AdvancedBlendOp.PlusClampedAlpha, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x1A3C3A6D32DEC368, 0xBCAE519EC6AAA045), new AdvancedBlendEntry(AdvancedBlendOp.PlusDarker, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x6FD380261A63B240, 0x17C3B335DBB9E3DB), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x1D39164823D3A2D1, 0xC45350959CE1C8FB), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x18DF09FF53B129FE, 0xC02EDA33C36019F6), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x5973E583271EBF06, 0x711497D75D1272E0), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x4759E0E5DA54D5E8, 0x1FDD57C0C38AFA1F), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x337684D43CCE97FA, 0x0139E30CC529E1C9), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xDA59E85D8428992D, 0x1D3D7C64C9EF0132), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x9455B949298CE805, 0xE73D3301518BE98A), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xBDD3B4DEDBE336AA, 0xBFA4DCD50D535DEE), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x22D4E970A028649A, 0x4F3FCB055FCED965), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xA346A91311D72114, 0x151A27A3FB0A1904), new AdvancedBlendEntry(AdvancedBlendOp.Minus, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.ReverseSubtractGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x8A307241061FACD6, 0xA39D1826440B8EE7), new AdvancedBlendEntry(AdvancedBlendOp.MinusClamped, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xB3BE569485EFFFE0, 0x0BA4E269B3CFB165), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x36FCA3277DC11822, 0x2BC0F6CAC2029672), new AdvancedBlendEntry(AdvancedBlendOp.Contrast, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(2f, 2f, 2f), new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x4A6226AF2DE9BD7F, 0xEB890D7DA716F73A), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0xF364CAA94E160FEB, 0xBF364512C72A3797), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x6BF791AB4AC19C87, 0x6FA17A994EA0FCDE), new AdvancedBlendEntry(AdvancedBlendOp.InvertOvg, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x053C75A0AE0BB222, 0x03C791FEEB59754C), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x25762AB40B6CBDE9, 0x595E9A968AC4F01C), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xC2D05E2DBE16955D, 0xB8659C7A3FCFA7CE), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x223F220B8F74CBFB, 0xD3DD19D7C39209A5), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xD0DAE57A9F1FE78A, 0x353796BCFB8CE30B), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x601C8CBEC07FF8FF, 0xB8E22882360E8695), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x3A55B7B78C76A7A8, 0x206F503B2D9FFEAA), new AdvancedBlendEntry(AdvancedBlendOp.Red, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x80BC65C7831388E5, 0xC652457B2C766AEC), new AdvancedBlendEntry(AdvancedBlendOp.Green, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x3D3A912E5833EE13, 0x307895951349EE33), new AdvancedBlendEntry(AdvancedBlendOp.Blue, AdvancedBlendOverlap.Uncorrelated, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x289105BE92E81803, 0xFD8F1F03D15C53B4), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x007AE3BD140764EB, 0x0EE05A0D2E80BBAE), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x77F7EE0DB3FDDB96, 0xDEA47C881306DB3E), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x66F4E9A7D73CA157, 0x1486058A177DB11C), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Uncorrelated, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x593E9F331612D618, 0x9D217BEFA4EB919A), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0x0A5194C5E6891106, 0xDD8EC6586106557C), new AdvancedBlendEntry(AdvancedBlendOp.Dst, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x8D77173D5E06E916, 0x06AB190E7D10F4D4), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x655B4EBC148981DA, 0x455999EF2B9BD28A), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x98F5437D5F518929, 0xBFF4A6E83183DB63), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x6ADDEFE3B9CEF2FD, 0xB6F6272AFECB1AAB), new AdvancedBlendEntry(AdvancedBlendOp.DstIn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x80953F0953BF05B1, 0xD59ABFAA34F8196F), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xA401D9AA2A39C121, 0xFC0C8005C22AD7E3), new AdvancedBlendEntry(AdvancedBlendOp.DstOut, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x06274FB7CA9CDD22, 0x6CE8188B1A9AB6EF), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x0B079BE7F7F70817, 0xB72E7736CA51E321), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0x66215C99403CEDDE, 0x900B733D62204C48), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x12DEF2AD900CAD6C, 0x58CF5CC3004910DF), new AdvancedBlendEntry(AdvancedBlendOp.Plus, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x272BA3A49F64DAE4, 0xAC70B96C00A99EAF), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x206C34AAA7D3F545, 0xDA4B30CACAA483A0), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x3D93494920D257BE, 0xDCC573BE1F5F4449), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x0D7417D80191107B, 0xEAF40547827E005F), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xEC1B03E8C883F9C9, 0x2D3CA044C58C01B4), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x58A19A0135D68B31, 0x82F35B97AED068E5), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x20489F9AB36CC0E3, 0x20499874219E35EE), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xBB176935E5EE05BF, 0x95B26D4D30EA7A14), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x5FF9393C908ACFED, 0x068B0BD875773ABF), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x03181F8711C9802C, 0x6B02C7C6B224FE7B), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x2EE2209021F6B977, 0xF3AFA1491B8B89FC), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xD8BA4DD2EDE4DC9E, 0x01006114977CF715), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0xD156B99835A2D8ED, 0x2D0BEE9E135EA7A7), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x20CE8C898ED4BE27, 0x1514900B6F5E8F66), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xCDE5F743820BA2D9, 0x917845FE2ECB083D), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xEB03DF4A0C1D14CD, 0xBAE2E831C6E8FFE4), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x1DC9E49AABC779AC, 0x4053A1441EB713D3), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xFBDEF776248F7B3E, 0xE05EEFD65AC47CB7), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x415A1A48E03AA6E7, 0x046D7EE33CA46B9A), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Disjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x59A6901EC9BB2041, 0x2F3E19CE5EEC3EBE), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x044B2B6E105221DA, 0x3089BBC033F994AF), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x374A5A24AA8E6CC5, 0x29930FAA6215FA2B), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x30CD0F7AF0CF26F9, 0x06CCA6744DE7DCF5), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Disjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x1A6C9A1F6FE494A5, 0xA0CFAF77617E54DD), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0x081AF6DAAB1C8717, 0xBFEDCE59AE3DC9AC), new AdvancedBlendEntry(AdvancedBlendOp.Dst, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x3518E44573AB68BA, 0xC96EE71AF9F8F546), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xF89E81FE8D73C96F, 0x4583A04577A0F21C), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xDF4026421CB61119, 0x14115A1F5139AFC7), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x91A20262C3E3A695, 0x0B3A102BFCDC6B1C), new AdvancedBlendEntry(AdvancedBlendOp.DstIn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x44F4C7CCFEB9EBFA, 0xF68394E6D56E5C2F), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xB89F17C7021E9760, 0x430357EE0F7188EF), new AdvancedBlendEntry(AdvancedBlendOp.DstOut, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xDA2D20EA4242B8A0, 0x0D1EC05B72E3838F), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x855DFEE1208D11B9, 0x77C6E3DDCFE30B85), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0x9B3808439683FD58, 0x123DCBE4705AB25E), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xA42CF045C248A00A, 0x0C6C63C24EA0B0C1), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x320A83B6D00C8059, 0x796EDAB3EB7314BC), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x45253AC9ABFFC613, 0x8F92EA70195FB573), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x1A5D263B588274B6, 0x167D305F6C794179), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x709C1A837FE966AC, 0x75D8CE49E8A78EDB), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x8265C26F85E4145F, 0x932E6CCBF37CB600), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x3F252B3FEF983F27, 0x9370D7EEFEFA1A9E), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x66A334A4AEA41078, 0xCB52254E1E395231), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xFDD05C53B25F0035, 0xB7E3ECEE166C222F), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x25D932A77FFED81A, 0xA50D797B0FCA94E8), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x4A953B6F5F7D341C, 0xDC05CFB50DDB5DC1), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x838CB660C4F41F6D, 0x9E7D958697543495), new AdvancedBlendEntry(AdvancedBlendOp.Invert, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x4DF6EC1348A8F797, 0xA128E0CD69DB5A64), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x178CDFAB9A015295, 0x2BF40EA72E596D57), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x338FC99050E56AFD, 0x2AF41CF82BE602BF), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x62E02ED60D1E978E, 0xBF726B3E68C11E4D), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xFBAF92DD4C101502, 0x7AF2EDA6596B819D), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x0EF1241F65D4B50A, 0xE8D85DFA6AEDDB84), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x77FE024B5C9D4A18, 0xF19D48A932F6860F), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Conjoint, true, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x9C88CBFA2E09D857, 0x0A0361704CBEEE1D), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x5B94127FA190E640, 0x8D1FEFF837A91268), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xB9C9105B7E063DDB, 0xF6A70E1D511B96FD), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xF0751AAE332B3ED1, 0xC40146F5C83C2533), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, true, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x579EB12F595F75AD, 0x151BF0504703B81B), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xF9CA152C03AC8C62, 0x1581336205E5CF47), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.DstAlphaGl, BlendFactor.ZeroGl)) },
{ new Hash128(0x98ACD8BB5E195D0F, 0x91F937672BE899F0), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneMinusDstAlphaGl, BlendFactor.ZeroGl)) },
{ new Hash128(0xBF97F10FC301F44C, 0x75721789F0D48548), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x1B982263B8B08A10, 0x3350C76E2E1B27DF), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0xFF20AC79F64EDED8, 0xAF9025B2D97B9273), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneMinusDstAlphaGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x9FFD986600FB112F, 0x384FDDF4E060139A), new AdvancedBlendEntry(AdvancedBlendOp.PlusClamped, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x0425E40B5B8B3B52, 0x5880CBED7CAB631C), new AdvancedBlendEntry(AdvancedBlendOp.PlusClampedAlpha, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x16DAC8593F28623A, 0x233DBC82325B8AED), new AdvancedBlendEntry(AdvancedBlendOp.PlusDarker, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xB37E5F234B9F0948, 0xD5F957A2ECD98FD6), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xCA0FDADD1D20DBE3, 0x1A5C15CCBF1AC538), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x1C48304D73A9DF3A, 0x891DB93FA36E3450), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x53200F2279B7FA39, 0x051C2462EBF6789C), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xB88BFB80714DCD5C, 0xEBD6938D744E6A41), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xE33DC2A25FC1A976, 0x08B3DBB1F3027D45), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xCE97E71615370316, 0xE131AE49D3A4D62B), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xE059FD265149B256, 0x94AF817AC348F61F), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x16D31333D477E231, 0x9A98AAC84F72CC62), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x47FC3B0776366D3C, 0xE96D9BD83B277874), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x7230401E3FEA1F3B, 0xF0D15F05D3D1E309), new AdvancedBlendEntry(AdvancedBlendOp.Minus, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.ReverseSubtractGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x188212F9303742F5, 0x100C51CB96E03591), new AdvancedBlendEntry(AdvancedBlendOp.MinusClamped, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x52B755D296B44DC5, 0x4003B87275625973), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xD873ED973ADF7EAD, 0x73E68B57D92034E7), new AdvancedBlendEntry(AdvancedBlendOp.Contrast, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(2f, 2f, 2f), new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x471F9FA34B945ACB, 0x10524D1410B3C402), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x99F569454EA0EF32, 0x6FC70A8B3A07DC8B), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x5AD55F950067AC7E, 0x4BA60A4FBABDD0AC), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x03FF2C858C9C4C5B, 0xE95AE7F561FB60E9), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x6DC0E510C7BCF9D2, 0xAE805D7CECDCB5C1), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x44832332CED5C054, 0x2F8D5536C085B30A), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x4AB4D387618AC51F, 0x495B46E0555F4B32), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x99282B49405A01A8, 0xD6FA93F864F24A8E), new AdvancedBlendEntry(AdvancedBlendOp.Red, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x37B30C1064FBD23E, 0x5D068366F42317C2), new AdvancedBlendEntry(AdvancedBlendOp.Green, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x760FAE9D59E04BC2, 0xA40AD483EA01435E), new AdvancedBlendEntry(AdvancedBlendOp.Blue, AdvancedBlendOverlap.Uncorrelated, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0xE786950FD9D1C6EF, 0xF9FDD5AF6451D239), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x052458BB4788B0CA, 0x8AC58FDCA1F45EF5), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x6AFC3837D1D31920, 0xB9D49C2FE49642C6), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0xAFC2911949317E01, 0xD5B63636F5CB3422), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Uncorrelated, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneMinusSrcAlphaGl)) },
{ new Hash128(0x13B46DF507CC2C53, 0x86DE26517E6BF0A7), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0x5C372442474BE410, 0x79ECD3C0C496EF2E), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x74AAB45DBF5336E9, 0x01BFC4E181DAD442), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x43239E282A36C85C, 0x36FB65560E46AD0F), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x1A3BA8A7583B8F7A, 0xE64E41D548033180), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x32BBB9859E9B565D, 0x3D5CE94FE55F18B5), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0xD947A0766AE3C0FC, 0x391E5D53E86F4ED6), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0xBD9A7C08BDFD8CE6, 0x905407634901355E), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x8395475BCB0D7A8C, 0x48AF5DD501D44A70), new AdvancedBlendEntry(AdvancedBlendOp.Plus, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x80AAC23FEBD4A3E5, 0xEA8C70F0B4DE52DE), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x2F3AD1B0F1B3FD09, 0xC0EBC784BFAB8EA3), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x52B54032F2F70BFF, 0xC941D6FDED674765), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xCA7B86F72EC6A99B, 0x55868A131AFE359E), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x377919B60BD133CA, 0x0FD611627664EF40), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x9D4A0C5EE1153887, 0x7B869EBA218C589B), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x311F2A858545D123, 0xB4D09C802480AD62), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xCF78AA6A83AFA689, 0x9DC48B0C2182A3E1), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xC3018CD6F1CF62D1, 0x016E32DD9087B1BB), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x9CB62CE0E956EE29, 0x0FB67F503E60B3AD), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x3589A13C16EF3BFA, 0x15B29BFC91F3BDFB), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x3502CA5FB7529917, 0xFA51BFD0D1688071), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x62ADC25AD6D0A923, 0x76CB6D238276D3A3), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x09FDEB1116A9D52C, 0x85BB8627CD5C2733), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x0709FED1B65E18EB, 0x5BC3AA4D99EC19CF), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xB18D28AE5DE4C723, 0xE820AA2B75C9C02E), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x6743C51621497480, 0x4B164E40858834AE), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x63D1E181E34A2944, 0x1AE292C9D9F12819), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Disjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x079523298250BFF6, 0xC0C793510603CDB5), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x4C9D0A973C805EA6, 0xD1FF59AD5156B93C), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x1E914678F3057BCD, 0xD503AE389C12D229), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0x9FDBADE5556C5311, 0x03F0CBC798FC5C94), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Disjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xE39451534635403C, 0x606CC1CA1F452388), new AdvancedBlendEntry(AdvancedBlendOp.Src, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0x1D39F0F0A1008AA6, 0xBFDF2B97E6C3F125), new AdvancedBlendEntry(AdvancedBlendOp.SrcOver, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xDB81BED30D5BDBEA, 0xAF0B2856EB93AD2C), new AdvancedBlendEntry(AdvancedBlendOp.DstOver, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x83F69CCF1D0A79B6, 0x70D31332797430AC), new AdvancedBlendEntry(AdvancedBlendOp.SrcIn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MinimumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x7B87F807AB7A8F5C, 0x1241A2A01FB31771), new AdvancedBlendEntry(AdvancedBlendOp.SrcOut, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xF557172E20D5272D, 0xC1961F8C7A5D2820), new AdvancedBlendEntry(AdvancedBlendOp.SrcAtop, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0xA8476B3944DBBC9B, 0x84A2F6AF97B15FDF), new AdvancedBlendEntry(AdvancedBlendOp.DstAtop, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.OneGl, BlendFactor.ZeroGl)) },
{ new Hash128(0x3259602B55414DA3, 0x72AACCC00B5A9D10), new AdvancedBlendEntry(AdvancedBlendOp.Xor, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, 0, 0, 0)) },
{ new Hash128(0xC0CB8C10F36EDCD6, 0x8C2D088AD8191E1C), new AdvancedBlendEntry(AdvancedBlendOp.Multiply, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x81806C451C6255EF, 0x5AA8AC9A08941A15), new AdvancedBlendEntry(AdvancedBlendOp.Screen, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xE55A6537F4568198, 0xCA8735390B799B19), new AdvancedBlendEntry(AdvancedBlendOp.Overlay, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x5C044BA14536DDA3, 0xBCE0123ED7D510EC), new AdvancedBlendEntry(AdvancedBlendOp.Darken, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x6788346C405BE130, 0x372A4BB199C01F9F), new AdvancedBlendEntry(AdvancedBlendOp.Lighten, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x510EDC2A34E2856B, 0xE1727A407E294254), new AdvancedBlendEntry(AdvancedBlendOp.ColorDodge, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x4B7BE01BD398C7A8, 0x5BFF79BC00672C18), new AdvancedBlendEntry(AdvancedBlendOp.ColorBurn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x213B43845540CFEC, 0xDA857411CF1CCFCE), new AdvancedBlendEntry(AdvancedBlendOp.HardLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x765AFA6732E783F1, 0x8F1CABF1BC78A014), new AdvancedBlendEntry(AdvancedBlendOp.SoftLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.2605f, 0.2605f, 0.2605f), new RgbFloat(-0.7817f, -0.7817f, -0.7817f), new RgbFloat(0.3022f, 0.3022f, 0.3022f), new RgbFloat(0.2192f, 0.2192f, 0.2192f), new RgbFloat(0.25f, 0.25f, 0.25f), new RgbFloat(16f, 16f, 16f), new RgbFloat(12f, 12f, 12f), new RgbFloat(3f, 3f, 3f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xA4A5DE1CC06F6CB1, 0xA0634A0011001709), new AdvancedBlendEntry(AdvancedBlendOp.Difference, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x81F32BD8816EA796, 0x697EE86683165170), new AdvancedBlendEntry(AdvancedBlendOp.Exclusion, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xB870C209EAA5F092, 0xAF5FD923909CAA1F), new AdvancedBlendEntry(AdvancedBlendOp.InvertRGB, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.AddGl, BlendFactor.ZeroGl, BlendFactor.OneGl)) },
{ new Hash128(0x3649A9F5C936FB83, 0xDD7C834897AA182A), new AdvancedBlendEntry(AdvancedBlendOp.LinearDodge, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xD72A2B1097A5995C, 0x3D41B2763A913654), new AdvancedBlendEntry(AdvancedBlendOp.LinearBurn, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x551E212B9F6C454A, 0xB0DFA05BEB3C37FA), new AdvancedBlendEntry(AdvancedBlendOp.VividLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.5f, 0.5f, 0.5f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x681B5A313B7416BF, 0xCB1CBAEEB4D81500), new AdvancedBlendEntry(AdvancedBlendOp.LinearLight, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(2f, 2f, 2f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x9343A18BD4B16777, 0xEDB4AC1C8972C3A4), new AdvancedBlendEntry(AdvancedBlendOp.PinLight, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xC960BF6D8519DE28, 0x78D8557FD405D119), new AdvancedBlendEntry(AdvancedBlendOp.HardMix, AdvancedBlendOverlap.Conjoint, false, Array.Empty<RgbFloat>(), new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x65A7B01FDC73A46C, 0x297E096ED5CC4D8A), new AdvancedBlendEntry(AdvancedBlendOp.HslHue, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0xD9C99BA4A6CDC13B, 0x3CFF0ACEDC2EE150), new AdvancedBlendEntry(AdvancedBlendOp.HslSaturation, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x6BC00DA6EB922BD1, 0x5FD4C11F2A685234), new AdvancedBlendEntry(AdvancedBlendOp.HslColor, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
{ new Hash128(0x8652300E32D93050, 0x9460E7B449132371), new AdvancedBlendEntry(AdvancedBlendOp.HslLuminosity, AdvancedBlendOverlap.Conjoint, false, new[] { new RgbFloat(0.3f, 0.59f, 0.11f) }, new FixedFunctionAlpha(BlendUcodeEnable.EnableRGB, BlendOp.MaximumGl, BlendFactor.OneGl, BlendFactor.OneGl)) },
};
}
}

View File

@@ -0,0 +1,126 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
{
/// <summary>
/// Fixed function alpha state used for a advanced blend function.
/// </summary>
struct FixedFunctionAlpha
{
/// <summary>
/// Fixed function alpha state with alpha blending disabled.
/// </summary>
public static FixedFunctionAlpha Disabled => new FixedFunctionAlpha(BlendUcodeEnable.EnableRGBA, default, default, default);
/// <summary>
/// Individual enable bits for the RGB and alpha components.
/// </summary>
public BlendUcodeEnable Enable { get; }
/// <summary>
/// Alpha blend operation.
/// </summary>
public BlendOp AlphaOp { get; }
/// <summary>
/// Value multiplied with the blend source operand.
/// </summary>
public BlendFactor AlphaSrcFactor { get; }
/// <summary>
/// Value multiplied with the blend destination operand.
/// </summary>
public BlendFactor AlphaDstFactor { get; }
/// <summary>
/// Creates a new blend fixed function alpha state.
/// </summary>
/// <param name="enable">Individual enable bits for the RGB and alpha components</param>
/// <param name="alphaOp">Alpha blend operation</param>
/// <param name="alphaSrc">Value multiplied with the blend source operand</param>
/// <param name="alphaDst">Value multiplied with the blend destination operand</param>
public FixedFunctionAlpha(BlendUcodeEnable enable, BlendOp alphaOp, BlendFactor alphaSrc, BlendFactor alphaDst)
{
Enable = enable;
AlphaOp = alphaOp;
AlphaSrcFactor = alphaSrc;
AlphaDstFactor = alphaDst;
}
/// <summary>
/// Creates a new blend fixed function alpha state.
/// </summary>
/// <param name="alphaOp">Alpha blend operation</param>
/// <param name="alphaSrc">Value multiplied with the blend source operand</param>
/// <param name="alphaDst">Value multiplied with the blend destination operand</param>
public FixedFunctionAlpha(BlendOp alphaOp, BlendFactor alphaSrc, BlendFactor alphaDst) : this(BlendUcodeEnable.EnableRGB, alphaOp, alphaSrc, alphaDst)
{
}
}
/// <summary>
/// Blend microcode assembly function delegate.
/// </summary>
/// <param name="asm">Assembler</param>
/// <returns>Fixed function alpha state for the microcode</returns>
delegate FixedFunctionAlpha GenUcodeFunc(ref UcodeAssembler asm);
/// <summary>
/// Advanced blend microcode state.
/// </summary>
struct AdvancedBlendUcode
{
/// <summary>
/// Advanced blend operation.
/// </summary>
public AdvancedBlendOp Op { get; }
/// <summary>
/// Advanced blend overlap mode.
/// </summary>
public AdvancedBlendOverlap Overlap { get; }
/// <summary>
/// Whenever the source input is pre-multiplied.
/// </summary>
public bool SrcPreMultiplied { get; }
/// <summary>
/// Fixed function alpha state.
/// </summary>
public FixedFunctionAlpha Alpha { get; }
/// <summary>
/// Microcode.
/// </summary>
public uint[] Code { get; }
/// <summary>
/// Constants used by the microcode.
/// </summary>
public RgbFloat[] Constants { get; }
/// <summary>
/// Creates a new advanced blend state.
/// </summary>
/// <param name="op">Advanced blend operation</param>
/// <param name="overlap">Advanced blend overlap mode</param>
/// <param name="srcPreMultiplied">Whenever the source input is pre-multiplied</param>
/// <param name="genFunc">Function that will generate the advanced blend microcode</param>
public AdvancedBlendUcode(
AdvancedBlendOp op,
AdvancedBlendOverlap overlap,
bool srcPreMultiplied,
GenUcodeFunc genFunc)
{
Op = op;
Overlap = overlap;
SrcPreMultiplied = srcPreMultiplied;
UcodeAssembler asm = new UcodeAssembler();
Alpha = genFunc(ref asm);
Code = asm.GetCode();
Constants = asm.GetConstants();
}
}
}

View File

@@ -0,0 +1,305 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
{
/// <summary>
/// Blend microcode instruction.
/// </summary>
enum Instruction
{
Mmadd = 0,
Mmsub = 1,
Min = 2,
Max = 3,
Rcp = 4,
Add = 5,
Sub = 6
}
/// <summary>
/// Blend microcode condition code.
/// </summary>
enum CC
{
F = 0,
T = 1,
EQ = 2,
NE = 3,
LT = 4,
LE = 5,
GT = 6,
GE = 7
}
/// <summary>
/// Blend microcode opend B or D value.
/// </summary>
enum OpBD
{
ConstantZero = 0x0,
ConstantOne = 0x1,
SrcRGB = 0x2,
SrcAAA = 0x3,
OneMinusSrcAAA = 0x4,
DstRGB = 0x5,
DstAAA = 0x6,
OneMinusDstAAA = 0x7,
Temp0 = 0x9,
Temp1 = 0xa,
Temp2 = 0xb,
PBR = 0xc,
ConstantRGB = 0xd
}
/// <summary>
/// Blend microcode operand A or C value.
/// </summary>
enum OpAC
{
SrcRGB = 0,
DstRGB = 1,
SrcAAA = 2,
DstAAA = 3,
Temp0 = 4,
Temp1 = 5,
Temp2 = 6,
PBR = 7
}
/// <summary>
/// Blend microcode destination operand.
/// </summary>
enum OpDst
{
Temp0 = 0,
Temp1 = 1,
Temp2 = 2,
PBR = 3
}
/// <summary>
/// Blend microcode input swizzle.
/// </summary>
enum Swizzle
{
RGB = 0,
GBR = 1,
RRR = 2,
GGG = 3,
BBB = 4,
RToA = 5
}
/// <summary>
/// Blend microcode output components.
/// </summary>
enum WriteMask
{
RGB = 0,
R = 1,
G = 2,
B = 3
}
/// <summary>
/// Floating-point RGB color values.
/// </summary>
struct RgbFloat
{
/// <summary>
/// Red component value.
/// </summary>
public float R { get; }
/// <summary>
/// Green component value.
/// </summary>
public float G { get; }
/// <summary>
/// Blue component value.
/// </summary>
public float B { get; }
/// <summary>
/// Creates a new floating-point RGB value.
/// </summary>
/// <param name="r">Red component value</param>
/// <param name="g">Green component value</param>
/// <param name="b">Blue component value</param>
public RgbFloat(float r, float g, float b)
{
R = r;
G = g;
B = b;
}
}
/// <summary>
/// Blend microcode destination operand, including swizzle, write mask and condition code update flag.
/// </summary>
struct Dest
{
public static Dest Temp0 => new Dest(OpDst.Temp0, Swizzle.RGB, WriteMask.RGB, false);
public static Dest Temp1 => new Dest(OpDst.Temp1, Swizzle.RGB, WriteMask.RGB, false);
public static Dest Temp2 => new Dest(OpDst.Temp2, Swizzle.RGB, WriteMask.RGB, false);
public static Dest PBR => new Dest(OpDst.PBR, Swizzle.RGB, WriteMask.RGB, false);
public Dest GBR => new Dest(Dst, Swizzle.GBR, WriteMask, WriteCC);
public Dest RRR => new Dest(Dst, Swizzle.RRR, WriteMask, WriteCC);
public Dest GGG => new Dest(Dst, Swizzle.GGG, WriteMask, WriteCC);
public Dest BBB => new Dest(Dst, Swizzle.BBB, WriteMask, WriteCC);
public Dest RToA => new Dest(Dst, Swizzle.RToA, WriteMask, WriteCC);
public Dest R => new Dest(Dst, Swizzle, WriteMask.R, WriteCC);
public Dest G => new Dest(Dst, Swizzle, WriteMask.G, WriteCC);
public Dest B => new Dest(Dst, Swizzle, WriteMask.B, WriteCC);
public Dest CC => new Dest(Dst, Swizzle, WriteMask, true);
public OpDst Dst { get; }
public Swizzle Swizzle { get; }
public WriteMask WriteMask { get; }
public bool WriteCC { get; }
/// <summary>
/// Creates a new blend microcode destination operand.
/// </summary>
/// <param name="dst">Operand</param>
/// <param name="swizzle">Swizzle</param>
/// <param name="writeMask">Write maks</param>
/// <param name="writeCC">Indicates if condition codes should be updated</param>
public Dest(OpDst dst, Swizzle swizzle, WriteMask writeMask, bool writeCC)
{
Dst = dst;
Swizzle = swizzle;
WriteMask = writeMask;
WriteCC = writeCC;
}
}
/// <summary>
/// Blend microcode operaiton.
/// </summary>
struct UcodeOp
{
public readonly uint Word;
/// <summary>
/// Creates a new blend microcode operation.
/// </summary>
/// <param name="cc">Condition code that controls whenever the operation is executed or not</param>
/// <param name="inst">Instruction</param>
/// <param name="constIndex">Index on the constant table of the constant used by any constant operand</param>
/// <param name="dest">Destination operand</param>
/// <param name="srcA">First input operand</param>
/// <param name="srcB">Second input operand</param>
/// <param name="srcC">Third input operand</param>
/// <param name="srcD">Fourth input operand</param>
public UcodeOp(CC cc, Instruction inst, int constIndex, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD)
{
Word = (uint)cc |
((uint)inst << 3) |
((uint)constIndex << 6) |
((uint)srcA << 9) |
((uint)srcB << 12) |
((uint)srcC << 16) |
((uint)srcD << 19) |
((uint)dest.Swizzle << 23) |
((uint)dest.WriteMask << 26) |
((uint)dest.Dst << 28) |
(dest.WriteCC ? (1u << 31) : 0);
}
}
/// <summary>
/// Blend microcode assembler.
/// </summary>
struct UcodeAssembler
{
private List<uint> _code;
private RgbFloat[] _constants;
private int _constantIndex;
public void Mul(CC cc, Dest dest, OpAC srcA, OpBD srcB)
{
Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero);
}
public void Madd(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC)
{
Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, srcC, OpBD.ConstantOne);
}
public void Mmadd(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD)
{
Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, srcC, srcD);
}
public void Mmsub(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD)
{
Assemble(cc, Instruction.Mmsub, dest, srcA, srcB, srcC, srcD);
}
public void Min(CC cc, Dest dest, OpAC srcA, OpBD srcB)
{
Assemble(cc, Instruction.Min, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero);
}
public void Max(CC cc, Dest dest, OpAC srcA, OpBD srcB)
{
Assemble(cc, Instruction.Max, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero);
}
public void Rcp(CC cc, Dest dest, OpAC srcA)
{
Assemble(cc, Instruction.Rcp, dest, srcA, OpBD.ConstantZero, OpAC.SrcRGB, OpBD.ConstantZero);
}
public void Mov(CC cc, Dest dest, OpBD srcB)
{
Assemble(cc, Instruction.Add, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, OpBD.ConstantZero);
}
public void Add(CC cc, Dest dest, OpBD srcB, OpBD srcD)
{
Assemble(cc, Instruction.Add, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, srcD);
}
public void Sub(CC cc, Dest dest, OpBD srcB, OpBD srcD)
{
Assemble(cc, Instruction.Sub, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, srcD);
}
private void Assemble(CC cc, Instruction inst, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD)
{
(_code ??= new List<uint>()).Add(new UcodeOp(cc, inst, _constantIndex, dest, srcA, srcB, srcC, srcD).Word);
}
public void SetConstant(int index, float r, float g, float b)
{
if (_constants == null)
{
_constants = new RgbFloat[index + 1];
}
else if (_constants.Length <= index)
{
Array.Resize(ref _constants, index + 1);
}
_constants[index] = new RgbFloat(r, g, b);
_constantIndex = index;
}
public uint[] GetCode()
{
return _code?.ToArray();
}
public RgbFloat[] GetConstants()
{
return _constants;
}
}
}

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
@@ -26,6 +27,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
private readonly DeviceStateWithShadow<ThreedClassState> _state; private readonly DeviceStateWithShadow<ThreedClassState> _state;
private readonly DrawState _drawState; private readonly DrawState _drawState;
private readonly AdvancedBlendManager _blendManager;
private readonly StateUpdateTracker<ThreedClassState> _updateTracker; private readonly StateUpdateTracker<ThreedClassState> _updateTracker;
@@ -55,13 +57,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="state">3D engine state</param> /// <param name="state">3D engine state</param>
/// <param name="drawState">Draw state</param> /// <param name="drawState">Draw state</param>
/// <param name="blendManager">Advanced blend manager</param>
/// <param name="spec">Specialization state updater</param> /// <param name="spec">Specialization state updater</param>
public StateUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState, SpecializationStateUpdater spec) public StateUpdater(
GpuContext context,
GpuChannel channel,
DeviceStateWithShadow<ThreedClassState> state,
DrawState drawState,
AdvancedBlendManager blendManager,
SpecializationStateUpdater spec)
{ {
_context = context; _context = context;
_channel = channel; _channel = channel;
_state = state; _state = state;
_drawState = drawState; _drawState = drawState;
_blendManager = blendManager;
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages]; _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
_currentSpecState = spec; _currentSpecState = spec;
@@ -84,6 +94,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)), new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
new StateUpdateCallbackEntry(UpdateBlendState, new StateUpdateCallbackEntry(UpdateBlendState,
nameof(ThreedClassState.BlendUcodeEnable),
nameof(ThreedClassState.BlendUcodeSize),
nameof(ThreedClassState.BlendIndependent), nameof(ThreedClassState.BlendIndependent),
nameof(ThreedClassState.BlendConstant), nameof(ThreedClassState.BlendConstant),
nameof(ThreedClassState.BlendStateCommon), nameof(ThreedClassState.BlendStateCommon),
@@ -1154,6 +1166,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
private void UpdateBlendState() private void UpdateBlendState()
{ {
if (_state.State.BlendUcodeEnable != BlendUcodeEnable.Disabled)
{
if (_context.Capabilities.SupportsBlendEquationAdvanced && _blendManager.TryGetAdvancedBlend(out var blendDescriptor))
{
// Try to HLE it using advanced blend on the host if we can.
_context.Renderer.Pipeline.SetBlendState(blendDescriptor);
return;
}
else
{
// TODO: Blend emulation fallback.
}
}
bool blendIndependent = _state.State.BlendIndependent; bool blendIndependent = _state.State.BlendIndependent;
ColorF blendConstant = _state.State.BlendConstant; ColorF blendConstant = _state.State.BlendConstant;

View File

@@ -2,6 +2,7 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory; using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -18,6 +19,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private readonly DeviceStateWithShadow<ThreedClassState> _state; private readonly DeviceStateWithShadow<ThreedClassState> _state;
private readonly InlineToMemoryClass _i2mClass; private readonly InlineToMemoryClass _i2mClass;
private readonly AdvancedBlendManager _blendManager;
private readonly DrawManager _drawManager; private readonly DrawManager _drawManager;
private readonly SemaphoreUpdater _semaphoreUpdater; private readonly SemaphoreUpdater _semaphoreUpdater;
private readonly ConstantBufferUpdater _cbUpdater; private readonly ConstantBufferUpdater _cbUpdater;
@@ -40,6 +42,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ nameof(ThreedClassState.InvalidateSamplerCacheNoWfi), new RwCallback(InvalidateSamplerCacheNoWfi, null) }, { nameof(ThreedClassState.InvalidateSamplerCacheNoWfi), new RwCallback(InvalidateSamplerCacheNoWfi, null) },
{ nameof(ThreedClassState.InvalidateTextureHeaderCacheNoWfi), new RwCallback(InvalidateTextureHeaderCacheNoWfi, null) }, { nameof(ThreedClassState.InvalidateTextureHeaderCacheNoWfi), new RwCallback(InvalidateTextureHeaderCacheNoWfi, null) },
{ nameof(ThreedClassState.TextureBarrier), new RwCallback(TextureBarrier, null) }, { nameof(ThreedClassState.TextureBarrier), new RwCallback(TextureBarrier, null) },
{ nameof(ThreedClassState.LoadBlendUcodeStart), new RwCallback(LoadBlendUcodeStart, null) },
{ nameof(ThreedClassState.LoadBlendUcodeInstruction), new RwCallback(LoadBlendUcodeInstruction, null) },
{ nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) }, { nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) },
{ nameof(ThreedClassState.DrawTextureSrcY), new RwCallback(DrawTexture, null) }, { nameof(ThreedClassState.DrawTextureSrcY), new RwCallback(DrawTexture, null) },
{ nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceFirst), new RwCallback(DrawVertexArrayBeginEndInstanceFirst, null) }, { nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceFirst), new RwCallback(DrawVertexArrayBeginEndInstanceFirst, null) },
@@ -75,9 +79,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var drawState = new DrawState(); var drawState = new DrawState();
_drawManager = new DrawManager(context, channel, _state, drawState, spec); _drawManager = new DrawManager(context, channel, _state, drawState, spec);
_blendManager = new AdvancedBlendManager(_state);
_semaphoreUpdater = new SemaphoreUpdater(context, channel, _state); _semaphoreUpdater = new SemaphoreUpdater(context, channel, _state);
_cbUpdater = new ConstantBufferUpdater(channel, _state); _cbUpdater = new ConstantBufferUpdater(channel, _state);
_stateUpdater = new StateUpdater(context, channel, _state, drawState, spec); _stateUpdater = new StateUpdater(context, channel, _state, drawState, _blendManager, spec);
// This defaults to "always", even without any register write. // This defaults to "always", even without any register write.
// Reads just return 0, regardless of what was set there. // Reads just return 0, regardless of what was set there.
@@ -283,6 +288,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_context.Renderer.Pipeline.TextureBarrier(); _context.Renderer.Pipeline.TextureBarrier();
} }
/// <summary>
/// Sets the start offset of the blend microcode in memory.
/// </summary>
/// <param name="argument">Method call argument</param>
private void LoadBlendUcodeStart(int argument)
{
_blendManager.LoadBlendUcodeStart(argument);
}
/// <summary>
/// Pushes one word of blend microcode.
/// </summary>
/// <param name="argument">Method call argument</param>
private void LoadBlendUcodeInstruction(int argument)
{
_blendManager.LoadBlendUcodeInstruction(argument);
}
/// <summary> /// <summary>
/// Issues a texture barrier. /// Issues a texture barrier.
/// This waits until previous texture writes from the GPU to finish, before /// This waits until previous texture writes from the GPU to finish, before

View File

@@ -5,6 +5,7 @@ using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using System; using System;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Engine.Threed namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
@@ -214,6 +215,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
#pragma warning restore CS0649 #pragma warning restore CS0649
} }
/// <summary>
/// Indicates whenever the blend microcode processes RGB and alpha components.
/// </summary>
enum BlendUcodeEnable
{
Disabled = 0,
EnableRGB = 1,
EnableAlpha = 2,
EnableRGBA = 3
}
/// <summary> /// <summary>
/// Scissor state. /// Scissor state.
/// </summary> /// </summary>
@@ -434,6 +446,49 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
TriangleRastFlip = 1 << 4 TriangleRastFlip = 1 << 4
} }
/// <summary>
/// RGB color components packed as 16-bit float values.
/// </summary>
struct RgbHalf
{
#pragma warning disable CS0649
public uint R;
public uint G;
public uint B;
public uint Padding;
#pragma warning restore CS0649
/// <summary>
/// Unpacks the red color component as a 16-bit float value.
/// </summary>
/// <returns>The component value</returns>
public Half UnpackR()
{
ushort value = (ushort)R;
return Unsafe.As<ushort, Half>(ref value);
}
/// <summary>
/// Unpacks the green color component as a 16-bit float value.
/// </summary>
/// <returns>The component value</returns>
public Half UnpackG()
{
ushort value = (ushort)G;
return Unsafe.As<ushort, Half>(ref value);
}
/// <summary>
/// Unpacks the blue color component as a 16-bit float value.
/// </summary>
/// <returns>The component value</returns>
public Half UnpackB()
{
ushort value = (ushort)B;
return Unsafe.As<ushort, Half>(ref value);
}
}
/// <summary> /// <summary>
/// Condition for conditional rendering. /// Condition for conditional rendering.
/// </summary> /// </summary>
@@ -752,7 +807,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public Boolean32 EarlyZForce; public Boolean32 EarlyZForce;
public fixed uint Reserved214[45]; public fixed uint Reserved214[45];
public uint SyncpointAction; public uint SyncpointAction;
public fixed uint Reserved2CC[21]; public fixed uint Reserved2CC[10];
public uint BlendUcodeNormalizedDst;
public fixed uint Reserved2F8[10];
public TessMode TessMode; public TessMode TessMode;
public Array4<float> TessOuterLevel; public Array4<float> TessOuterLevel;
public Array2<float> TessInnerLevel; public Array2<float> TessInnerLevel;
@@ -781,11 +838,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public fixed uint ReservedDB8[2]; public fixed uint ReservedDB8[2];
public DepthBiasState DepthBiasState; public DepthBiasState DepthBiasState;
public int PatchVertices; public int PatchVertices;
public fixed uint ReservedDD0[4]; public BlendUcodeEnable BlendUcodeEnable;
public uint BlendUcodeSize;
public fixed uint ReservedDD8[2];
public uint TextureBarrier; public uint TextureBarrier;
public uint WatchdogTimer; public uint WatchdogTimer;
public Boolean32 PrimitiveRestartDrawArrays; public Boolean32 PrimitiveRestartDrawArrays;
public fixed uint ReservedDEC[5]; public uint ReservedDEC;
public uint LoadBlendUcodeStart;
public uint LoadBlendUcodeInstruction;
public fixed uint ReservedDF8[2];
public Array16<ScissorState> ScissorState; public Array16<ScissorState> ScissorState;
public fixed uint ReservedF00[21]; public fixed uint ReservedF00[21];
public StencilBackMasks StencilBackMasks; public StencilBackMasks StencilBackMasks;
@@ -850,7 +912,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public fixed uint Reserved142C[2]; public fixed uint Reserved142C[2];
public uint FirstVertex; public uint FirstVertex;
public uint FirstInstance; public uint FirstInstance;
public fixed uint Reserved143C[53]; public fixed uint Reserved143C[17];
public Array8<RgbHalf> BlendUcodeConstants;
public fixed uint Reserved1500[4];
public uint ClipDistanceEnable; public uint ClipDistanceEnable;
public uint Reserved1514; public uint Reserved1514;
public float PointSize; public float PointSize;

View File

@@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Address = address; Address = address;
Size = size; Size = size;
_memoryTracking = physicalMemory.BeginGranularTracking(address, size); _memoryTracking = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Pool);
_memoryTracking.RegisterPreciseAction(address, size, PreciseAction); _memoryTracking.RegisterPreciseAction(address, size, PreciseAction);
_modifiedDelegate = RegionModified; _modifiedDelegate = RegionModified;
} }

View File

@@ -477,7 +477,23 @@ namespace Ryujinx.Graphics.Gpu.Image
// If the start address is unmapped, let's try to find a page of memory that is mapped. // If the start address is unmapped, let's try to find a page of memory that is mapped.
if (address == MemoryManager.PteUnmapped) if (address == MemoryManager.PteUnmapped)
{ {
address = memoryManager.TranslateFirstMapped(info.GpuAddress, (ulong)info.CalculateSizeInfo(layerSize).TotalSize); // Make sure that the dimensions are valid before calculating the texture size.
if (info.Width < 1 || info.Height < 1 || info.Levels < 1)
{
return null;
}
if ((info.Target == Target.Texture3D ||
info.Target == Target.Texture2DArray ||
info.Target == Target.Texture2DMultisampleArray ||
info.Target == Target.CubemapArray) && info.DepthOrLayers < 1)
{
return null;
}
ulong dataSize = (ulong)info.CalculateSizeInfo(layerSize).TotalSize;
address = memoryManager.TranslateFirstMapped(info.GpuAddress, dataSize);
} }
// If address is still invalid, the texture is fully unmapped, so it has no data, just return null. // If address is still invalid, the texture is fully unmapped, so it has no data, just return null.

View File

@@ -214,41 +214,6 @@ namespace Ryujinx.Graphics.Gpu.Image
return true; return true;
} }
/// <summary>
/// Checks if two formats are compatible, according to the host API copy format compatibility rules.
/// </summary>
/// <param name="lhsFormat">First comparand</param>
/// <param name="rhsFormat">Second comparand</param>
/// <param name="caps">Host GPU capabilities</param>
/// <returns>True if the formats are compatible, false otherwise</returns>
public static bool FormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps)
{
FormatInfo lhsFormat = lhs.FormatInfo;
FormatInfo rhsFormat = rhs.FormatInfo;
if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil())
{
return lhsFormat.Format == rhsFormat.Format;
}
if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps))
{
return lhsFormat.Format == rhsFormat.Format;
}
if (lhsFormat.IsCompressed && rhsFormat.IsCompressed)
{
FormatClass lhsClass = GetFormatClass(lhsFormat.Format);
FormatClass rhsClass = GetFormatClass(rhsFormat.Format);
return lhsClass == rhsClass;
}
else
{
return lhsFormat.BytesPerPixel == rhsFormat.BytesPerPixel;
}
}
/// <summary> /// <summary>
/// Checks if the texture format matches with the specified texture information. /// Checks if the texture format matches with the specified texture information.
/// </summary> /// </summary>
@@ -391,6 +356,13 @@ namespace Ryujinx.Graphics.Gpu.Image
Size lhsSize = GetSizeInBlocks(lhs, level); Size lhsSize = GetSizeInBlocks(lhs, level);
Size rhsSize = GetSizeInBlocks(rhs); Size rhsSize = GetSizeInBlocks(rhs);
bool alignedWidthMatches = lhsAlignedSize.Width == rhsAlignedSize.Width;
if (lhs.FormatInfo.BytesPerPixel != rhs.FormatInfo.BytesPerPixel && IsIncompatibleFormatAliasingAllowed(lhs.FormatInfo, rhs.FormatInfo))
{
alignedWidthMatches = lhsSize.Width * lhs.FormatInfo.BytesPerPixel == rhsSize.Width * rhs.FormatInfo.BytesPerPixel;
}
TextureViewCompatibility result = TextureViewCompatibility.Full; TextureViewCompatibility result = TextureViewCompatibility.Full;
// For copies, we can copy a subset of the 3D texture slices, // For copies, we can copy a subset of the 3D texture slices,
@@ -404,7 +376,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// so the width may not match in this case for different uses of the same texture. // so the width may not match in this case for different uses of the same texture.
// To account for this, we compare the aligned width here. // To account for this, we compare the aligned width here.
// We expect height to always match exactly, if the texture is the same. // We expect height to always match exactly, if the texture is the same.
if (lhsAlignedSize.Width == rhsAlignedSize.Width && lhsSize.Height == rhsSize.Height) if (alignedWidthMatches && lhsSize.Height == rhsSize.Height)
{ {
return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width
? TextureViewCompatibility.CopyOnly ? TextureViewCompatibility.CopyOnly
@@ -659,21 +631,62 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>The view compatibility level of the texture formats</returns> /// <returns>The view compatibility level of the texture formats</returns>
public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps) public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps)
{ {
if (FormatCompatible(lhs, rhs, caps)) FormatInfo lhsFormat = lhs.FormatInfo;
FormatInfo rhsFormat = rhs.FormatInfo;
if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil())
{ {
if (lhs.FormatInfo.IsCompressed != rhs.FormatInfo.IsCompressed) return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
{ }
return TextureViewCompatibility.CopyOnly;
} if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps))
else {
{ return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
return TextureViewCompatibility.Full; }
}
if (lhsFormat.IsCompressed && rhsFormat.IsCompressed)
{
FormatClass lhsClass = GetFormatClass(lhsFormat.Format);
FormatClass rhsClass = GetFormatClass(rhsFormat.Format);
return lhsClass == rhsClass ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible;
}
else if (lhsFormat.BytesPerPixel == rhsFormat.BytesPerPixel)
{
return lhs.FormatInfo.IsCompressed == rhs.FormatInfo.IsCompressed
? TextureViewCompatibility.Full
: TextureViewCompatibility.CopyOnly;
}
else if (IsIncompatibleFormatAliasingAllowed(lhsFormat, rhsFormat))
{
return TextureViewCompatibility.CopyOnly;
} }
return TextureViewCompatibility.Incompatible; return TextureViewCompatibility.Incompatible;
} }
/// <summary>
/// Checks if aliasing of two formats that would normally be considered incompatible be allowed,
/// using copy dependencies.
/// </summary>
/// <param name="lhsFormat">Format information of the first texture</param
/// <param name="rhsFormat">Format information of the second texture</param>
/// <returns>True if aliasing should be allowed, false otherwise</returns>
private static bool IsIncompatibleFormatAliasingAllowed(FormatInfo lhsFormat, FormatInfo rhsFormat)
{
// Some games will try to alias textures with incompatible foramts, with different BPP (bytes per pixel).
// We allow that in some cases as long Width * BPP is equal on both textures.
// This is very conservative right now as we want to avoid copies as much as possible,
// so we only consider the formats we have seen being aliased.
if (rhsFormat.BytesPerPixel < lhsFormat.BytesPerPixel)
{
(lhsFormat, rhsFormat) = (rhsFormat, lhsFormat);
}
return lhsFormat.Format == Format.R8Unorm && rhsFormat.Format == Format.R8G8B8A8Unorm;
}
/// <summary> /// <summary>
/// Check if the target of the first texture view information is compatible with the target of the second texture view information. /// Check if the target of the first texture view information is compatible with the target of the second texture view information.
/// This follows the host API target compatibility rules. /// This follows the host API target compatibility rules.

View File

@@ -854,7 +854,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A CpuRegionHandle covering the given range</returns> /// <returns>A CpuRegionHandle covering the given range</returns>
private CpuRegionHandle GenerateHandle(ulong address, ulong size) private CpuRegionHandle GenerateHandle(ulong address, ulong size)
{ {
return _physicalMemory.BeginTracking(address, size); return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture);
} }
/// <summary> /// <summary>

View File

@@ -105,13 +105,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_useGranular) if (_useGranular)
{ {
_memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, baseHandles); _memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Buffer, baseHandles);
_memoryTrackingGranular.RegisterPreciseAction(address, size, PreciseAction); _memoryTrackingGranular.RegisterPreciseAction(address, size, PreciseAction);
} }
else else
{ {
_memoryTracking = physicalMemory.BeginTracking(address, size); _memoryTracking = physicalMemory.BeginTracking(address, size, ResourceKind.Buffer);
if (baseHandles != null) if (baseHandles != null)
{ {

View File

@@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value); _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value);
buffer.SignalModified(address, size); memoryManager.Physical.FillTrackedResource(address, size, value, ResourceKind.Buffer);
} }
/// <summary> /// <summary>

View File

@@ -7,6 +7,7 @@ using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
@@ -295,23 +296,41 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Fills the specified memory region with a 32-bit integer value.
/// </summary>
/// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param>
/// <param name="value">Value to fill the region with</param>
/// <param name="kind">Kind of the resource being filled, which will not be signalled as CPU modified</param>
public void FillTrackedResource(ulong address, ulong size, uint value, ResourceKind kind)
{
_cpuMemory.SignalMemoryTracking(address, size, write: true, precise: true, (int)kind);
using WritableRegion region = _cpuMemory.GetWritableRegion(address, (int)size);
MemoryMarshal.Cast<byte, uint>(region.Memory.Span).Fill(value);
}
/// <summary> /// <summary>
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuRegionHandle BeginTracking(ulong address, ulong size) public CpuRegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind)
{ {
return _cpuMemory.BeginTracking(address, size); return _cpuMemory.BeginTracking(address, size, (int)kind);
} }
/// <summary> /// <summary>
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
/// </summary> /// </summary>
/// <param name="range">Ranges of physical memory where the data is located</param> /// <param name="range">Ranges of physical memory where the data is located</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public GpuRegionHandle BeginTracking(MultiRange range) public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind)
{ {
var cpuRegionHandles = new CpuRegionHandle[range.Count]; var cpuRegionHandles = new CpuRegionHandle[range.Count];
int count = 0; int count = 0;
@@ -321,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
var currentRange = range.GetSubRange(i); var currentRange = range.GetSubRange(i);
if (currentRange.Address != MemoryManager.PteUnmapped) if (currentRange.Address != MemoryManager.PteUnmapped)
{ {
cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size); cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size, (int)kind);
} }
} }
@@ -338,12 +357,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <param name="handles">Handles to inherit state from or reuse</param> /// <param name="handles">Handles to inherit state from or reuse</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096) public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096)
{ {
return _cpuMemory.BeginGranularTracking(address, size, handles, granularity); return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind);
} }
/// <summary> /// <summary>
@@ -351,11 +371,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity = 4096) public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096)
{ {
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity); return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind);
} }
/// <summary> /// <summary>

View File

@@ -0,0 +1,13 @@
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// Kind of a GPU resource.
/// </summary>
enum ResourceKind
{
None,
Buffer,
Texture,
Pool
}
}

View File

@@ -34,6 +34,126 @@ namespace Ryujinx.Graphics.OpenGL
return TextureWrapMode.Clamp; return TextureWrapMode.Clamp;
} }
public static NvBlendEquationAdvanced Convert(this AdvancedBlendOp op)
{
switch (op)
{
case AdvancedBlendOp.Zero:
return NvBlendEquationAdvanced.Zero;
case AdvancedBlendOp.Src:
return NvBlendEquationAdvanced.SrcNv;
case AdvancedBlendOp.Dst:
return NvBlendEquationAdvanced.DstNv;
case AdvancedBlendOp.SrcOver:
return NvBlendEquationAdvanced.SrcOverNv;
case AdvancedBlendOp.DstOver:
return NvBlendEquationAdvanced.DstOverNv;
case AdvancedBlendOp.SrcIn:
return NvBlendEquationAdvanced.SrcInNv;
case AdvancedBlendOp.DstIn:
return NvBlendEquationAdvanced.DstInNv;
case AdvancedBlendOp.SrcOut:
return NvBlendEquationAdvanced.SrcOutNv;
case AdvancedBlendOp.DstOut:
return NvBlendEquationAdvanced.DstOutNv;
case AdvancedBlendOp.SrcAtop:
return NvBlendEquationAdvanced.SrcAtopNv;
case AdvancedBlendOp.DstAtop:
return NvBlendEquationAdvanced.DstAtopNv;
case AdvancedBlendOp.Xor:
return NvBlendEquationAdvanced.XorNv;
case AdvancedBlendOp.Plus:
return NvBlendEquationAdvanced.PlusNv;
case AdvancedBlendOp.PlusClamped:
return NvBlendEquationAdvanced.PlusClampedNv;
case AdvancedBlendOp.PlusClampedAlpha:
return NvBlendEquationAdvanced.PlusClampedAlphaNv;
case AdvancedBlendOp.PlusDarker:
return NvBlendEquationAdvanced.PlusDarkerNv;
case AdvancedBlendOp.Multiply:
return NvBlendEquationAdvanced.MultiplyNv;
case AdvancedBlendOp.Screen:
return NvBlendEquationAdvanced.ScreenNv;
case AdvancedBlendOp.Overlay:
return NvBlendEquationAdvanced.OverlayNv;
case AdvancedBlendOp.Darken:
return NvBlendEquationAdvanced.DarkenNv;
case AdvancedBlendOp.Lighten:
return NvBlendEquationAdvanced.LightenNv;
case AdvancedBlendOp.ColorDodge:
return NvBlendEquationAdvanced.ColordodgeNv;
case AdvancedBlendOp.ColorBurn:
return NvBlendEquationAdvanced.ColorburnNv;
case AdvancedBlendOp.HardLight:
return NvBlendEquationAdvanced.HardlightNv;
case AdvancedBlendOp.SoftLight:
return NvBlendEquationAdvanced.SoftlightNv;
case AdvancedBlendOp.Difference:
return NvBlendEquationAdvanced.DifferenceNv;
case AdvancedBlendOp.Minus:
return NvBlendEquationAdvanced.MinusNv;
case AdvancedBlendOp.MinusClamped:
return NvBlendEquationAdvanced.MinusClampedNv;
case AdvancedBlendOp.Exclusion:
return NvBlendEquationAdvanced.ExclusionNv;
case AdvancedBlendOp.Contrast:
return NvBlendEquationAdvanced.ContrastNv;
case AdvancedBlendOp.Invert:
return NvBlendEquationAdvanced.Invert;
case AdvancedBlendOp.InvertRGB:
return NvBlendEquationAdvanced.InvertRgbNv;
case AdvancedBlendOp.InvertOvg:
return NvBlendEquationAdvanced.InvertOvgNv;
case AdvancedBlendOp.LinearDodge:
return NvBlendEquationAdvanced.LineardodgeNv;
case AdvancedBlendOp.LinearBurn:
return NvBlendEquationAdvanced.LinearburnNv;
case AdvancedBlendOp.VividLight:
return NvBlendEquationAdvanced.VividlightNv;
case AdvancedBlendOp.LinearLight:
return NvBlendEquationAdvanced.LinearlightNv;
case AdvancedBlendOp.PinLight:
return NvBlendEquationAdvanced.PinlightNv;
case AdvancedBlendOp.HardMix:
return NvBlendEquationAdvanced.HardmixNv;
case AdvancedBlendOp.Red:
return NvBlendEquationAdvanced.RedNv;
case AdvancedBlendOp.Green:
return NvBlendEquationAdvanced.GreenNv;
case AdvancedBlendOp.Blue:
return NvBlendEquationAdvanced.BlueNv;
case AdvancedBlendOp.HslHue:
return NvBlendEquationAdvanced.HslHueNv;
case AdvancedBlendOp.HslSaturation:
return NvBlendEquationAdvanced.HslSaturationNv;
case AdvancedBlendOp.HslColor:
return NvBlendEquationAdvanced.HslColorNv;
case AdvancedBlendOp.HslLuminosity:
return NvBlendEquationAdvanced.HslLuminosityNv;
}
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(AdvancedBlendOp)} enum value: {op}.");
return NvBlendEquationAdvanced.Zero;
}
public static All Convert(this AdvancedBlendOverlap overlap)
{
switch (overlap)
{
case AdvancedBlendOverlap.Uncorrelated:
return All.UncorrelatedNv;
case AdvancedBlendOverlap.Disjoint:
return All.DisjointNv;
case AdvancedBlendOverlap.Conjoint:
return All.ConjointNv;
}
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(AdvancedBlendOverlap)} enum value: {overlap}.");
return All.UncorrelatedNv;
}
public static All Convert(this BlendFactor factor) public static All Convert(this BlendFactor factor)
{ {
switch (factor) switch (factor)

View File

@@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.OpenGL
{ {
private static readonly Lazy<bool> _supportsAlphaToCoverageDitherControl = new Lazy<bool>(() => HasExtension("GL_NV_alpha_to_coverage_dither_control")); private static readonly Lazy<bool> _supportsAlphaToCoverageDitherControl = new Lazy<bool>(() => HasExtension("GL_NV_alpha_to_coverage_dither_control"));
private static readonly Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); private static readonly Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
private static readonly Lazy<bool> _supportsBlendEquationAdvanced = new Lazy<bool>(() => HasExtension("GL_NV_blend_equation_advanced"));
private static readonly Lazy<bool> _supportsDrawTexture = new Lazy<bool>(() => HasExtension("GL_NV_draw_texture")); private static readonly Lazy<bool> _supportsDrawTexture = new Lazy<bool>(() => HasExtension("GL_NV_draw_texture"));
private static readonly Lazy<bool> _supportsFragmentShaderInterlock = new Lazy<bool>(() => HasExtension("GL_ARB_fragment_shader_interlock")); private static readonly Lazy<bool> _supportsFragmentShaderInterlock = new Lazy<bool>(() => HasExtension("GL_ARB_fragment_shader_interlock"));
private static readonly Lazy<bool> _supportsFragmentShaderOrdering = new Lazy<bool>(() => HasExtension("GL_INTEL_fragment_shader_ordering")); private static readonly Lazy<bool> _supportsFragmentShaderOrdering = new Lazy<bool>(() => HasExtension("GL_INTEL_fragment_shader_ordering"));
@@ -51,6 +52,7 @@ namespace Ryujinx.Graphics.OpenGL
public static bool SupportsAlphaToCoverageDitherControl => _supportsAlphaToCoverageDitherControl.Value; public static bool SupportsAlphaToCoverageDitherControl => _supportsAlphaToCoverageDitherControl.Value;
public static bool SupportsAstcCompression => _supportsAstcCompression.Value; public static bool SupportsAstcCompression => _supportsAstcCompression.Value;
public static bool SupportsBlendEquationAdvanced => _supportsBlendEquationAdvanced.Value;
public static bool SupportsDrawTexture => _supportsDrawTexture.Value; public static bool SupportsDrawTexture => _supportsDrawTexture.Value;
public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value; public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value;
public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value; public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value;

View File

@@ -0,0 +1,252 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Numerics;
namespace Ryujinx.Graphics.OpenGL.Image
{
class TextureCopyIncompatible
{
private const string ComputeShaderShortening = @"#version 450 core
layout (binding = 0, $SRC_FORMAT$) uniform uimage2D src;
layout (binding = 1, $DST_FORMAT$) uniform uimage2D dst;
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
uvec2 coords = gl_GlobalInvocationID.xy;
ivec2 imageSz = imageSize(src);
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
{
return;
}
uint coordsShifted = coords.x << $RATIO_LOG2$;
uvec2 dstCoords0 = uvec2(coordsShifted, coords.y);
uvec2 dstCoords1 = uvec2(coordsShifted + 1, coords.y);
uvec2 dstCoords2 = uvec2(coordsShifted + 2, coords.y);
uvec2 dstCoords3 = uvec2(coordsShifted + 3, coords.y);
uvec4 rgba = imageLoad(src, ivec2(coords));
imageStore(dst, ivec2(dstCoords0), rgba.rrrr);
imageStore(dst, ivec2(dstCoords1), rgba.gggg);
imageStore(dst, ivec2(dstCoords2), rgba.bbbb);
imageStore(dst, ivec2(dstCoords3), rgba.aaaa);
}";
private const string ComputeShaderWidening = @"#version 450 core
layout (binding = 0, $SRC_FORMAT$) uniform uimage2D src;
layout (binding = 1, $DST_FORMAT$) uniform uimage2D dst;
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
uvec2 coords = gl_GlobalInvocationID.xy;
ivec2 imageSz = imageSize(dst);
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
{
return;
}
uvec2 srcCoords = uvec2(coords.x << $RATIO_LOG2$, coords.y);
uint r = imageLoad(src, ivec2(srcCoords) + ivec2(0, 0)).r;
uint g = imageLoad(src, ivec2(srcCoords) + ivec2(1, 0)).r;
uint b = imageLoad(src, ivec2(srcCoords) + ivec2(2, 0)).r;
uint a = imageLoad(src, ivec2(srcCoords) + ivec2(3, 0)).r;
imageStore(dst, ivec2(coords), uvec4(r, g, b, a));
}";
private readonly OpenGLRenderer _renderer;
private readonly Dictionary<int, int> _shorteningProgramHandles;
private readonly Dictionary<int, int> _wideningProgramHandles;
public TextureCopyIncompatible(OpenGLRenderer renderer)
{
_renderer = renderer;
_shorteningProgramHandles = new Dictionary<int, int>();
_wideningProgramHandles = new Dictionary<int, int>();
}
public void CopyIncompatibleFormats(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int depth, int levels)
{
TextureCreateInfo srcInfo = src.Info;
TextureCreateInfo dstInfo = dst.Info;
int srcBpp = src.Info.BytesPerPixel;
int dstBpp = dst.Info.BytesPerPixel;
// Calculate ideal component size, given our constraints:
// - Component size must not exceed bytes per pixel of source and destination image formats.
// - Maximum component size is 4 (R32).
int componentSize = Math.Min(Math.Min(srcBpp, dstBpp), 4);
int srcComponentsCount = srcBpp / componentSize;
int dstComponentsCount = dstBpp / componentSize;
var srcFormat = GetFormat(componentSize, srcComponentsCount);
var dstFormat = GetFormat(componentSize, dstComponentsCount);
GL.UseProgram(srcBpp < dstBpp
? GetWideningShader(componentSize, srcComponentsCount, dstComponentsCount)
: GetShorteningShader(componentSize, srcComponentsCount, dstComponentsCount));
for (int l = 0; l < levels; l++)
{
int srcWidth = Math.Max(1, src.Info.Width >> l);
int srcHeight = Math.Max(1, src.Info.Height >> l);
int dstWidth = Math.Max(1, dst.Info.Width >> l);
int dstHeight = Math.Max(1, dst.Info.Height >> l);
int width = Math.Min(srcWidth, dstWidth);
int height = Math.Min(srcHeight, dstHeight);
for (int z = 0; z < depth; z++)
{
GL.BindImageTexture(0, src.Handle, srcLevel + l, false, srcLayer + z, TextureAccess.ReadOnly, srcFormat);
GL.BindImageTexture(1, dst.Handle, dstLevel + l, false, dstLayer + z, TextureAccess.WriteOnly, dstFormat);
GL.DispatchCompute((width + 31) / 32, (height + 31) / 32, 1);
}
}
Pipeline pipeline = (Pipeline)_renderer.Pipeline;
pipeline.RestoreProgram();
pipeline.RestoreImages1And2();
}
private static SizedInternalFormat GetFormat(int componentSize, int componentsCount)
{
if (componentSize == 1)
{
return componentsCount switch
{
1 => SizedInternalFormat.R8ui,
2 => SizedInternalFormat.Rg8ui,
4 => SizedInternalFormat.Rgba8ui,
_ => throw new ArgumentException($"Invalid components count {componentsCount}.")
};
}
else if (componentSize == 2)
{
return componentsCount switch
{
1 => SizedInternalFormat.R16ui,
2 => SizedInternalFormat.Rg16ui,
4 => SizedInternalFormat.Rgba16ui,
_ => throw new ArgumentException($"Invalid components count {componentsCount}.")
};
}
else if (componentSize == 4)
{
return componentsCount switch
{
1 => SizedInternalFormat.R32ui,
2 => SizedInternalFormat.Rg32ui,
4 => SizedInternalFormat.Rgba32ui,
_ => throw new ArgumentException($"Invalid components count {componentsCount}.")
};
}
else
{
throw new ArgumentException($"Invalid component size {componentSize}.");
}
}
private int GetShorteningShader(int componentSize, int srcComponentsCount, int dstComponentsCount)
{
return GetShader(ComputeShaderShortening, _shorteningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount);
}
private int GetWideningShader(int componentSize, int srcComponentsCount, int dstComponentsCount)
{
return GetShader(ComputeShaderWidening, _wideningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount);
}
private int GetShader(
string code,
Dictionary<int, int> programHandles,
int componentSize,
int srcComponentsCount,
int dstComponentsCount)
{
int componentSizeLog2 = BitOperations.Log2((uint)componentSize);
int srcIndex = componentSizeLog2 + BitOperations.Log2((uint)srcComponentsCount) * 3;
int dstIndex = componentSizeLog2 + BitOperations.Log2((uint)dstComponentsCount) * 3;
int key = srcIndex | (dstIndex << 8);
if (!programHandles.TryGetValue(key, out int programHandle))
{
int csHandle = GL.CreateShader(ShaderType.ComputeShader);
string[] formatTable = new[] { "r8ui", "r16ui", "r32ui", "rg8ui", "rg16ui", "rg32ui", "rgba8ui", "rgba16ui", "rgba32ui" };
string srcFormat = formatTable[srcIndex];
string dstFormat = formatTable[dstIndex];
int srcBpp = srcComponentsCount * componentSize;
int dstBpp = dstComponentsCount * componentSize;
int ratio = srcBpp < dstBpp ? dstBpp / srcBpp : srcBpp / dstBpp;
int ratioLog2 = BitOperations.Log2((uint)ratio);
GL.ShaderSource(csHandle, code
.Replace("$SRC_FORMAT$", srcFormat)
.Replace("$DST_FORMAT$", dstFormat)
.Replace("$RATIO_LOG2$", ratioLog2.ToString(CultureInfo.InvariantCulture)));
GL.CompileShader(csHandle);
programHandle = GL.CreateProgram();
GL.AttachShader(programHandle, csHandle);
GL.LinkProgram(programHandle);
GL.DetachShader(programHandle, csHandle);
GL.DeleteShader(csHandle);
GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status);
if (status == 0)
{
throw new Exception(GL.GetProgramInfoLog(programHandle));
}
programHandles.Add(key, programHandle);
}
return programHandle;
}
public void Dispose()
{
foreach (int handle in _shorteningProgramHandles.Values)
{
GL.DeleteProgram(handle);
}
_shorteningProgramHandles.Clear();
foreach (int handle in _wideningProgramHandles.Values)
{
GL.DeleteProgram(handle);
}
_wideningProgramHandles.Clear();
}
}
}

View File

@@ -127,6 +127,12 @@ namespace Ryujinx.Graphics.OpenGL.Image
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer); int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers); _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers);
} }
else if (destinationView.Info.BytesPerPixel != Info.BytesPerPixel)
{
int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
int levels = Math.Min(Info.Levels, destinationView.Info.Levels - firstLevel);
_renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, 0, firstLayer, 0, firstLevel, layers, levels);
}
else else
{ {
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel); _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
@@ -145,6 +151,10 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
_renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1); _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1);
} }
else if (destinationView.Info.BytesPerPixel != Info.BytesPerPixel)
{
_renderer.TextureCopyIncompatible.CopyIncompatibleFormats(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
}
else else
{ {
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);

View File

@@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.OpenGL
private TextureCopy _textureCopy; private TextureCopy _textureCopy;
private TextureCopy _backgroundTextureCopy; private TextureCopy _backgroundTextureCopy;
internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy; internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy;
internal TextureCopyIncompatible TextureCopyIncompatible { get; }
internal TextureCopyMS TextureCopyMS { get; } internal TextureCopyMS TextureCopyMS { get; }
private Sync _sync; private Sync _sync;
@@ -49,6 +50,7 @@ namespace Ryujinx.Graphics.OpenGL
_window = new Window(this); _window = new Window(this);
_textureCopy = new TextureCopy(this); _textureCopy = new TextureCopy(this);
_backgroundTextureCopy = new TextureCopy(this); _backgroundTextureCopy = new TextureCopy(this);
TextureCopyIncompatible = new TextureCopyIncompatible(this);
TextureCopyMS = new TextureCopyMS(this); TextureCopyMS = new TextureCopyMS(this);
_sync = new Sync(); _sync = new Sync();
PersistentBuffers = new PersistentBuffers(); PersistentBuffers = new PersistentBuffers();
@@ -119,6 +121,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsR4G4B4A4Format: true, supportsR4G4B4A4Format: true,
supportsSnormBufferTextureFormat: false, supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true, supports5BitComponentFormat: true,
supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,

View File

@@ -59,6 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
private uint _fragmentOutputMap; private uint _fragmentOutputMap;
private uint _componentMasks; private uint _componentMasks;
private uint _currentComponentMasks; private uint _currentComponentMasks;
private bool _advancedBlendEnable;
private uint _scissorEnables; private uint _scissorEnables;
@@ -784,8 +785,26 @@ namespace Ryujinx.Graphics.OpenGL
GL.Enable(EnableCap.AlphaTest); GL.Enable(EnableCap.AlphaTest);
} }
public void SetBlendState(AdvancedBlendDescriptor blend)
{
if (HwCapabilities.SupportsBlendEquationAdvanced)
{
GL.BlendEquation((BlendEquationMode)blend.Op.Convert());
GL.NV.BlendParameter(NvBlendEquationAdvanced.BlendOverlapNv, (int)blend.Overlap.Convert());
GL.NV.BlendParameter(NvBlendEquationAdvanced.BlendPremultipliedSrcNv, blend.SrcPreMultiplied ? 1 : 0);
GL.Enable(EnableCap.Blend);
_advancedBlendEnable = true;
}
}
public void SetBlendState(int index, BlendDescriptor blend) public void SetBlendState(int index, BlendDescriptor blend)
{ {
if (_advancedBlendEnable)
{
GL.Disable(EnableCap.Blend);
_advancedBlendEnable = false;
}
if (!blend.Enable) if (!blend.Enable)
{ {
GL.Disable(IndexedEnableCap.Blend, index); GL.Disable(IndexedEnableCap.Blend, index);

View File

@@ -39,7 +39,6 @@ namespace Ryujinx.Graphics.Vulkan
BufferUsageFlags.VertexBufferBit | BufferUsageFlags.VertexBufferBit |
BufferUsageFlags.TransformFeedbackBufferBitExt; BufferUsageFlags.TransformFeedbackBufferBitExt;
private readonly PhysicalDevice _physicalDevice;
private readonly Device _device; private readonly Device _device;
private readonly IdList<BufferHolder> _buffers; private readonly IdList<BufferHolder> _buffers;
@@ -48,9 +47,8 @@ namespace Ryujinx.Graphics.Vulkan
public StagingBuffer StagingBuffer { get; } public StagingBuffer StagingBuffer { get; }
public BufferManager(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device) public BufferManager(VulkanRenderer gd, Device device)
{ {
_physicalDevice = physicalDevice;
_device = device; _device = device;
_buffers = new IdList<BufferHolder>(); _buffers = new IdList<BufferHolder>();
StagingBuffer = new StagingBuffer(gd, this); StagingBuffer = new StagingBuffer(gd, this);
@@ -114,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
allocateFlagsAlt = DefaultBufferMemoryAltFlags; allocateFlagsAlt = DefaultBufferMemoryAltFlags;
} }
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt); var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt);
if (allocation.Memory.Handle == 0UL) if (allocation.Memory.Handle == 0UL)
{ {

View File

@@ -79,6 +79,60 @@ namespace Ryujinx.Graphics.Vulkan
}; };
} }
public static Silk.NET.Vulkan.BlendOp Convert(this GAL.AdvancedBlendOp op)
{
return op switch
{
GAL.AdvancedBlendOp.Zero => Silk.NET.Vulkan.BlendOp.ZeroExt,
GAL.AdvancedBlendOp.Src => Silk.NET.Vulkan.BlendOp.SrcExt,
GAL.AdvancedBlendOp.Dst => Silk.NET.Vulkan.BlendOp.DstExt,
GAL.AdvancedBlendOp.SrcOver => Silk.NET.Vulkan.BlendOp.SrcOverExt,
GAL.AdvancedBlendOp.DstOver => Silk.NET.Vulkan.BlendOp.DstOverExt,
GAL.AdvancedBlendOp.SrcIn => Silk.NET.Vulkan.BlendOp.SrcInExt,
GAL.AdvancedBlendOp.DstIn => Silk.NET.Vulkan.BlendOp.DstInExt,
GAL.AdvancedBlendOp.SrcOut => Silk.NET.Vulkan.BlendOp.SrcOutExt,
GAL.AdvancedBlendOp.DstOut => Silk.NET.Vulkan.BlendOp.DstOutExt,
GAL.AdvancedBlendOp.SrcAtop => Silk.NET.Vulkan.BlendOp.SrcAtopExt,
GAL.AdvancedBlendOp.DstAtop => Silk.NET.Vulkan.BlendOp.DstAtopExt,
GAL.AdvancedBlendOp.Xor => Silk.NET.Vulkan.BlendOp.XorExt,
GAL.AdvancedBlendOp.Plus => Silk.NET.Vulkan.BlendOp.PlusExt,
GAL.AdvancedBlendOp.PlusClamped => Silk.NET.Vulkan.BlendOp.PlusClampedExt,
GAL.AdvancedBlendOp.PlusClampedAlpha => Silk.NET.Vulkan.BlendOp.PlusClampedAlphaExt,
GAL.AdvancedBlendOp.PlusDarker => Silk.NET.Vulkan.BlendOp.PlusDarkerExt,
GAL.AdvancedBlendOp.Multiply => Silk.NET.Vulkan.BlendOp.MultiplyExt,
GAL.AdvancedBlendOp.Screen => Silk.NET.Vulkan.BlendOp.ScreenExt,
GAL.AdvancedBlendOp.Overlay => Silk.NET.Vulkan.BlendOp.OverlayExt,
GAL.AdvancedBlendOp.Darken => Silk.NET.Vulkan.BlendOp.DarkenExt,
GAL.AdvancedBlendOp.Lighten => Silk.NET.Vulkan.BlendOp.LightenExt,
GAL.AdvancedBlendOp.ColorDodge => Silk.NET.Vulkan.BlendOp.ColordodgeExt,
GAL.AdvancedBlendOp.ColorBurn => Silk.NET.Vulkan.BlendOp.ColorburnExt,
GAL.AdvancedBlendOp.HardLight => Silk.NET.Vulkan.BlendOp.HardlightExt,
GAL.AdvancedBlendOp.SoftLight => Silk.NET.Vulkan.BlendOp.SoftlightExt,
GAL.AdvancedBlendOp.Difference => Silk.NET.Vulkan.BlendOp.DifferenceExt,
GAL.AdvancedBlendOp.Minus => Silk.NET.Vulkan.BlendOp.MinusExt,
GAL.AdvancedBlendOp.MinusClamped => Silk.NET.Vulkan.BlendOp.MinusClampedExt,
GAL.AdvancedBlendOp.Exclusion => Silk.NET.Vulkan.BlendOp.ExclusionExt,
GAL.AdvancedBlendOp.Contrast => Silk.NET.Vulkan.BlendOp.ContrastExt,
GAL.AdvancedBlendOp.Invert => Silk.NET.Vulkan.BlendOp.InvertExt,
GAL.AdvancedBlendOp.InvertRGB => Silk.NET.Vulkan.BlendOp.InvertRgbExt,
GAL.AdvancedBlendOp.InvertOvg => Silk.NET.Vulkan.BlendOp.InvertOvgExt,
GAL.AdvancedBlendOp.LinearDodge => Silk.NET.Vulkan.BlendOp.LineardodgeExt,
GAL.AdvancedBlendOp.LinearBurn => Silk.NET.Vulkan.BlendOp.LinearburnExt,
GAL.AdvancedBlendOp.VividLight => Silk.NET.Vulkan.BlendOp.VividlightExt,
GAL.AdvancedBlendOp.LinearLight => Silk.NET.Vulkan.BlendOp.LinearlightExt,
GAL.AdvancedBlendOp.PinLight => Silk.NET.Vulkan.BlendOp.PinlightExt,
GAL.AdvancedBlendOp.HardMix => Silk.NET.Vulkan.BlendOp.HardmixExt,
GAL.AdvancedBlendOp.Red => Silk.NET.Vulkan.BlendOp.RedExt,
GAL.AdvancedBlendOp.Green => Silk.NET.Vulkan.BlendOp.GreenExt,
GAL.AdvancedBlendOp.Blue => Silk.NET.Vulkan.BlendOp.BlueExt,
GAL.AdvancedBlendOp.HslHue => Silk.NET.Vulkan.BlendOp.HslHueExt,
GAL.AdvancedBlendOp.HslSaturation => Silk.NET.Vulkan.BlendOp.HslSaturationExt,
GAL.AdvancedBlendOp.HslColor => Silk.NET.Vulkan.BlendOp.HslColorExt,
GAL.AdvancedBlendOp.HslLuminosity => Silk.NET.Vulkan.BlendOp.HslLuminosityExt,
_ => LogInvalidAndReturn(op, nameof(GAL.AdvancedBlendOp), Silk.NET.Vulkan.BlendOp.Add)
};
}
public static Silk.NET.Vulkan.BlendOp Convert(this GAL.BlendOp op) public static Silk.NET.Vulkan.BlendOp Convert(this GAL.BlendOp op)
{ {
return op switch return op switch
@@ -92,6 +146,17 @@ namespace Ryujinx.Graphics.Vulkan
}; };
} }
public static Silk.NET.Vulkan.BlendOverlapEXT Convert(this GAL.AdvancedBlendOverlap overlap)
{
return overlap switch
{
GAL.AdvancedBlendOverlap.Uncorrelated => Silk.NET.Vulkan.BlendOverlapEXT.UncorrelatedExt,
GAL.AdvancedBlendOverlap.Disjoint => Silk.NET.Vulkan.BlendOverlapEXT.DisjointExt,
GAL.AdvancedBlendOverlap.Conjoint => Silk.NET.Vulkan.BlendOverlapEXT.ConjointExt,
_ => LogInvalidAndReturn(overlap, nameof(GAL.AdvancedBlendOverlap), Silk.NET.Vulkan.BlendOverlapEXT.UncorrelatedExt)
};
}
public static Silk.NET.Vulkan.CompareOp Convert(this GAL.CompareOp op) public static Silk.NET.Vulkan.CompareOp Convert(this GAL.CompareOp op)
{ {
return op switch return op switch

View File

@@ -8,17 +8,20 @@ namespace Ryujinx.Graphics.Vulkan
{ {
None = 0, None = 0,
VertexBufferAlignment4B = 1, NoTriangleFans = 1,
NoTriangleFans = 1 << 1, NoPointMode = 1 << 1,
NoPointMode = 1 << 2, No3DImageView = 1 << 2,
No3DImageView = 1 << 3, NoLodBias = 1 << 3
NoLodBias = 1 << 4
} }
readonly struct HardwareCapabilities readonly struct HardwareCapabilities
{ {
public readonly bool SupportsIndexTypeUint8; public readonly bool SupportsIndexTypeUint8;
public readonly bool SupportsCustomBorderColor; public readonly bool SupportsCustomBorderColor;
public readonly bool SupportsBlendEquationAdvanced;
public readonly bool SupportsBlendEquationAdvancedCorrelatedOverlap;
public readonly bool SupportsBlendEquationAdvancedNonPreMultipliedSrcColor;
public readonly bool SupportsBlendEquationAdvancedNonPreMultipliedDstColor;
public readonly bool SupportsIndirectParameters; public readonly bool SupportsIndirectParameters;
public readonly bool SupportsFragmentShaderInterlock; public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsGeometryShaderPassthrough;
@@ -40,10 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
public readonly ShaderStageFlags RequiredSubgroupSizeStages; public readonly ShaderStageFlags RequiredSubgroupSizeStages;
public readonly SampleCountFlags SupportedSampleCounts; public readonly SampleCountFlags SupportedSampleCounts;
public readonly PortabilitySubsetFlags PortabilitySubset; public readonly PortabilitySubsetFlags PortabilitySubset;
public readonly uint VertexBufferAlignment;
public HardwareCapabilities( public HardwareCapabilities(
bool supportsIndexTypeUint8, bool supportsIndexTypeUint8,
bool supportsCustomBorderColor, bool supportsCustomBorderColor,
bool supportsBlendEquationAdvanced,
bool supportsBlendEquationAdvancedCorrelatedOverlap,
bool supportsBlendEquationAdvancedNonPreMultipliedSrcColor,
bool supportsBlendEquationAdvancedNonPreMultipliedDstColor,
bool supportsIndirectParameters, bool supportsIndirectParameters,
bool supportsFragmentShaderInterlock, bool supportsFragmentShaderInterlock,
bool supportsGeometryShaderPassthrough, bool supportsGeometryShaderPassthrough,
@@ -64,10 +72,15 @@ namespace Ryujinx.Graphics.Vulkan
uint maxSubgroupSize, uint maxSubgroupSize,
ShaderStageFlags requiredSubgroupSizeStages, ShaderStageFlags requiredSubgroupSizeStages,
SampleCountFlags supportedSampleCounts, SampleCountFlags supportedSampleCounts,
PortabilitySubsetFlags portabilitySubset) PortabilitySubsetFlags portabilitySubset,
uint vertexBufferAlignment)
{ {
SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsIndexTypeUint8 = supportsIndexTypeUint8;
SupportsCustomBorderColor = supportsCustomBorderColor; SupportsCustomBorderColor = supportsCustomBorderColor;
SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced;
SupportsBlendEquationAdvancedCorrelatedOverlap = supportsBlendEquationAdvancedCorrelatedOverlap;
SupportsBlendEquationAdvancedNonPreMultipliedSrcColor = supportsBlendEquationAdvancedNonPreMultipliedSrcColor;
SupportsBlendEquationAdvancedNonPreMultipliedDstColor = supportsBlendEquationAdvancedNonPreMultipliedDstColor;
SupportsIndirectParameters = supportsIndirectParameters; SupportsIndirectParameters = supportsIndirectParameters;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock; SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
@@ -89,6 +102,7 @@ namespace Ryujinx.Graphics.Vulkan
RequiredSubgroupSizeStages = requiredSubgroupSizeStages; RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
SupportedSampleCounts = supportedSampleCounts; SupportedSampleCounts = supportedSampleCounts;
PortabilitySubset = portabilitySubset; PortabilitySubset = portabilitySubset;
VertexBufferAlignment = vertexBufferAlignment;
} }
} }
} }

View File

@@ -5,6 +5,7 @@ using Ryujinx.Graphics.Vulkan.Shaders;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using VkFormat = Silk.NET.Vulkan.Format; using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
@@ -32,7 +33,9 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programStrideChange; private readonly IProgram _programStrideChange;
private readonly IProgram _programConvertIndexBuffer; private readonly IProgram _programConvertIndexBuffer;
private readonly IProgram _programConvertIndirectData; private readonly IProgram _programConvertIndirectData;
private readonly IProgram _programColorCopyShortening;
private readonly IProgram _programColorCopyToNonMs; private readonly IProgram _programColorCopyToNonMs;
private readonly IProgram _programColorCopyWidening;
private readonly IProgram _programColorDrawToMs; private readonly IProgram _programColorDrawToMs;
private readonly IProgram _programDepthBlit; private readonly IProgram _programDepthBlit;
private readonly IProgram _programDepthBlitMs; private readonly IProgram _programDepthBlitMs;
@@ -112,15 +115,25 @@ namespace Ryujinx.Graphics.Vulkan
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
}); });
var colorCopyToNonMsBindings = new ShaderBindings( var colorCopyBindings = new ShaderBindings(
new[] { 0 }, new[] { 0 },
Array.Empty<int>(), Array.Empty<int>(),
new[] { 0 }, new[] { 0 },
new[] { 0 }); new[] { 0 });
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
});
_programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[] _programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
{ {
new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyToNonMsBindings, ShaderStage.Compute, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
});
_programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv),
}); });
var colorDrawToMsVertexBindings = new ShaderBindings( var colorDrawToMsVertexBindings = new ShaderBindings(
@@ -922,6 +935,107 @@ namespace Ryujinx.Graphics.Vulkan
convertedCount * outputIndexSize); convertedCount * outputIndexSize);
} }
public void CopyIncompatibleFormats(
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
TextureView dst,
int srcLayer,
int dstLayer,
int srcLevel,
int dstLevel,
int depth,
int levels)
{
const int ParamsBufferSize = 4;
Span<int> shaderParams = stackalloc int[sizeof(int)];
int srcBpp = src.Info.BytesPerPixel;
int dstBpp = dst.Info.BytesPerPixel;
int ratio = srcBpp < dstBpp ? dstBpp / srcBpp : srcBpp / dstBpp;
shaderParams[0] = BitOperations.Log2((uint)ratio);
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
TextureView.InsertImageBarrier(
gd.Api,
cbs.CommandBuffer,
src.GetImage().Get(cbs).Value,
TextureStorage.DefaultAccessMask,
AccessFlags.ShaderReadBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.ComputeShaderBit,
ImageAspectFlags.ColorBit,
src.FirstLayer + srcLayer,
src.FirstLevel + srcLevel,
depth,
levels);
_pipeline.SetCommandBuffer(cbs);
_pipeline.SetProgram(srcBpp < dstBpp ? _programColorCopyWidening : _programColorCopyShortening);
// Calculate ideal component size, given our constraints:
// - Component size must not exceed bytes per pixel of source and destination image formats.
// - Maximum component size is 4 (R32).
int componentSize = Math.Min(Math.Min(srcBpp, dstBpp), 4);
var srcFormat = GetFormat(componentSize, srcBpp / componentSize);
var dstFormat = GetFormat(componentSize, dstBpp / componentSize);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
for (int l = 0; l < levels; l++)
{
for (int z = 0; z < depth; z++)
{
var srcView = Create2DLayerView(src, srcLayer + z, srcLevel + l, srcFormat);
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, dstFormat);
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
if (srcView != src)
{
srcView.Release();
}
if (dstView != dst)
{
dstView.Release();
}
}
}
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs);
TextureView.InsertImageBarrier(
gd.Api,
cbs.CommandBuffer,
dst.GetImage().Get(cbs).Value,
AccessFlags.ShaderWriteBit,
TextureStorage.DefaultAccessMask,
PipelineStageFlags.ComputeShaderBit,
PipelineStageFlags.AllCommandsBit,
ImageAspectFlags.ColorBit,
dst.FirstLayer + dstLayer,
dst.FirstLevel + dstLevel,
depth,
levels);
}
public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth) public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
{ {
const int ParamsBufferSize = 16; const int ParamsBufferSize = 16;
@@ -1196,6 +1310,44 @@ namespace Ryujinx.Graphics.Vulkan
}; };
} }
private static GAL.Format GetFormat(int componentSize, int componentsCount)
{
if (componentSize == 1)
{
return componentsCount switch
{
1 => GAL.Format.R8Uint,
2 => GAL.Format.R8G8Uint,
4 => GAL.Format.R8G8B8A8Uint,
_ => throw new ArgumentException($"Invalid components count {componentsCount}.")
};
}
else if (componentSize == 2)
{
return componentsCount switch
{
1 => GAL.Format.R16Uint,
2 => GAL.Format.R16G16Uint,
4 => GAL.Format.R16G16B16A16Uint,
_ => throw new ArgumentException($"Invalid components count {componentsCount}.")
};
}
else if (componentSize == 4)
{
return componentsCount switch
{
1 => GAL.Format.R32Uint,
2 => GAL.Format.R32G32Uint,
4 => GAL.Format.R32G32B32A32Uint,
_ => throw new ArgumentException($"Invalid components count {componentsCount}.")
};
}
else
{
throw new ArgumentException($"Invalid component size {componentSize}.");
}
}
public void ConvertIndexBufferIndirect( public void ConvertIndexBufferIndirect(
VulkanRenderer gd, VulkanRenderer gd,
CommandBufferScoped cbs, CommandBufferScoped cbs,
@@ -1336,7 +1488,9 @@ namespace Ryujinx.Graphics.Vulkan
_programStrideChange.Dispose(); _programStrideChange.Dispose();
_programConvertIndexBuffer.Dispose(); _programConvertIndexBuffer.Dispose();
_programConvertIndirectData.Dispose(); _programConvertIndirectData.Dispose();
_programColorCopyShortening.Dispose();
_programColorCopyToNonMs.Dispose(); _programColorCopyToNonMs.Dispose();
_programColorCopyWidening.Dispose();
_programColorDrawToMs.Dispose(); _programColorDrawToMs.Dispose();
_programDepthBlit.Dispose(); _programDepthBlit.Dispose();
_programDepthBlitMs.Dispose(); _programDepthBlitMs.Dispose();

View File

@@ -9,34 +9,36 @@ namespace Ryujinx.Graphics.Vulkan
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
private readonly Vk _api; private readonly Vk _api;
private readonly PhysicalDevice _physicalDevice;
private readonly Device _device; private readonly Device _device;
private readonly List<MemoryAllocatorBlockList> _blockLists; private readonly List<MemoryAllocatorBlockList> _blockLists;
private readonly int _blockAlignment;
private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties;
private int _blockAlignment; public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount)
public MemoryAllocator(Vk api, Device device, uint maxMemoryAllocationCount)
{ {
_api = api; _api = api;
_physicalDevice = physicalDevice;
_device = device; _device = device;
_blockLists = new List<MemoryAllocatorBlockList>(); _blockLists = new List<MemoryAllocatorBlockList>();
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount); _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount);
_api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties);
} }
public MemoryAllocation AllocateDeviceMemory( public MemoryAllocation AllocateDeviceMemory(
PhysicalDevice physicalDevice,
MemoryRequirements requirements, MemoryRequirements requirements,
MemoryPropertyFlags flags = 0) MemoryPropertyFlags flags = 0)
{ {
return AllocateDeviceMemory(physicalDevice, requirements, flags, flags); return AllocateDeviceMemory(requirements, flags, flags);
} }
public MemoryAllocation AllocateDeviceMemory( public MemoryAllocation AllocateDeviceMemory(
PhysicalDevice physicalDevice,
MemoryRequirements requirements, MemoryRequirements requirements,
MemoryPropertyFlags flags, MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags) MemoryPropertyFlags alternativeFlags)
{ {
int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags); int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags);
if (memoryTypeIndex < 0) if (memoryTypeIndex < 0)
{ {
return default; return default;
@@ -65,20 +67,16 @@ namespace Ryujinx.Graphics.Vulkan
return newBl.Allocate(size, alignment, map); return newBl.Allocate(size, alignment, map);
} }
private static int FindSuitableMemoryTypeIndex( private int FindSuitableMemoryTypeIndex(
Vk api,
PhysicalDevice physicalDevice,
uint memoryTypeBits, uint memoryTypeBits,
MemoryPropertyFlags flags, MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags) MemoryPropertyFlags alternativeFlags)
{ {
int bestCandidateIndex = -1; int bestCandidateIndex = -1;
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
for (int i = 0; i < properties.MemoryTypeCount; i++)
{ {
var type = properties.MemoryTypes[i]; var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
if ((memoryTypeBits & (1 << i)) != 0) if ((memoryTypeBits & (1 << i)) != 0)
{ {

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
@@ -111,11 +112,9 @@ namespace Ryujinx.Graphics.Vulkan
var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f }; var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
new Span<Vector4<float>>(_renderScale).Fill(defaultScale); new Span<Vector4<float>>(_renderScale).Fill(defaultScale);
_newState.Initialize(); _storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets];
_newState.LineWidth = 1f;
_newState.SamplesCount = 1;
_storedBlend = new PipelineColorBlendAttachmentState[8]; _newState.Initialize();
} }
public void Initialize() public void Initialize()
@@ -675,6 +674,49 @@ namespace Ryujinx.Graphics.Vulkan
// to avoid creating one version of the shader per reference value used. // to avoid creating one version of the shader per reference value used.
} }
public void SetBlendState(AdvancedBlendDescriptor blend)
{
for (int index = 0; index < Constants.MaxRenderTargets; index++)
{
ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index];
if (index == 0)
{
var blendOp = blend.Op.Convert();
vkBlend = new PipelineColorBlendAttachmentState(
blendEnable: true,
colorBlendOp: blendOp,
alphaBlendOp: blendOp,
colorWriteMask: vkBlend.ColorWriteMask);
if (Gd.Capabilities.SupportsBlendEquationAdvancedNonPreMultipliedSrcColor)
{
_newState.AdvancedBlendSrcPreMultiplied = blend.SrcPreMultiplied;
}
if (Gd.Capabilities.SupportsBlendEquationAdvancedCorrelatedOverlap)
{
_newState.AdvancedBlendOverlap = blend.Overlap.Convert();
}
}
else
{
vkBlend = new PipelineColorBlendAttachmentState(
colorWriteMask: vkBlend.ColorWriteMask);
}
if (vkBlend.ColorWriteMask == 0)
{
_storedBlend[index] = vkBlend;
vkBlend = new PipelineColorBlendAttachmentState();
}
}
SignalStateChange();
}
public void SetBlendState(int index, BlendDescriptor blend) public void SetBlendState(int index, BlendDescriptor blend)
{ {
ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index]; ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index];
@@ -708,6 +750,11 @@ namespace Ryujinx.Graphics.Vulkan
blend.BlendConstant.Blue, blend.BlendConstant.Blue,
blend.BlendConstant.Alpha); blend.BlendConstant.Alpha);
// Reset advanced blend state back defaults to the cache to help the pipeline cache.
_newState.AdvancedBlendSrcPreMultiplied = true;
_newState.AdvancedBlendDstPreMultiplied = true;
_newState.AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt;
SignalStateChange(); SignalStateChange();
} }
@@ -1136,7 +1183,7 @@ namespace Ryujinx.Graphics.Vulkan
buffer.Dispose(); buffer.Dispose();
if (!Gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B) && if (Gd.Capabilities.VertexBufferAlignment < 2 &&
(vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{ {
buffer = new VertexBufferState( buffer = new VertexBufferState(

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
@@ -253,7 +254,7 @@ namespace Ryujinx.Graphics.Vulkan
if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment)) if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment))
{ {
alignedStride = (vertexBuffer.Stride + (alignment - 1)) & -alignment; alignedStride = BitUtils.AlignUp(vertexBuffer.Stride, alignment);
} }
// TODO: Support divisor > 1 // TODO: Support divisor > 1

View File

@@ -285,6 +285,24 @@ namespace Ryujinx.Graphics.Vulkan
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1);
} }
public bool AdvancedBlendSrcPreMultiplied
{
get => ((Internal.Id9 >> 2) & 0x1) != 0UL;
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2);
}
public bool AdvancedBlendDstPreMultiplied
{
get => ((Internal.Id9 >> 3) & 0x1) != 0UL;
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3);
}
public BlendOverlapEXT AdvancedBlendOverlap
{
get => (BlendOverlapEXT)((Internal.Id9 >> 4) & 0x3);
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4);
}
public NativeArray<PipelineShaderStageCreateInfo> Stages; public NativeArray<PipelineShaderStageCreateInfo> Stages;
public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes; public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes;
public PipelineLayout PipelineLayout; public PipelineLayout PipelineLayout;
@@ -303,6 +321,13 @@ namespace Ryujinx.Graphics.Vulkan
RequiredSubgroupSize = RequiredSubgroupSize RequiredSubgroupSize = RequiredSubgroupSize
}; };
} }
AdvancedBlendSrcPreMultiplied = true;
AdvancedBlendDstPreMultiplied = true;
AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt;
LineWidth = 1f;
SamplesCount = 1;
} }
public unsafe Auto<DisposablePipeline> CreateComputePipeline( public unsafe Auto<DisposablePipeline> CreateComputePipeline(
@@ -486,6 +511,23 @@ namespace Ryujinx.Graphics.Vulkan
PAttachments = pColorBlendAttachmentState PAttachments = pColorBlendAttachmentState
}; };
PipelineColorBlendAdvancedStateCreateInfoEXT colorBlendAdvancedState;
if (!AdvancedBlendSrcPreMultiplied ||
!AdvancedBlendDstPreMultiplied ||
AdvancedBlendOverlap != BlendOverlapEXT.UncorrelatedExt)
{
colorBlendAdvancedState = new PipelineColorBlendAdvancedStateCreateInfoEXT()
{
SType = StructureType.PipelineColorBlendAdvancedStateCreateInfoExt,
SrcPremultiplied = AdvancedBlendSrcPreMultiplied,
DstPremultiplied = AdvancedBlendDstPreMultiplied,
BlendOverlap = AdvancedBlendOverlap
};
colorBlendState.PNext = &colorBlendAdvancedState;
}
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState; bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
int dynamicStatesCount = supportsExtDynamicState ? 9 : 8; int dynamicStatesCount = supportsExtDynamicState ? 9 : 8;

View File

@@ -0,0 +1,36 @@
#version 450 core
layout (std140, binding = 0) uniform ratio_in
{
int ratio;
};
layout (set = 2, binding = 0) uniform usampler2D src;
layout (set = 3, binding = 0) writeonly uniform uimage2D dst;
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
uvec2 coords = gl_GlobalInvocationID.xy;
ivec2 textureSz = textureSize(src, 0);
if (int(coords.x) >= textureSz.x || int(coords.y) >= textureSz.y)
{
return;
}
uint coordsShifted = coords.x << ratio;
uvec2 dstCoords0 = uvec2(coordsShifted, coords.y);
uvec2 dstCoords1 = uvec2(coordsShifted + 1, coords.y);
uvec2 dstCoords2 = uvec2(coordsShifted + 2, coords.y);
uvec2 dstCoords3 = uvec2(coordsShifted + 3, coords.y);
uvec4 rgba = texelFetch(src, ivec2(coords), 0);
imageStore(dst, ivec2(dstCoords0), rgba.rrrr);
imageStore(dst, ivec2(dstCoords1), rgba.gggg);
imageStore(dst, ivec2(dstCoords2), rgba.bbbb);
imageStore(dst, ivec2(dstCoords3), rgba.aaaa);
}

View File

@@ -0,0 +1,31 @@
#version 450 core
layout (std140, binding = 0) uniform ratio_in
{
int ratio;
};
layout (set = 2, binding = 0) uniform usampler2D src;
layout (set = 3, binding = 0) writeonly uniform uimage2D dst;
layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
uvec2 coords = gl_GlobalInvocationID.xy;
ivec2 imageSz = imageSize(dst);
if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
{
return;
}
uvec2 srcCoords = uvec2(coords.x << ratio, coords.y);
uint r = texelFetchOffset(src, ivec2(srcCoords), 0, ivec2(0, 0)).r;
uint g = texelFetchOffset(src, ivec2(srcCoords), 0, ivec2(1, 0)).r;
uint b = texelFetchOffset(src, ivec2(srcCoords), 0, ivec2(2, 0)).r;
uint a = texelFetchOffset(src, ivec2(srcCoords), 0, ivec2(3, 0)).r;
imageStore(dst, ivec2(coords), uvec4(r, g, b, a));
}

View File

@@ -669,6 +669,138 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
0x35, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x35, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
}; };
public static readonly byte[] ColorCopyShorteningComputeShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x79, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74,
0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00,
0x73, 0x72, 0x63, 0x00, 0x05, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69,
0x6F, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00,
0x64, 0x73, 0x74, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x36, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x76, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00,
0x36, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00,
0x38, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x51, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x5E, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x5F, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
0xF7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x4F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00,
0x13, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x67, 0x00, 0x05, 0x00,
0x10, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00,
0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x26, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x27, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x27, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x31, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x77, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00,
0xC4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
0x41, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00,
0x3C, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
0x4E, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x56, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x61, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00,
0x65, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00,
0x66, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00,
0x6A, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00,
0x66, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00,
0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
0x5D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00,
0x6F, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00,
0x70, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x72, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0x56, 0x00, 0x00, 0x00,
0x74, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00,
0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x77, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x77, 0x00, 0x00, 0x00,
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
public static readonly byte[] ColorCopyToNonMsComputeShaderSource = new byte[] public static readonly byte[] ColorCopyToNonMsComputeShaderSource = new byte[]
{ {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x86, 0x00, 0x00, 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x86, 0x00, 0x00, 0x00,
@@ -801,6 +933,133 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
0x84, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x84, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
}; };
public static readonly byte[] ColorCopyWideningComputeShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x72, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x32, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
0x42, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74,
0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00,
0x64, 0x73, 0x74, 0x00, 0x05, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69,
0x6F, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00,
0x73, 0x72, 0x63, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x37, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00,
0x3F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1B, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00,
0x10, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00,
0x58, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x61, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00,
0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00,
0x25, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
0xF7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00,
0x19, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x71, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x4F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x13, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x68, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x1D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x24, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x7C, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x18, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00,
0x38, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x3A, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00,
0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00,
0x47, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00,
0x49, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00,
0x36, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00,
0x3F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00,
0x48, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00,
0x43, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x48, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
0x63, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
0x62, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00,
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0x66, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x48, 0x00, 0x00, 0x00,
0x6D, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00,
0x65, 0x00, 0x00, 0x00, 0x63, 0x00, 0x05, 0x00, 0x66, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00,
0x6D, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x70, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
public static readonly byte[] ColorDrawToMsVertexShaderSource = new byte[] public static readonly byte[] ColorDrawToMsVertexShaderSource = new byte[]
{ {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x2E, 0x00, 0x00, 0x00,

View File

@@ -55,7 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe TextureStorage( public unsafe TextureStorage(
VulkanRenderer gd, VulkanRenderer gd,
PhysicalDevice physicalDevice,
Device device, Device device,
TextureCreateInfo info, TextureCreateInfo info,
float scaleFactor, float scaleFactor,
@@ -118,7 +117,7 @@ namespace Ryujinx.Graphics.Vulkan
if (foreignAllocation == null) if (foreignAllocation == null)
{ {
gd.Api.GetImageMemoryRequirements(device, _image, out var requirements); gd.Api.GetImageMemoryRequirements(device, _image, out var requirements);
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(physicalDevice, requirements, DefaultImageMemoryFlags); var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, DefaultImageMemoryFlags);
if (allocation.Memory.Handle == 0UL) if (allocation.Memory.Handle == 0UL)
{ {
@@ -173,7 +172,7 @@ namespace Ryujinx.Graphics.Vulkan
var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel); var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel);
storage = new TextureStorage(_gd, default, _device, info, ScaleFactor, _allocationAuto); storage = new TextureStorage(_gd, _device, info, ScaleFactor, _allocationAuto);
_aliasedStorages.Add(format, storage); _aliasedStorages.Add(format, storage);
} }

View File

@@ -199,6 +199,12 @@ namespace Ryujinx.Graphics.Vulkan
int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers); _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers);
} }
else if (dst.Info.BytesPerPixel != Info.BytesPerPixel)
{
int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel);
_gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels);
}
else else
{ {
TextureCopy.Copy( TextureCopy.Copy(
@@ -244,6 +250,10 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1);
} }
else if (dst.Info.BytesPerPixel != Info.BytesPerPixel)
{
_gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
}
else else
{ {
TextureCopy.Copy( TextureCopy.Copy(

View File

@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
ExtTransformFeedback.ExtensionName, ExtTransformFeedback.ExtensionName,
KhrDrawIndirectCount.ExtensionName, KhrDrawIndirectCount.ExtensionName,
KhrPushDescriptor.ExtensionName, KhrPushDescriptor.ExtensionName,
"VK_EXT_blend_operation_advanced",
"VK_EXT_custom_border_color", "VK_EXT_custom_border_color",
"VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV. "VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV.
"VK_EXT_fragment_shader_interlock", "VK_EXT_fragment_shader_interlock",
@@ -36,7 +37,8 @@ namespace Ryujinx.Graphics.Vulkan
"VK_KHR_shader_float16_int8", "VK_KHR_shader_float16_int8",
"VK_EXT_shader_subgroup_ballot", "VK_EXT_shader_subgroup_ballot",
"VK_EXT_subgroup_size_control", "VK_EXT_subgroup_size_control",
"VK_NV_geometry_shader_passthrough" "VK_NV_geometry_shader_passthrough",
"VK_KHR_portability_subset", // By spec, we should enable this if present.
}; };
public static string[] RequiredExtensions { get; } = new string[] public static string[] RequiredExtensions { get; } = new string[]

View File

@@ -149,6 +149,19 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceProperties2 SType = StructureType.PhysicalDeviceProperties2
}; };
PhysicalDeviceBlendOperationAdvancedPropertiesEXT propertiesBlendOperationAdvanced = new PhysicalDeviceBlendOperationAdvancedPropertiesEXT()
{
SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt
};
bool supportsBlendOperationAdvanced = supportedExtensions.Contains("VK_EXT_blend_operation_advanced");
if (supportsBlendOperationAdvanced)
{
propertiesBlendOperationAdvanced.PNext = properties2.PNext;
properties2.PNext = &propertiesBlendOperationAdvanced;
}
PhysicalDeviceSubgroupSizeControlPropertiesEXT propertiesSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlPropertiesEXT() PhysicalDeviceSubgroupSizeControlPropertiesEXT propertiesSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlPropertiesEXT()
{ {
SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt
@@ -234,19 +247,21 @@ namespace Ryujinx.Graphics.Vulkan
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
var portabilityFlags = PortabilitySubsetFlags.None; var portabilityFlags = PortabilitySubsetFlags.None;
uint vertexBufferAlignment = 1;
if (usePortability) if (usePortability)
{ {
portabilityFlags |= propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment > 1 ? PortabilitySubsetFlags.VertexBufferAlignment4B : 0; vertexBufferAlignment = propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment;
portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans; portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans;
portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode; portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode;
portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView; portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView;
portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias; portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
} }
bool customBorderColorSupported = supportedExtensions.Contains("VK_EXT_custom_border_color") && bool supportsCustomBorderColor = supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColor.CustomBorderColors && featuresCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColor.CustomBorderColorWithoutFormat; featuresCustomBorderColor.CustomBorderColorWithoutFormat;
ref var properties = ref properties2.Properties; ref var properties = ref properties2.Properties;
@@ -257,7 +272,11 @@ namespace Ryujinx.Graphics.Vulkan
Capabilities = new HardwareCapabilities( Capabilities = new HardwareCapabilities(
supportedExtensions.Contains("VK_EXT_index_type_uint8"), supportedExtensions.Contains("VK_EXT_index_type_uint8"),
customBorderColorSupported, supportsCustomBorderColor,
supportsBlendOperationAdvanced,
propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap,
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor,
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor,
supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"), supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
@@ -278,9 +297,10 @@ namespace Ryujinx.Graphics.Vulkan
propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize,
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
supportedSampleCounts, supportedSampleCounts,
portabilityFlags); portabilityFlags,
vertexBufferAlignment);
MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount); MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
@@ -290,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan
BackgroundResources = new BackgroundResources(this, _device); BackgroundResources = new BackgroundResources(this, _device);
BufferManager = new BufferManager(this, _physicalDevice, _device); BufferManager = new BufferManager(this, _device);
_syncManager = new SyncManager(this, _device); _syncManager = new SyncManager(this, _device);
_pipeline = new PipelineFull(this, _device); _pipeline = new PipelineFull(this, _device);
@@ -388,7 +408,7 @@ namespace Ryujinx.Graphics.Vulkan
internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale) internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale)
{ {
return new TextureStorage(this, _physicalDevice, _device, info, scale); return new TextureStorage(this, _device, info, scale);
} }
public void DeleteBuffer(BufferHandle buffer) public void DeleteBuffer(BufferHandle buffer)
@@ -523,6 +543,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsR4G4B4A4Format: supportsR4G4B4A4Format, supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsSnormBufferTextureFormat: true, supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat, supports5BitComponentFormat: supports5BitComponentFormat,
supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock, supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false, supportsFragmentShaderOrderingIntel: false,
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
@@ -636,11 +657,11 @@ namespace Ryujinx.Graphics.Vulkan
PrintGpuInformation(); PrintGpuInformation();
} }
public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment) internal bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
{ {
if (Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B)) if (Capabilities.VertexBufferAlignment > 1)
{ {
alignment = 4; alignment = (int)Capabilities.VertexBufferAlignment;
return true; return true;
} }

View File

@@ -96,7 +96,7 @@ namespace Ryujinx.Memory.Tests
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -34,8 +34,8 @@ namespace Ryujinx.Memory.Tests
private IMultiRegionHandle GetGranular(bool smart, ulong address, ulong size, ulong granularity) private IMultiRegionHandle GetGranular(bool smart, ulong address, ulong size, ulong granularity)
{ {
return smart ? return smart ?
_tracking.BeginSmartGranularTracking(address, size, granularity) : _tracking.BeginSmartGranularTracking(address, size, granularity, 0) :
(IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity); (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity, 0);
} }
private void RandomOrder(Random random, List<int> indices, Action<int> action) private void RandomOrder(Random random, List<int> indices, Action<int> action)
@@ -216,7 +216,7 @@ namespace Ryujinx.Memory.Tests
{ {
int region = regionSizes[i]; int region = regionSizes[i];
handle.QueryModified(address, (ulong)(PageSize * region), (address, size) => { }); handle.QueryModified(address, (ulong)(PageSize * region), (address, size) => { });
// There should be a gap between regions, // There should be a gap between regions,
// So that they don't combine and we can see the full effects. // So that they don't combine and we can see the full effects.
address += (ulong)(PageSize * (region + 1)); address += (ulong)(PageSize * (region + 1));
@@ -294,7 +294,7 @@ namespace Ryujinx.Memory.Tests
bool[] actionsTriggered = new bool[3]; bool[] actionsTriggered = new bool[3];
MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0);
PreparePages(granular, 3, PageSize * 3); PreparePages(granular, 3, PageSize * 3);
// Write to the second handle in the multiregion. // Write to the second handle in the multiregion.
@@ -307,7 +307,7 @@ namespace Ryujinx.Memory.Tests
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize); singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize, 0);
singlePages[i].Reprotect(); singlePages[i].Reprotect();
} }
@@ -321,7 +321,7 @@ namespace Ryujinx.Memory.Tests
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2); doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2, 0);
doublePages[i].Reprotect(); doublePages[i].Reprotect();
} }
@@ -340,7 +340,7 @@ namespace Ryujinx.Memory.Tests
doublePages doublePages
}; };
MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize); MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize, 0);
bool[] expectedDirty = new bool[] bool[] expectedDirty = new bool[]
{ {
@@ -405,7 +405,7 @@ namespace Ryujinx.Memory.Tests
{ {
bool actionTriggered = false; bool actionTriggered = false;
MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0);
PreparePages(granular, 3, PageSize * 3); PreparePages(granular, 3, PageSize * 3);
// Add a precise action to the second and third handle in the multiregion. // Add a precise action to the second and third handle in the multiregion.

View File

@@ -44,7 +44,7 @@ namespace Ryujinx.Memory.Tests
[Test] [Test]
public void SingleRegion() public void SingleRegion()
{ {
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
(ulong address, ulong size)? readTrackingTriggered = null; (ulong address, ulong size)? readTrackingTriggered = null;
handle.RegisterAction((address, size) => handle.RegisterAction((address, size) =>
{ {
@@ -97,7 +97,7 @@ namespace Ryujinx.Memory.Tests
[Test] [Test]
public void OverlappingRegions() public void OverlappingRegions()
{ {
RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16); RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16, 0);
allHandle.Reprotect(); allHandle.Reprotect();
(ulong address, ulong size)? readTrackingTriggeredAll = null; (ulong address, ulong size)? readTrackingTriggeredAll = null;
@@ -116,7 +116,7 @@ namespace Ryujinx.Memory.Tests
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0);
containedHandles[i].Reprotect(); containedHandles[i].Reprotect();
} }
@@ -163,7 +163,7 @@ namespace Ryujinx.Memory.Tests
ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize; ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize;
ulong alignedSize = alignedEnd - alignedStart; ulong alignedSize = alignedEnd - alignedStart;
RegionHandle handle = _tracking.BeginTracking(address, size); RegionHandle handle = _tracking.BeginTracking(address, size, 0);
// Anywhere inside the pages the region is contained on should trigger. // Anywhere inside the pages the region is contained on should trigger.
@@ -207,7 +207,7 @@ namespace Ryujinx.Memory.Tests
for (int i = 0; i < handles.Length; i++) for (int i = 0; i < handles.Length; i++)
{ {
handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0);
handles[i].Reprotect(); handles[i].Reprotect();
} }
@@ -263,7 +263,7 @@ namespace Ryujinx.Memory.Tests
Random random = new Random(randSeed + 512); Random random = new Random(randSeed + 512);
while (Stopwatch.GetTimestamp() < finishedTime) while (Stopwatch.GetTimestamp() < finishedTime)
{ {
RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536)); RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536), 0);
handle.Dispose(); handle.Dispose();
@@ -295,7 +295,7 @@ namespace Ryujinx.Memory.Tests
// Read actions should only be triggered once for each registration. // Read actions should only be triggered once for each registration.
// The implementation should use an interlocked exchange to make sure other threads can't get the action. // The implementation should use an interlocked exchange to make sure other threads can't get the action.
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
int triggeredCount = 0; int triggeredCount = 0;
int registeredCount = 0; int registeredCount = 0;
@@ -359,7 +359,7 @@ namespace Ryujinx.Memory.Tests
{ {
// Ensure that disposed handles correctly remove their virtual and physical regions. // Ensure that disposed handles correctly remove their virtual and physical regions.
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
handle.Reprotect(); handle.Reprotect();
Assert.AreEqual(1, _tracking.GetRegionCount()); Assert.AreEqual(1, _tracking.GetRegionCount());
@@ -372,8 +372,8 @@ namespace Ryujinx.Memory.Tests
// We expect there to be three regions after creating both, one for the small region and two covering the big one around it. // We expect there to be three regions after creating both, one for the small region and two covering the big one around it.
// Regions are always split to avoid overlapping, which is why there are three instead of two. // Regions are always split to avoid overlapping, which is why there are three instead of two.
RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize); RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize, 0);
RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4); RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4, 0);
Assert.AreEqual(3, _tracking.GetRegionCount()); Assert.AreEqual(3, _tracking.GetRegionCount());
@@ -398,7 +398,7 @@ namespace Ryujinx.Memory.Tests
protection = newProtection; protection = newProtection;
}; };
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
// After creating the handle, there is no protection yet. // After creating the handle, there is no protection yet.
Assert.AreEqual(MemoryPermission.ReadAndWrite, protection); Assert.AreEqual(MemoryPermission.ReadAndWrite, protection);
@@ -453,7 +453,7 @@ namespace Ryujinx.Memory.Tests
[Test] [Test]
public void PreciseAction() public void PreciseAction()
{ {
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
(ulong address, ulong size, bool write)? preciseTriggered = null; (ulong address, ulong size, bool write)? preciseTriggered = null;
handle.RegisterPreciseAction((address, size, write) => handle.RegisterPreciseAction((address, size, write) =>

View File

@@ -462,7 +462,7 @@ namespace Ryujinx.Memory
} }
/// <inheritdoc/> /// <inheritdoc/>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
// Only the ARM Memory Manager has tracking for now. // Only the ARM Memory Manager has tracking for now.
} }

View File

@@ -175,7 +175,8 @@ namespace Ryujinx.Memory
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="write">True if the region was written, false if read</param> /// <param name="write">True if the region was written, false if read</param>
/// <param name="precise">True if the access is precise, false otherwise</param> /// <param name="precise">True if the access is precise, false otherwise</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false); /// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
/// <summary> /// <summary>
/// Reprotect a region of virtual memory for tracking. /// Reprotect a region of virtual memory for tracking.

View File

@@ -97,10 +97,8 @@ namespace Ryujinx.Memory.Range
/// <returns>The new region (high part)</returns> /// <returns>The new region (high part)</returns>
private T Split(T region, ulong splitAddress) private T Split(T region, ulong splitAddress)
{ {
Remove(region);
T newRegion = (T)region.Split(splitAddress); T newRegion = (T)region.Split(splitAddress);
Add(region); Update(region);
Add(newRegion); Add(newRegion);
return newRegion; return newRegion;
} }

View File

@@ -67,6 +67,43 @@ namespace Ryujinx.Memory.Range
Insert(index, new RangeItem<T>(item)); Insert(index, new RangeItem<T>(item));
} }
/// <summary>
/// Updates an item's end address on the list. Address must be the same.
/// </summary>
/// <param name="item">The item to be updated</param>
/// <returns>True if the item was located and updated, false otherwise</returns>
public bool Update(T item)
{
int index = BinarySearch(item.Address);
if (index >= 0)
{
while (index > 0 && _items[index - 1].Address == item.Address)
{
index--;
}
while (index < Count)
{
if (_items[index].Value.Equals(item))
{
_items[index] = new RangeItem<T>(item);
return true;
}
if (_items[index].Address > item.Address)
{
break;
}
index++;
}
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Insert(int index, RangeItem<T> item) private void Insert(int index, RangeItem<T> item)
{ {

View File

@@ -50,7 +50,8 @@ namespace Ryujinx.Memory.Tracking
/// <param name="address">Address accessed</param> /// <param name="address">Address accessed</param>
/// <param name="size">Size of the region affected in bytes</param> /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param> /// <param name="write">Whether the region was written to or read</param>
public abstract void Signal(ulong address, ulong size, bool write); /// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
public abstract void Signal(ulong address, ulong size, bool write, int? exemptId);
/// <summary> /// <summary>
/// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained. /// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained.
@@ -58,10 +59,11 @@ namespace Ryujinx.Memory.Tracking
/// <param name="address">Address accessed</param> /// <param name="address">Address accessed</param>
/// <param name="size">Size of the region affected in bytes</param> /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param> /// <param name="write">Whether the region was written to or read</param>
public abstract void SignalPrecise(ulong address, ulong size, bool write); /// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
public abstract void SignalPrecise(ulong address, ulong size, bool write, int? exemptId);
/// <summary> /// <summary>
/// Split this region into two, around the specified address. /// Split this region into two, around the specified address.
/// This region is updated to end at the split address, and a new region is created to represent past that point. /// This region is updated to end at the split address, and a new region is created to represent past that point.
/// </summary> /// </summary>
/// <param name="splitAddress">Address to split the region around</param> /// <param name="splitAddress">Address to split the region around</param>

View File

@@ -136,10 +136,11 @@ namespace Ryujinx.Memory.Tracking
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param> /// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new MultiRegionHandle(this, address, size, handles, granularity); return new MultiRegionHandle(this, address, size, handles, granularity, id);
} }
/// <summary> /// <summary>
@@ -148,12 +149,13 @@ namespace Ryujinx.Memory.Tracking
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
(address, size) = PageAlign(address, size); (address, size) = PageAlign(address, size);
return new SmartMultiRegionHandle(this, address, size, granularity); return new SmartMultiRegionHandle(this, address, size, granularity, id);
} }
/// <summary> /// <summary>
@@ -161,14 +163,16 @@ namespace Ryujinx.Memory.Tracking
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public RegionHandle BeginTracking(ulong address, ulong size) public RegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
var (paAddress, paSize) = PageAlign(address, size); var (paAddress, paSize) = PageAlign(address, size);
lock (TrackingLock) lock (TrackingLock)
{ {
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, _memoryManager.IsRangeMapped(address, size)); bool mapped = _memoryManager.IsRangeMapped(address, size);
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, id, mapped);
return handle; return handle;
} }
@@ -181,28 +185,31 @@ namespace Ryujinx.Memory.Tracking
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="bitmap">The bitmap owning the dirty flag for this handle</param> /// <param name="bitmap">The bitmap owning the dirty flag for this handle</param>
/// <param name="bit">The bit of this handle within the dirty flag</param> /// <param name="bit">The bit of this handle within the dirty flag</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit) internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit, int id)
{ {
var (paAddress, paSize) = PageAlign(address, size); var (paAddress, paSize) = PageAlign(address, size);
lock (TrackingLock) lock (TrackingLock)
{ {
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size)); bool mapped = _memoryManager.IsRangeMapped(address, size);
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, id, mapped);
return handle; return handle;
} }
} }
/// <summary> /// <summary>
/// Signal that a virtual memory event happened at the given location (one byte). /// Signal that a virtual memory event happened at the given location.
/// </summary> /// </summary>
/// <param name="address">Virtual address accessed</param> /// <param name="address">Virtual address accessed</param>
/// <param name="write">Whether the address was written to or read</param> /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param>
/// <returns>True if the event triggered any tracking regions, false otherwise</returns> /// <returns>True if the event triggered any tracking regions, false otherwise</returns>
public bool VirtualMemoryEventTracking(ulong address, bool write) public bool VirtualMemoryEvent(ulong address, ulong size, bool write)
{ {
return VirtualMemoryEvent(address, 1, write); return VirtualMemoryEvent(address, size, write, precise: false, null);
} }
/// <summary> /// <summary>
@@ -214,8 +221,9 @@ namespace Ryujinx.Memory.Tracking
/// <param name="size">Size of the region affected in bytes</param> /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param> /// <param name="write">Whether the region was written to or read</param>
/// <param name="precise">True if the access is precise, false otherwise</param> /// <param name="precise">True if the access is precise, false otherwise</param>
/// <param name="exemptId">Optional ID that of the handles that should not be signalled</param>
/// <returns>True if the event triggered any tracking regions, false otherwise</returns> /// <returns>True if the event triggered any tracking regions, false otherwise</returns>
public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise = false) public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise, int? exemptId = null)
{ {
// Look up the virtual region using the region list. // Look up the virtual region using the region list.
// Signal up the chain to relevant handles. // Signal up the chain to relevant handles.
@@ -250,11 +258,11 @@ namespace Ryujinx.Memory.Tracking
if (precise) if (precise)
{ {
region.SignalPrecise(address, size, write); region.SignalPrecise(address, size, write, exemptId);
} }
else else
{ {
region.Signal(address, size, write); region.Signal(address, size, write, exemptId);
} }
} }
} }

View File

@@ -30,7 +30,13 @@ namespace Ryujinx.Memory.Tracking
public bool Dirty { get; private set; } = true; public bool Dirty { get; private set; } = true;
internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) internal MultiRegionHandle(
MemoryTracking tracking,
ulong address,
ulong size,
IEnumerable<IRegionHandle> handles,
ulong granularity,
int id)
{ {
_handles = new RegionHandle[(size + granularity - 1) / granularity]; _handles = new RegionHandle[(size + granularity - 1) / granularity];
Granularity = granularity; Granularity = granularity;
@@ -55,7 +61,7 @@ namespace Ryujinx.Memory.Tracking
// Fill any gap left before this handle. // Fill any gap left before this handle.
while (i < startIndex) while (i < startIndex)
{ {
RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
fillHandle.Parent = this; fillHandle.Parent = this;
_handles[i++] = fillHandle; _handles[i++] = fillHandle;
} }
@@ -76,7 +82,7 @@ namespace Ryujinx.Memory.Tracking
while (i < endIndex) while (i < endIndex)
{ {
RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
splitHandle.Parent = this; splitHandle.Parent = this;
splitHandle.Reprotect(handle.Dirty); splitHandle.Reprotect(handle.Dirty);
@@ -99,7 +105,7 @@ namespace Ryujinx.Memory.Tracking
// Fill any remaining space with new handles. // Fill any remaining space with new handles.
while (i < _handles.Length) while (i < _handles.Length)
{ {
RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
handle.Parent = this; handle.Parent = this;
_handles[i++] = handle; _handles[i++] = handle;
} }

View File

@@ -15,12 +15,12 @@ namespace Ryujinx.Memory.Tracking
/// If more than this number of checks have been performed on a dirty flag since its last reprotect, /// If more than this number of checks have been performed on a dirty flag since its last reprotect,
/// then it is dirtied infrequently. /// then it is dirtied infrequently.
/// </summary> /// </summary>
private static int CheckCountForInfrequent = 3; private const int CheckCountForInfrequent = 3;
/// <summary> /// <summary>
/// Number of frequent dirty/consume in a row to make this handle volatile. /// Number of frequent dirty/consume in a row to make this handle volatile.
/// </summary> /// </summary>
private static int VolatileThreshold = 5; private const int VolatileThreshold = 5;
public bool Dirty public bool Dirty
{ {
@@ -35,6 +35,7 @@ namespace Ryujinx.Memory.Tracking
} }
internal int SequenceNumber { get; set; } internal int SequenceNumber { get; set; }
internal int Id { get; }
public bool Unmapped { get; private set; } public bool Unmapped { get; private set; }
@@ -97,14 +98,26 @@ namespace Ryujinx.Memory.Tracking
/// <param name="realSize">The real, unaligned size of the handle</param> /// <param name="realSize">The real, unaligned size of the handle</param>
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param> /// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
/// <param name="bit">The bit index representing the dirty flag for this handle</param> /// <param name="bit">The bit index representing the dirty flag for this handle</param>
/// <param name="id">Handle ID</param>
/// <param name="mapped">True if the region handle starts mapped</param> /// <param name="mapped">True if the region handle starts mapped</param>
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, ConcurrentBitmap bitmap, int bit, bool mapped = true) internal RegionHandle(
MemoryTracking tracking,
ulong address,
ulong size,
ulong realAddress,
ulong realSize,
ConcurrentBitmap bitmap,
int bit,
int id,
bool mapped = true)
{ {
Bitmap = bitmap; Bitmap = bitmap;
DirtyBit = bit; DirtyBit = bit;
Dirty = mapped; Dirty = mapped;
Id = id;
Unmapped = !mapped; Unmapped = !mapped;
Address = address; Address = address;
Size = size; Size = size;
@@ -131,11 +144,14 @@ namespace Ryujinx.Memory.Tracking
/// <param name="size">Size of the region to track</param> /// <param name="size">Size of the region to track</param>
/// <param name="realAddress">The real, unaligned address of the handle</param> /// <param name="realAddress">The real, unaligned address of the handle</param>
/// <param name="realSize">The real, unaligned size of the handle</param> /// <param name="realSize">The real, unaligned size of the handle</param>
/// <param name="id">Handle ID</param>
/// <param name="mapped">True if the region handle starts mapped</param> /// <param name="mapped">True if the region handle starts mapped</param>
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, bool mapped = true) internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, int id, bool mapped = true)
{ {
Bitmap = new ConcurrentBitmap(1, mapped); Bitmap = new ConcurrentBitmap(1, mapped);
Id = id;
Unmapped = !mapped; Unmapped = !mapped;
Address = address; Address = address;

View File

@@ -18,10 +18,11 @@ namespace Ryujinx.Memory.Tracking
private readonly ulong _granularity; private readonly ulong _granularity;
private readonly ulong _size; private readonly ulong _size;
private MemoryTracking _tracking; private MemoryTracking _tracking;
private readonly int _id;
public bool Dirty { get; private set; } = true; public bool Dirty { get; private set; } = true;
internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity) internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity, int id)
{ {
// For this multi-region handle, the handle list starts empty. // For this multi-region handle, the handle list starts empty.
// As regions are queried, they are added to the _handles array at their start index. // As regions are queried, they are added to the _handles array at their start index.
@@ -34,6 +35,7 @@ namespace Ryujinx.Memory.Tracking
_address = address; _address = address;
_size = size; _size = size;
_id = id;
} }
public void SignalWrite() public void SignalWrite()
@@ -102,7 +104,7 @@ namespace Ryujinx.Memory.Tracking
RegionSignal signal = handle.PreAction; RegionSignal signal = handle.PreAction;
handle.Dispose(); handle.Dispose();
RegionHandle splitLow = _tracking.BeginTracking(address, size); RegionHandle splitLow = _tracking.BeginTracking(address, size, _id);
splitLow.Parent = this; splitLow.Parent = this;
if (signal != null) if (signal != null)
{ {
@@ -110,7 +112,7 @@ namespace Ryujinx.Memory.Tracking
} }
_handles[handleIndex] = splitLow; _handles[handleIndex] = splitLow;
RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size); RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size, _id);
splitHigh.Parent = this; splitHigh.Parent = this;
if (signal != null) if (signal != null)
{ {
@@ -145,7 +147,7 @@ namespace Ryujinx.Memory.Tracking
if (handle != null) if (handle != null)
{ {
// Fill up to the found handle. // Fill up to the found handle.
handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle)); handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle), _id);
handle.Parent = this; handle.Parent = this;
_handles[startHandle] = handle; _handles[startHandle] = handle;
return; return;
@@ -153,7 +155,7 @@ namespace Ryujinx.Memory.Tracking
} }
// Can fill the whole range. // Can fill the whole range.
_handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle)); _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle), _id);
_handles[startHandle].Parent = this; _handles[startHandle].Parent = this;
} }

View File

@@ -19,19 +19,24 @@ namespace Ryujinx.Memory.Tracking
_tracking = tracking; _tracking = tracking;
} }
public override void Signal(ulong address, ulong size, bool write) /// <inheritdoc/>
public override void Signal(ulong address, ulong size, bool write, int? exemptId)
{ {
IList<RegionHandle> handles = Handles; IList<RegionHandle> handles = Handles;
for (int i = 0; i < handles.Count; i++) for (int i = 0; i < handles.Count; i++)
{ {
handles[i].Signal(address, size, write, ref handles); if (exemptId == null || handles[i].Id != exemptId.Value)
{
handles[i].Signal(address, size, write, ref handles);
}
} }
UpdateProtection(); UpdateProtection();
} }
public override void SignalPrecise(ulong address, ulong size, bool write) /// <inheritdoc/>
public override void SignalPrecise(ulong address, ulong size, bool write, int? exemptId)
{ {
IList<RegionHandle> handles = Handles; IList<RegionHandle> handles = Handles;
@@ -39,7 +44,10 @@ namespace Ryujinx.Memory.Tracking
for (int i = 0; i < handles.Count; i++) for (int i = 0; i < handles.Count; i++)
{ {
allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles); if (exemptId == null || handles[i].Id != exemptId.Value)
{
allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles);
}
} }
// Only update protection if a regular signal handler was called. // Only update protection if a regular signal handler was called.

View File

@@ -0,0 +1,93 @@
using NUnit.Framework;
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Server.Upsampler;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Ryujinx.Tests.Audio.Renderer.Dsp
{
class ResamplerTests
{
[Test]
[TestCase(VoiceInParameter.SampleRateConversionQuality.Low)]
[TestCase(VoiceInParameter.SampleRateConversionQuality.Default)]
[TestCase(VoiceInParameter.SampleRateConversionQuality.High)]
public void TestResamplerConsistencyUpsampling(VoiceInParameter.SampleRateConversionQuality quality)
{
DoResamplingTest(44100, 48000, quality);
}
[Test]
[TestCase(VoiceInParameter.SampleRateConversionQuality.Low)]
[TestCase(VoiceInParameter.SampleRateConversionQuality.Default)]
[TestCase(VoiceInParameter.SampleRateConversionQuality.High)]
public void TestResamplerConsistencyDownsampling(VoiceInParameter.SampleRateConversionQuality quality)
{
DoResamplingTest(48000, 44100, quality);
}
/// <summary>
/// Generates a 1-second sine wave sample at input rate, resamples it to output rate, and
/// ensures that it resampled at the expected rate with no discontinuities
/// </summary>
/// <param name="inputRate">The input sample rate to test</param>
/// <param name="outputRate">The output sample rate to test</param>
/// <param name="quality">The resampler quality to use</param>
private static void DoResamplingTest(int inputRate, int outputRate, VoiceInParameter.SampleRateConversionQuality quality)
{
float inputSampleRate = (float)inputRate;
float outputSampleRate = (float)outputRate;
int inputSampleCount = inputRate;
int outputSampleCount = outputRate;
short[] inputBuffer = new short[inputSampleCount + 100]; // add some safety buffer at the end
float[] outputBuffer = new float[outputSampleCount + 100];
for (int sample = 0; sample < inputBuffer.Length; sample++)
{
// 440 hz sine wave with amplitude = 0.5f at input sample rate
inputBuffer[sample] = (short)(32767 * MathF.Sin((440 / inputSampleRate) * (float)sample * MathF.PI * 2f) * 0.5f);
}
float fraction = 0;
ResamplerHelper.Resample(
outputBuffer.AsSpan(),
inputBuffer.AsSpan(),
inputSampleRate / outputSampleRate,
ref fraction,
outputSampleCount,
quality,
false);
float[] expectedOutput = new float[outputSampleCount];
float sumDifference = 0;
int delay = quality switch
{
VoiceInParameter.SampleRateConversionQuality.High => 3,
VoiceInParameter.SampleRateConversionQuality.Default => 1,
_ => 0
};
for (int sample = 0; sample < outputSampleCount; sample++)
{
outputBuffer[sample] /= 32767;
// 440 hz sine wave with amplitude = 0.5f at output sample rate
expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (float)(sample + delay) * MathF.PI * 2f) * 0.5f;
float thisDelta = Math.Abs(expectedOutput[sample] - outputBuffer[sample]);
// Ensure no discontinuities
Assert.IsTrue(thisDelta < 0.1f);
sumDifference += thisDelta;
}
sumDifference = sumDifference / (float)outputSampleCount;
// Expect the output to be 99% similar to the expected resampled sine wave
Assert.IsTrue(sumDifference < 0.01f);
}
}
}

View File

@@ -0,0 +1,64 @@
using NUnit.Framework;
using Ryujinx.Audio.Renderer.Dsp;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Server.Upsampler;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Ryujinx.Tests.Audio.Renderer.Dsp
{
class UpsamplerTests
{
[Test]
public void TestUpsamplerConsistency()
{
UpsamplerBufferState bufferState = new UpsamplerBufferState();
int inputBlockSize = 160;
int numInputSamples = 32000;
int numOutputSamples = 48000;
float inputSampleRate = numInputSamples;
float outputSampleRate = numOutputSamples;
float[] inputBuffer = new float[numInputSamples + 100];
float[] outputBuffer = new float[numOutputSamples + 100];
for (int sample = 0; sample < inputBuffer.Length; sample++)
{
// 440 hz sine wave with amplitude = 0.5f at input sample rate
inputBuffer[sample] = MathF.Sin((440 / inputSampleRate) * (float)sample * MathF.PI * 2f) * 0.5f;
}
int inputIdx = 0;
int outputIdx = 0;
while (inputIdx + inputBlockSize < numInputSamples)
{
int outputBufLength = (int)Math.Round((float)(inputIdx + inputBlockSize) * outputSampleRate / inputSampleRate) - outputIdx;
UpsamplerHelper.Upsample(
outputBuffer.AsSpan(outputIdx),
inputBuffer.AsSpan(inputIdx),
outputBufLength,
inputBlockSize,
ref bufferState);
inputIdx += inputBlockSize;
outputIdx += outputBufLength;
}
float[] expectedOutput = new float[numOutputSamples];
float sumDifference = 0;
for (int sample = 0; sample < numOutputSamples; sample++)
{
// 440 hz sine wave with amplitude = 0.5f at output sample rate with an offset of 15
expectedOutput[sample] = MathF.Sin((440 / outputSampleRate) * (float)(sample - 15) * MathF.PI * 2f) * 0.5f;
sumDifference += Math.Abs(expectedOutput[sample] - outputBuffer[sample]);
}
sumDifference = sumDifference / (float)expectedOutput.Length;
// Expect the output to be 98% similar to the expected resampled sine wave
Assert.IsTrue(sumDifference < 0.02f);
}
}
}

View File

@@ -40,7 +40,7 @@ namespace Ryujinx.Tests.Memory
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -9,6 +9,7 @@ using Ryujinx.Ui;
using Ryujinx.Ui.Widgets; using Ryujinx.Ui.Widgets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -23,20 +24,20 @@ namespace Ryujinx.Modules
{ {
public static class Updater public static class Updater
{ {
private const string GitHubApiURL = "https://api.github.com";
private const int ConnectionCount = 4;
internal static bool Running; internal static bool Running;
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish");
private static readonly int ConnectionCount = 4;
private static string _buildVer; private static string _buildVer;
private static string _platformExt; private static string _platformExt;
private static string _buildUrl; private static string _buildUrl;
private static long _buildSize; private static long _buildSize;
private const string GitHubApiURL = "https://api.github.com";
// On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates.
private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" };
@@ -44,7 +45,7 @@ namespace Ryujinx.Modules
{ {
HttpClient result = new HttpClient(); HttpClient result = new HttpClient();
// Required by GitHub to interract with APIs. // Required by GitHub to interact with APIs.
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
return result; return result;
@@ -101,50 +102,48 @@ namespace Ryujinx.Modules
// Get latest version number from GitHub API // Get latest version number from GitHub API
try try
{ {
using (HttpClient jsonClient = ConstructHttpClient()) using HttpClient jsonClient = ConstructHttpClient();
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
// Fetch latest build information
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
JObject jsonRoot = JObject.Parse(fetchedJson);
JToken assets = jsonRoot["assets"];
_buildVer = (string)jsonRoot["name"];
foreach (JToken asset in assets)
{ {
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string assetName = (string)asset["name"];
string assetState = (string)asset["state"];
string downloadURL = (string)asset["browser_download_url"];
// Fetch latest build information if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt))
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
JObject jsonRoot = JObject.Parse(fetchedJson);
JToken assets = jsonRoot["assets"];
_buildVer = (string)jsonRoot["name"];
foreach (JToken asset in assets)
{ {
string assetName = (string)asset["name"]; _buildUrl = downloadURL;
string assetState = (string)asset["state"];
string downloadURL = (string)asset["browser_download_url"];
if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt)) if (assetState != "uploaded")
{ {
_buildUrl = downloadURL; if (showVersionUpToDate)
if (assetState != "uploaded")
{ {
if (showVersionUpToDate) GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", "");
{
GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", "");
}
return;
} }
break; return;
} }
}
if (_buildUrl == null) break;
}
}
if (_buildUrl == null)
{
if (showVersionUpToDate)
{ {
if (showVersionUpToDate) GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", "");
{
GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", "");
}
return;
} }
return;
} }
} }
catch (Exception exception) catch (Exception exception)
@@ -247,160 +246,142 @@ namespace Ryujinx.Modules
for (int i = 0; i < ConnectionCount; i++) for (int i = 0; i < ConnectionCount; i++)
{ {
list.Add(new byte[0]); list.Add(Array.Empty<byte>());
} }
for (int i = 0; i < ConnectionCount; i++) for (int i = 0; i < ConnectionCount; i++)
{ {
#pragma warning disable SYSLIB0014 #pragma warning disable SYSLIB0014
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
using (WebClient client = new WebClient()) using WebClient client = new WebClient();
#pragma warning restore SYSLIB0014 #pragma warning restore SYSLIB0014
webClients.Add(client);
if (i == ConnectionCount - 1)
{ {
webClients.Add(client); client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}");
}
else
{
client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}");
}
if (i == ConnectionCount - 1) client.DownloadProgressChanged += (_, args) =>
{
int index = (int)args.UserState;
Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]);
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount;
};
client.DownloadDataCompleted += (_, args) =>
{
int index = (int)args.UserState;
if (args.Cancelled)
{ {
client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); webClients[index].Dispose();
}
else
{
client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}");
}
client.DownloadProgressChanged += (_, args) =>
{
int index = (int)args.UserState;
Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]);
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount;
};
client.DownloadDataCompleted += (_, args) =>
{
int index = (int)args.UserState;
if (args.Cancelled)
{
webClients[index].Dispose();
return;
}
list[index] = args.Result;
Interlocked.Increment(ref completedRequests);
if (Equals(completedRequests, ConnectionCount))
{
byte[] mergedFileBytes = new byte[_buildSize];
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++)
{
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
destinationOffset += list[connectionIndex].Length;
}
File.WriteAllBytes(updateFile, mergedFileBytes);
try
{
InstallUpdate(updateDialog, updateFile);
}
catch (Exception e)
{
Logger.Warning?.Print(LogClass.Application, e.Message);
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile);
return;
}
}
};
try
{
client.DownloadDataAsync(new Uri(downloadUrl), i);
}
catch (WebException ex)
{
Logger.Warning?.Print(LogClass.Application, ex.Message);
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
for (int j = 0; j < webClients.Count; j++)
{
webClients[j].CancelAsync();
}
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile);
return; return;
} }
list[index] = args.Result;
Interlocked.Increment(ref completedRequests);
if (Equals(completedRequests, ConnectionCount))
{
byte[] mergedFileBytes = new byte[_buildSize];
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++)
{
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
destinationOffset += list[connectionIndex].Length;
}
File.WriteAllBytes(updateFile, mergedFileBytes);
try
{
InstallUpdate(updateDialog, updateFile);
}
catch (Exception e)
{
Logger.Warning?.Print(LogClass.Application, e.Message);
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile);
return;
}
}
};
try
{
client.DownloadDataAsync(new Uri(downloadUrl), i);
}
catch (WebException ex)
{
Logger.Warning?.Print(LogClass.Application, ex.Message);
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
foreach (WebClient webClient in webClients)
{
webClient.CancelAsync();
}
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile);
return;
} }
} }
} }
private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile) private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile)
{ {
using (HttpClient client = new HttpClient()) using HttpClient client = new HttpClient();
// We do not want to timeout while downloading
client.Timeout = TimeSpan.FromDays(1);
using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result)
using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result)
{ {
// We do not want to timeout while downloading using (Stream updateFileStream = File.Open(updateFile, FileMode.Create))
client.Timeout = TimeSpan.FromDays(1);
using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result)
using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result)
{ {
using (Stream updateFileStream = File.Open(updateFile, FileMode.Create)) long totalBytes = response.Content.Headers.ContentLength.Value;
long byteWritten = 0;
byte[] buffer = new byte[32 * 1024];
while (true)
{ {
long totalBytes = response.Content.Headers.ContentLength.Value; int readSize = remoteFileStream.Read(buffer);
long byteWritten = 0;
byte[] buffer = new byte[32 * 1024]; if (readSize == 0)
while (true)
{ {
int readSize = remoteFileStream.Read(buffer); break;
if (readSize == 0)
{
break;
}
byteWritten += readSize;
updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100;
updateFileStream.Write(buffer, 0, readSize);
} }
byteWritten += readSize;
updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100;
updateFileStream.Write(buffer, 0, readSize);
} }
} }
InstallUpdate(updateDialog, updateFile);
} }
InstallUpdate(updateDialog, updateFile);
} }
private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile)
{ {
Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)); Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile))
worker.Name = "Updater.SingleThreadWorker";
worker.Start();
}
private static void SetFileExecutable(string path)
{
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
UnixFileMode.UserWrite |
UnixFileMode.UserRead |
UnixFileMode.GroupRead |
UnixFileMode.GroupWrite |
UnixFileMode.OtherRead |
UnixFileMode.OtherWrite;
if (!OperatingSystem.IsWindows() && File.Exists(path))
{ {
File.SetUnixFileMode(path, ExecutableFileMode); Name = "Updater.SingleThreadWorker"
} };
worker.Start();
} }
private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile) private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile)
@@ -411,15 +392,17 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())
{ {
using (Stream inStream = File.OpenRead(updateFile)) using Stream inStream = File.OpenRead(updateFile);
using (Stream gzipStream = new GZipInputStream(inStream)) using Stream gzipStream = new GZipInputStream(inStream);
using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII)) using TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII);
{ updateDialog.ProgressBar.MaxValue = inStream.Length;
updateDialog.ProgressBar.MaxValue = inStream.Length;
await Task.Run(() => await Task.Run(() =>
{
TarEntry tarEntry;
if (!OperatingSystem.IsWindows())
{ {
TarEntry tarEntry;
while ((tarEntry = tarStream.GetNextEntry()) != null) while ((tarEntry = tarStream.GetNextEntry()) != null)
{ {
if (tarEntry.IsDirectory) continue; if (tarEntry.IsDirectory) continue;
@@ -433,6 +416,7 @@ namespace Ryujinx.Modules
tarStream.CopyEntryContents(outStream); tarStream.CopyEntryContents(outStream);
} }
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
TarEntry entry = tarEntry; TarEntry entry = tarEntry;
@@ -442,43 +426,41 @@ namespace Ryujinx.Modules
updateDialog.ProgressBar.Value += entry.Size; updateDialog.ProgressBar.Value += entry.Size;
}); });
} }
}); }
});
updateDialog.ProgressBar.Value = inStream.Length; updateDialog.ProgressBar.Value = inStream.Length;
}
} }
else else
{ {
using (Stream inStream = File.OpenRead(updateFile)) using Stream inStream = File.OpenRead(updateFile);
using (ZipFile zipFile = new ZipFile(inStream)) using ZipFile zipFile = new ZipFile(inStream);
updateDialog.ProgressBar.MaxValue = zipFile.Count;
await Task.Run(() =>
{ {
updateDialog.ProgressBar.MaxValue = zipFile.Count; foreach (ZipEntry zipEntry in zipFile)
await Task.Run(() =>
{ {
foreach (ZipEntry zipEntry in zipFile) if (zipEntry.IsDirectory) continue;
string outPath = Path.Combine(UpdateDir, zipEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (Stream zipStream = zipFile.GetInputStream(zipEntry))
using (FileStream outStream = File.OpenWrite(outPath))
{ {
if (zipEntry.IsDirectory) continue; zipStream.CopyTo(outStream);
string outPath = Path.Combine(UpdateDir, zipEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (Stream zipStream = zipFile.GetInputStream(zipEntry))
using (FileStream outStream = File.OpenWrite(outPath))
{
zipStream.CopyTo(outStream);
}
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
Application.Invoke(delegate
{
updateDialog.ProgressBar.Value++;
});
} }
});
} File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
Application.Invoke(delegate
{
updateDialog.ProgressBar.Value++;
});
}
});
} }
// Delete downloaded zip // Delete downloaded zip
@@ -522,8 +504,6 @@ namespace Ryujinx.Modules
Directory.Delete(UpdateDir, true); Directory.Delete(UpdateDir, true);
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
updateDialog.MainText.Text = "Update Complete!"; updateDialog.MainText.Text = "Update Complete!";
updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?"; updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?";
updateDialog.Modal = true; updateDialog.Modal = true;
@@ -640,4 +620,4 @@ namespace Ryujinx.Modules
} }
} }
} }
} }