Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
b8556530f2 | |||
4f3af839be | |||
155736c986 | |||
dba908dc78 | |||
ecee34a50c | |||
9b5a0c3889 | |||
80b4972139 | |||
5d85468302 | |||
9b1cc2cec6 | |||
e691622f0a | |||
f663a5cd38 | |||
f7c2e867f4 | |||
cedd200745 | |||
58207685c0 | |||
095ad923ad | |||
f07ae7d53f | |||
c308f09722 | |||
f1eef29409 | |||
1f8d66db7c | |||
c3a5716a95 | |||
1f1e2a7f03 | |||
e54f9dc4b4 | |||
edfd4d70c0 | |||
fc43aecbbd | |||
58d7a1fe97 | |||
7aa430f1a5 | |||
6bf460e104 | |||
efb135b74c | |||
a707842e14 | |||
a5a9b9bc8b | |||
17078ad929 | |||
32450d45de | |||
ed7a0474c6 | |||
fe9c49949a | |||
052b23c83c | |||
e4f68592c3 | |||
1dcd44b94f | |||
61b1ce252f | |||
5f38086f94 | |||
7bae440d3a | |||
f1943fd0b6 | |||
ec8d4f3af5 | |||
b3f0978869 | |||
f614d2c435 | |||
40c9416097 | |||
618c8edc79 | |||
99fc4fa61b | |||
f6d5499a16 | |||
26bf13a65d | |||
96cf242bcf | |||
59755818ef | |||
f8beeeb7d3 | |||
cb250162cb |
57
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
57
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,31 +1,27 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: File a bug report
|
description: File a bug report
|
||||||
|
title: "[Bug]"
|
||||||
|
labels: bug
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search to see if an issue already exists for the bug you encountered.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- 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:
|
||||||
@ -34,36 +30,51 @@ 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:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: cpu
|
||||||
|
attributes:
|
||||||
|
label: CPU
|
||||||
|
placeholder: "e.g. i7-6700"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: gpu
|
||||||
|
attributes:
|
||||||
|
label: GPU
|
||||||
|
placeholder: "e.g. NVIDIA RTX 2070"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: ram
|
||||||
|
attributes:
|
||||||
|
label: RAM
|
||||||
|
placeholder: "e.g. 16GB"
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: environment
|
id: mods
|
||||||
attributes:
|
attributes:
|
||||||
label: Environment?
|
label: List of applied mods
|
||||||
value: |
|
placeholder: You can list applied mods here.
|
||||||
- ##### CPU: *(e.g. i7-6700)*
|
|
||||||
- ##### GPU: *(e.g. NVIDIA RTX 2070)*
|
|
||||||
- ##### RAM: *(e.g. 16GB)*
|
|
||||||
- Applied Mods: [ Yes (Which ones) / No ]
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: additional-context
|
id: additional-context
|
||||||
attributes:
|
attributes:
|
||||||
|
12
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
12
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,13 +1,7 @@
|
|||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: Suggest a new feature for Ryujinx.
|
description: Suggest a new feature for Ryujinx.
|
||||||
|
title: "[Feature Request]"
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search to see if an issue already exists for the feature you are requesting.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: overview
|
id: overview
|
||||||
attributes:
|
attributes:
|
||||||
@ -18,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
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
name: Missing CPU Instruction
|
name: Missing CPU Instruction
|
||||||
description: CPU Instruction is missing in Ryujinx.
|
description: CPU Instruction is missing in Ryujinx.
|
||||||
|
title: "[CPU]"
|
||||||
|
labels: [cpu, not-implemented]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search for existing missing CPU instruction
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: instruction
|
id: instruction
|
||||||
attributes:
|
attributes:
|
||||||
|
10
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
10
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
@ -1,17 +1,11 @@
|
|||||||
name: Missing Service Call
|
name: Missing Service Call
|
||||||
description: Service call is missing in Ryujinx.
|
description: Service call is missing in Ryujinx.
|
||||||
|
labels: not-implemented
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this?
|
|
||||||
description: Please search for an [existing missing service call issue](https://github.com/Ryujinx/Ryujinx/issues) first.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- 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
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
using ARMeilleure.Decoders;
|
using ARMeilleure.Decoders;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHashHelper;
|
using static ARMeilleure.Instructions.InstEmitHashHelper;
|
||||||
|
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
|
@ -4,9 +4,8 @@ using ARMeilleure.IntermediateRepresentation;
|
|||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||||
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
|
@ -19,17 +19,17 @@
|
|||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
||||||
<PackageVersion Include="LibHac" Version="0.17.0" />
|
<PackageVersion Include="LibHac" Version="0.18.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.5.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" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
@ -44,11 +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.0" />
|
<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-f7c841d" />
|
||||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -96,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
|||||||
|
|
||||||
- **GPU**
|
- **GPU**
|
||||||
|
|
||||||
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently six graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Anti-Aliasing, Scaling Filters (including FSR), Anisotropic Filtering and Aspect Ratio Adjustment. These enhancements can be adjusted or toggled as desired in the GUI.
|
||||||
|
|
||||||
- **Input**
|
- **Input**
|
||||||
|
|
||||||
|
@ -5,9 +5,8 @@ using Ryujinx.Memory;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
|
||||||
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||||
|
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Backends.SoundIo
|
namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using System;
|
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||||
using System.Diagnostics;
|
|
||||||
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
|
||||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Audio.Renderer.Common;
|
using Ryujinx.Audio.Renderer.Common;
|
||||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
|
||||||
using Ryujinx.Audio.Renderer.Parameter;
|
using Ryujinx.Audio.Renderer.Parameter;
|
||||||
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Server.MemoryPool;
|
using Ryujinx.Audio.Renderer.Server.MemoryPool;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
@ -53,7 +53,6 @@ using Key = Ryujinx.Input.Key;
|
|||||||
using MouseButton = Ryujinx.Input.MouseButton;
|
using MouseButton = Ryujinx.Input.MouseButton;
|
||||||
using Size = Avalonia.Size;
|
using Size = Avalonia.Size;
|
||||||
using Switch = Ryujinx.HLE.Switch;
|
using Switch = Ryujinx.HLE.Switch;
|
||||||
using WindowState = Avalonia.Controls.WindowState;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava
|
namespace Ryujinx.Ava
|
||||||
{
|
{
|
||||||
@ -172,6 +171,11 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
||||||
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||||
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||||
|
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||||
|
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||||
|
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||||
|
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
}
|
}
|
||||||
@ -194,6 +198,17 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
|
||||||
|
{
|
||||||
|
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e)
|
||||||
|
{
|
||||||
|
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
|
}
|
||||||
|
|
||||||
private void ShowCursor()
|
private void ShowCursor()
|
||||||
{
|
{
|
||||||
@ -346,6 +361,11 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateAntiAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e)
|
||||||
|
{
|
||||||
|
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue);
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e)
|
private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e)
|
||||||
{
|
{
|
||||||
Device?.System.ChangeDockedModeState(e.NewValue);
|
Device?.System.ChangeDockedModeState(e.NewValue);
|
||||||
@ -412,6 +432,9 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
||||||
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
||||||
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
|
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
|
||||||
|
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
|
||||||
|
|
||||||
_topLevel.PointerMoved -= TopLevel_PointerMoved;
|
_topLevel.PointerMoved -= TopLevel_PointerMoved;
|
||||||
|
|
||||||
@ -766,7 +789,7 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void RenderLoop()
|
private void RenderLoop()
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
@ -789,6 +812,10 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
Device.Gpu.Renderer.Initialize(_glLogLevel);
|
Device.Gpu.Renderer.Initialize(_glLogLevel);
|
||||||
|
|
||||||
|
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value);
|
||||||
|
_renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
_renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
|
|
||||||
Width = (int)_rendererHost.Bounds.Width;
|
Width = (int)_rendererHost.Bounds.Width;
|
||||||
Height = (int)_rendererHost.Bounds.Height;
|
Height = (int)_rendererHost.Bounds.Height;
|
||||||
|
|
||||||
@ -802,6 +829,8 @@ namespace Ryujinx.Ava
|
|||||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||||
Translator.IsReadyForTranslation.Set();
|
Translator.IsReadyForTranslation.Set();
|
||||||
|
|
||||||
|
_renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
||||||
|
|
||||||
while (_isActive)
|
while (_isActive)
|
||||||
{
|
{
|
||||||
_ticks += _chrono.ElapsedTicks;
|
_ticks += _chrono.ElapsedTicks;
|
||||||
|
@ -626,6 +626,16 @@
|
|||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"UserProfilesRecoverHeading" : "Saves were found for the following accounts",
|
"UserProfilesRecoverHeading" : "Saves were found for the following accounts",
|
||||||
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
||||||
|
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
||||||
|
"GraphicsAALabel": "Anti-Aliasing:",
|
||||||
|
"GraphicsScalingFilterLabel": "Scaling Filter:",
|
||||||
|
"GraphicsScalingFilterTooltip": "Enables Framebuffer Scaling",
|
||||||
|
"GraphicsScalingFilterLevelLabel": "Level",
|
||||||
|
"GraphicsScalingFilterLevelTooltip": "Set Scaling Filter Level",
|
||||||
|
"SmaaLow": "SMAA Low",
|
||||||
|
"SmaaMedium": "SMAA Medium",
|
||||||
|
"SmaaHigh": "SMAA High",
|
||||||
|
"SmaaUltra": "SMAA Ultra",
|
||||||
"UserEditorTitle" : "Edit User",
|
"UserEditorTitle" : "Edit User",
|
||||||
"UserEditorTitleCreate" : "Create User"
|
"UserEditorTitleCreate" : "Create User"
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
{
|
{
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
using var ncaFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
|
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
|
||||||
if (nca.Header.ContentType == NcaContentType.Program)
|
if (nca.Header.ContentType == NcaContentType.Program)
|
||||||
@ -249,8 +249,8 @@ namespace Ryujinx.Ava.Common
|
|||||||
using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
|
using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
|
||||||
using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
|
using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
|
||||||
|
|
||||||
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref());
|
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
|
||||||
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref());
|
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
|
||||||
|
|
||||||
(Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);
|
(Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ using System.Net.Http;
|
|||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -57,7 +58,7 @@ namespace Ryujinx.Modules
|
|||||||
// Detect current platform
|
// Detect current platform
|
||||||
if (OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
_platformExt = "osx_x64.zip";
|
_platformExt = "macos_universal.app.tar.gz";
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsWindows())
|
else if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
@ -132,8 +133,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 +241,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;
|
||||||
|
|
||||||
@ -286,24 +287,40 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
if (_updateSuccessful)
|
if (_updateSuccessful)
|
||||||
{
|
{
|
||||||
var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
bool shouldRestart = true;
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]);
|
if (!OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]);
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldRestart)
|
if (shouldRestart)
|
||||||
{
|
{
|
||||||
|
List<string> arguments = CommandLineState.Arguments.ToList();
|
||||||
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
||||||
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
string executableDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
string executablePath = Path.Combine(executableDirectory, ryuName);
|
||||||
|
|
||||||
if (!Path.Exists(ryuExe))
|
if (!Path.Exists(executablePath))
|
||||||
{
|
{
|
||||||
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
|
executablePath = Path.Combine(executableDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
|
||||||
}
|
}
|
||||||
|
|
||||||
SetFileExecutable(ryuExe);
|
// On macOS we perform the update at relaunch.
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
|
||||||
|
string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app");
|
||||||
|
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
|
||||||
|
string currentPid = Process.GetCurrentProcess().Id.ToString();
|
||||||
|
|
||||||
Process.Start(ryuExe, CommandLineState.Arguments);
|
executablePath = "/bin/bash";
|
||||||
|
arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid });
|
||||||
|
}
|
||||||
|
|
||||||
|
Process.Start(executablePath, arguments);
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,6 +400,15 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
File.WriteAllBytes(updateFile, mergedFileBytes);
|
File.WriteAllBytes(updateFile, mergedFileBytes);
|
||||||
|
|
||||||
|
// On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution.
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
using (Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile }))
|
||||||
|
{
|
||||||
|
xattrProcess.WaitForExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
InstallUpdate(taskDialog, updateFile);
|
InstallUpdate(taskDialog, updateFile);
|
||||||
@ -408,9 +434,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,19 +498,74 @@ namespace Ryujinx.Modules
|
|||||||
worker.Start();
|
worker.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetFileExecutable(string path)
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
||||||
{
|
{
|
||||||
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
|
using Stream inStream = File.OpenRead(archivePath);
|
||||||
UnixFileMode.UserWrite |
|
using GZipInputStream gzipStream = new(inStream);
|
||||||
UnixFileMode.UserRead |
|
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
||||||
UnixFileMode.GroupRead |
|
|
||||||
UnixFileMode.GroupWrite |
|
|
||||||
UnixFileMode.OtherRead |
|
|
||||||
UnixFileMode.OtherWrite;
|
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && File.Exists(path))
|
TarEntry tarEntry;
|
||||||
|
|
||||||
|
while ((tarEntry = tarStream.GetNextEntry()) is not null)
|
||||||
{
|
{
|
||||||
File.SetUnixFileMode(path, ExecutableFileMode);
|
if (tarEntry.IsDirectory)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string outPath = Path.Combine(outputDirectoryPath, 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
||||||
|
{
|
||||||
|
using Stream inStream = File.OpenRead(archivePath);
|
||||||
|
using ZipFile zipFile = new(inStream);
|
||||||
|
|
||||||
|
double count = 0;
|
||||||
|
foreach (ZipEntry zipEntry in zipFile)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
if (zipEntry.IsDirectory) continue;
|
||||||
|
|
||||||
|
string outPath = Path.Combine(outputDirectoryPath, 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));
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,73 +575,21 @@ namespace Ryujinx.Modules
|
|||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux())
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
using Stream inStream = File.OpenRead(updateFile);
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
using GZipInputStream gzipStream = new(inStream);
|
|
||||||
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
|
||||||
|
|
||||||
await Task.Run(() =>
|
|
||||||
{
|
{
|
||||||
TarEntry tarEntry;
|
ExtractTarGzipFile(taskDialog, updateFile, UpdateDir);
|
||||||
while ((tarEntry = tarStream.GetNextEntry()) != null)
|
}
|
||||||
{
|
else if (OperatingSystem.IsWindows())
|
||||||
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.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
|
|
||||||
|
|
||||||
TarEntry entry = tarEntry;
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
|
||||||
{
|
|
||||||
taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using Stream inStream = File.OpenRead(updateFile);
|
|
||||||
using ZipFile zipFile = new(inStream);
|
|
||||||
|
|
||||||
await Task.Run(() =>
|
|
||||||
{
|
{
|
||||||
double count = 0;
|
ExtractZipFile(taskDialog, updateFile, UpdateDir);
|
||||||
foreach (ZipEntry zipEntry in zipFile)
|
}
|
||||||
{
|
else
|
||||||
count++;
|
{
|
||||||
if (zipEntry.IsDirectory) continue;
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
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));
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
|
||||||
{
|
|
||||||
taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete downloaded zip
|
// Delete downloaded zip
|
||||||
File.Delete(updateFile);
|
File.Delete(updateFile);
|
||||||
@ -570,40 +599,42 @@ namespace Ryujinx.Modules
|
|||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming];
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming];
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
// Replace old files
|
// NOTE: On macOS, replacement is delayed to the restart phase.
|
||||||
await Task.Run(() =>
|
if (!OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
double count = 0;
|
// Replace old files
|
||||||
foreach (string file in allFiles)
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
count++;
|
double count = 0;
|
||||||
try
|
foreach (string file in allFiles)
|
||||||
{
|
{
|
||||||
File.Move(file, file + ".ryuold");
|
count++;
|
||||||
|
try
|
||||||
Dispatcher.UIThread.Post(() =>
|
|
||||||
{
|
{
|
||||||
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
File.Move(file, file + ".ryuold");
|
||||||
});
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
});
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
||||||
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
});
|
||||||
|
|
||||||
|
MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog);
|
||||||
});
|
});
|
||||||
|
|
||||||
MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog);
|
Directory.Delete(UpdateDir, true);
|
||||||
});
|
}
|
||||||
|
|
||||||
Directory.Delete(UpdateDir, true);
|
|
||||||
|
|
||||||
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
|
|
||||||
|
|
||||||
_updateSuccessful = true;
|
_updateSuccessful = true;
|
||||||
|
|
||||||
@ -613,7 +644,7 @@ namespace Ryujinx.Modules
|
|||||||
public static bool CanUpdate(bool showWarnings)
|
public static bool CanUpdate(bool showWarnings)
|
||||||
{
|
{
|
||||||
#if !DISABLE_UPDATER
|
#if !DISABLE_UPDATER
|
||||||
if (RuntimeInformation.OSArchitecture != Architecture.X64)
|
if (RuntimeInformation.OSArchitecture != Architecture.X64 && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
@ -686,7 +717,7 @@ namespace Ryujinx.Modules
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This method should always reflect the latest build layout.s
|
// NOTE: This method should always reflect the latest build layout.
|
||||||
private static IEnumerable<string> EnumerateFilesToDelete()
|
private static IEnumerable<string> EnumerateFilesToDelete()
|
||||||
{
|
{
|
||||||
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
|
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
|
||||||
|
@ -16,9 +16,9 @@ using Ryujinx.Ava.UI.Views.User;
|
|||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
namespace Ryujinx.Ava.UI.Controls
|
||||||
@ -121,7 +121,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||||
|
|
||||||
HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
||||||
|
|
||||||
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
{
|
{
|
||||||
using AvaLogger = Avalonia.Logging.Logger;
|
using AvaLogger = Avalonia.Logging.Logger;
|
||||||
using AvaLogLevel = Avalonia.Logging.LogEventLevel;
|
using AvaLogLevel = Avalonia.Logging.LogEventLevel;
|
||||||
using RyuLogger = Ryujinx.Common.Logging.Logger;
|
|
||||||
using RyuLogClass = Ryujinx.Common.Logging.LogClass;
|
using RyuLogClass = Ryujinx.Common.Logging.LogClass;
|
||||||
|
using RyuLogger = Ryujinx.Common.Logging.Logger;
|
||||||
|
|
||||||
internal class LoggerAdapter : Avalonia.Logging.ILogSink
|
internal class LoggerAdapter : Avalonia.Logging.ILogSink
|
||||||
{
|
{
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Avalonia;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Helpers
|
|
||||||
{
|
|
||||||
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
|
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
static partial class MetalHelper
|
|
||||||
{
|
|
||||||
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
|
|
||||||
|
|
||||||
private struct Selector
|
|
||||||
{
|
|
||||||
public readonly IntPtr NativePtr;
|
|
||||||
|
|
||||||
public unsafe Selector(string value)
|
|
||||||
{
|
|
||||||
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
|
|
||||||
byte* data = stackalloc byte[size];
|
|
||||||
|
|
||||||
fixed (char* pValue = value)
|
|
||||||
{
|
|
||||||
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
NativePtr = sel_registerName(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator Selector(string value) => new Selector(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static unsafe IntPtr GetClass(string value)
|
|
||||||
{
|
|
||||||
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
|
|
||||||
byte* data = stackalloc byte[size];
|
|
||||||
|
|
||||||
fixed (char* pValue = value)
|
|
||||||
{
|
|
||||||
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objc_getClass(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct NSPoint
|
|
||||||
{
|
|
||||||
public double X;
|
|
||||||
public double Y;
|
|
||||||
|
|
||||||
public NSPoint(double x, double y)
|
|
||||||
{
|
|
||||||
X = x;
|
|
||||||
Y = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct NSRect
|
|
||||||
{
|
|
||||||
public NSPoint Pos;
|
|
||||||
public NSPoint Size;
|
|
||||||
|
|
||||||
public NSRect(double x, double y, double width, double height)
|
|
||||||
{
|
|
||||||
Pos = new NSPoint(x, y);
|
|
||||||
Size = new NSPoint(width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr GetMetalLayer(out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds)
|
|
||||||
{
|
|
||||||
// Create a new CAMetalLayer.
|
|
||||||
IntPtr layerClass = GetClass("CAMetalLayer");
|
|
||||||
IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc");
|
|
||||||
objc_msgSend(metalLayer, "init");
|
|
||||||
|
|
||||||
// Create a child NSView to render into.
|
|
||||||
IntPtr nsViewClass = GetClass("NSView");
|
|
||||||
IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc");
|
|
||||||
objc_msgSend(child, "init", new NSRect(0, 0, 0, 0));
|
|
||||||
|
|
||||||
// Make its renderer our metal layer.
|
|
||||||
objc_msgSend(child, "setWantsLayer:", (byte)1);
|
|
||||||
objc_msgSend(child, "setLayer:", metalLayer);
|
|
||||||
objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
|
||||||
|
|
||||||
// Ensure the scale factor is up to date.
|
|
||||||
updateBounds = (Rect rect) => {
|
|
||||||
objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
|
||||||
};
|
|
||||||
|
|
||||||
nsView = child;
|
|
||||||
return metalLayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DestroyMetalLayer(IntPtr nsView, IntPtr metalLayer)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static unsafe partial IntPtr sel_registerName(byte* data);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static unsafe partial IntPtr objc_getClass(byte* data);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport)]
|
|
||||||
private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
|
|
||||||
|
|
||||||
[LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")]
|
|
||||||
private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,9 +2,9 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using SPB.Graphics;
|
using SPB.Graphics;
|
||||||
using SPB.Platform;
|
using SPB.Platform;
|
||||||
using SPB.Platform.GLX;
|
using SPB.Platform.GLX;
|
||||||
@ -30,6 +30,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
protected IntPtr NsView { get; set; }
|
protected IntPtr NsView { get; set; }
|
||||||
protected IntPtr MetalLayer { get; set; }
|
protected IntPtr MetalLayer { get; set; }
|
||||||
|
|
||||||
|
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
|
||||||
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
|
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
|
||||||
|
|
||||||
public event EventHandler<IntPtr> WindowCreated;
|
public event EventHandler<IntPtr> WindowCreated;
|
||||||
@ -237,8 +238,29 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
IPlatformHandle CreateMacOS()
|
IPlatformHandle CreateMacOS()
|
||||||
{
|
{
|
||||||
MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
|
// Create a new CAMetalLayer.
|
||||||
|
IntPtr layerClass = ObjectiveC.objc_getClass("CAMetalLayer");
|
||||||
|
IntPtr metalLayer = ObjectiveC.IntPtr_objc_msgSend(layerClass, "alloc");
|
||||||
|
ObjectiveC.objc_msgSend(metalLayer, "init");
|
||||||
|
|
||||||
|
// Create a child NSView to render into.
|
||||||
|
IntPtr nsViewClass = ObjectiveC.objc_getClass("NSView");
|
||||||
|
IntPtr child = ObjectiveC.IntPtr_objc_msgSend(nsViewClass, "alloc");
|
||||||
|
ObjectiveC.objc_msgSend(child, "init", new ObjectiveC.NSRect(0, 0, 0, 0));
|
||||||
|
|
||||||
|
// Make its renderer our metal layer.
|
||||||
|
ObjectiveC.objc_msgSend(child, "setWantsLayer:", 1);
|
||||||
|
ObjectiveC.objc_msgSend(child, "setLayer:", metalLayer);
|
||||||
|
ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
||||||
|
|
||||||
|
// Ensure the scale factor is up to date.
|
||||||
|
_updateBoundsCallback = rect =>
|
||||||
|
{
|
||||||
|
ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
|
||||||
|
};
|
||||||
|
|
||||||
|
IntPtr nsView = child;
|
||||||
|
MetalLayer = metalLayer;
|
||||||
NsView = nsView;
|
NsView = nsView;
|
||||||
|
|
||||||
return new PlatformHandle(nsView, "NSView");
|
return new PlatformHandle(nsView, "NSView");
|
||||||
@ -260,7 +282,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
void DestroyMacOS()
|
void DestroyMacOS()
|
||||||
{
|
{
|
||||||
MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ using Avalonia.Collections;
|
|||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
@ -17,7 +16,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
@ -246,7 +246,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
using var file = new UniqueRef<IFile>();
|
using var file = new UniqueRef<IFile>();
|
||||||
|
|
||||||
romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read)
|
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read)
|
||||||
.ThrowIfFailure();
|
.ThrowIfFailure();
|
||||||
|
|
||||||
using (MemoryStream stream = new())
|
using (MemoryStream stream = new())
|
||||||
|
@ -3,11 +3,8 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Svg.Skia;
|
using Avalonia.Svg.Skia;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using LibHac.Bcat;
|
|
||||||
using LibHac.Tools.Fs;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
|
@ -7,8 +7,7 @@ using DynamicData;
|
|||||||
using DynamicData.Binding;
|
using DynamicData.Binding;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSystem;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using LibHac.Tools.Fs;
|
|
||||||
using Ryujinx.Ava.Common;
|
using Ryujinx.Ava.Common;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
|
@ -45,6 +45,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private KeyboardHotkeys _keyboardHotkeys;
|
private KeyboardHotkeys _keyboardHotkeys;
|
||||||
private int _graphicsBackendIndex;
|
private int _graphicsBackendIndex;
|
||||||
private string _customThemePath;
|
private string _customThemePath;
|
||||||
|
private int _scalingFilter;
|
||||||
|
private int _scalingFilterLevel;
|
||||||
|
|
||||||
public event Action CloseWindow;
|
public event Action CloseWindow;
|
||||||
public event Action SaveSettingsEvent;
|
public event Action SaveSettingsEvent;
|
||||||
@ -153,6 +155,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public bool IsSDL2Enabled { get; set; }
|
public bool IsSDL2Enabled { get; set; }
|
||||||
public bool EnableCustomTheme { get; set; }
|
public bool EnableCustomTheme { get; set; }
|
||||||
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
|
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
|
||||||
|
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
|
||||||
|
|
||||||
public bool IsVulkanSelected => GraphicsBackendIndex == 0;
|
public bool IsVulkanSelected => GraphicsBackendIndex == 0;
|
||||||
public bool UseHypervisor { get; set; }
|
public bool UseHypervisor { get; set; }
|
||||||
|
|
||||||
@ -179,6 +183,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public int AudioBackend { get; set; }
|
public int AudioBackend { get; set; }
|
||||||
public int MaxAnisotropy { get; set; }
|
public int MaxAnisotropy { get; set; }
|
||||||
public int AspectRatio { get; set; }
|
public int AspectRatio { get; set; }
|
||||||
|
public int AntiAliasingEffect { get; set; }
|
||||||
|
public string ScalingFilterLevelText => ScalingFilterLevel.ToString("0");
|
||||||
|
public int ScalingFilterLevel
|
||||||
|
{
|
||||||
|
get => _scalingFilterLevel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_scalingFilterLevel = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(ScalingFilterLevelText));
|
||||||
|
}
|
||||||
|
}
|
||||||
public int OpenglDebugLevel { get; set; }
|
public int OpenglDebugLevel { get; set; }
|
||||||
public int MemoryMode { get; set; }
|
public int MemoryMode { get; set; }
|
||||||
public int BaseStyleIndex { get; set; }
|
public int BaseStyleIndex { get; set; }
|
||||||
@ -192,6 +208,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
OnPropertyChanged(nameof(IsVulkanSelected));
|
OnPropertyChanged(nameof(IsVulkanSelected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public int ScalingFilter
|
||||||
|
{
|
||||||
|
get => _scalingFilter;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_scalingFilter = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(IsScalingFilterActive));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int PreferredGpuIndex { get; set; }
|
public int PreferredGpuIndex { get; set; }
|
||||||
|
|
||||||
@ -365,6 +391,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
AspectRatio = (int)config.Graphics.AspectRatio.Value;
|
AspectRatio = (int)config.Graphics.AspectRatio.Value;
|
||||||
GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
|
GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
|
||||||
ShaderDumpPath = config.Graphics.ShadersDumpPath;
|
ShaderDumpPath = config.Graphics.ShadersDumpPath;
|
||||||
|
AntiAliasingEffect = (int)config.Graphics.AntiAliasing.Value;
|
||||||
|
ScalingFilter = (int)config.Graphics.ScalingFilter.Value;
|
||||||
|
ScalingFilterLevel = config.Graphics.ScalingFilterLevel.Value;
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
AudioBackend = (int)config.System.AudioBackend.Value;
|
AudioBackend = (int)config.System.AudioBackend.Value;
|
||||||
@ -447,6 +476,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
|
config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
|
||||||
config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
|
config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
|
||||||
config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
|
config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
|
||||||
|
config.Graphics.AntiAliasing.Value = (AntiAliasing)AntiAliasingEffect;
|
||||||
|
config.Graphics.ScalingFilter.Value = (ScalingFilter)ScalingFilter;
|
||||||
|
config.Graphics.ScalingFilterLevel.Value = ScalingFilterLevel;
|
||||||
|
|
||||||
if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
|
if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
|
||||||
{
|
{
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ public class TitleUpdateViewModel : BaseModel
|
|||||||
|
|
||||||
using UniqueRef<IFile> nacpFile = new();
|
using UniqueRef<IFile> nacpFile = new();
|
||||||
|
|
||||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
||||||
|
|
||||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
||||||
|
@ -126,7 +126,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
using var file = new UniqueRef<IFile>();
|
using var file = new UniqueRef<IFile>();
|
||||||
|
|
||||||
romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
using (MemoryStream stream = new())
|
using (MemoryStream stream = new())
|
||||||
using (MemoryStream streamPng = new())
|
using (MemoryStream streamPng = new())
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using LibHac.FsSystem;
|
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
|
Design.Width="1000"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:CompileBindings="True"
|
x:CompileBindings="True"
|
||||||
x:DataType="viewModels:SettingsViewModel">
|
x:DataType="viewModels:SettingsViewModel">
|
||||||
@ -111,6 +112,83 @@
|
|||||||
Minimum="0.1"
|
Minimum="0.1"
|
||||||
Value="{Binding CustomResolutionScale}" />
|
Value="{Binding CustomResolutionScale}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="10">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsAATooltip}"
|
||||||
|
Text="{locale:Locale GraphicsAALabel}"
|
||||||
|
Width="250" />
|
||||||
|
<ComboBox Width="350"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsAATooltip}"
|
||||||
|
SelectedIndex="{Binding AntiAliasingEffect}">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="FXAA" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SmaaLow}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SmaaMedium}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SmaaHigh}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SmaaUltra}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="10">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsScalingFilterTooltip}"
|
||||||
|
Text="{locale:Locale GraphicsScalingFilterLabel}"
|
||||||
|
Width="250" />
|
||||||
|
<ComboBox Width="350"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsScalingFilterTooltip}"
|
||||||
|
SelectedIndex="{Binding ScalingFilter}">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="Bilinear" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="Nearest" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="FSR" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
<Slider Value="{Binding ScalingFilterLevel}"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsScalingFilterLevelTooltip}"
|
||||||
|
MinWidth="150"
|
||||||
|
Margin="10,-3,0,0"
|
||||||
|
Height="32"
|
||||||
|
Padding="0,-5"
|
||||||
|
IsVisible="{Binding IsScalingFilterActive}"
|
||||||
|
TickFrequency="1"
|
||||||
|
IsSnapToTickEnabled="True"
|
||||||
|
LargeChange="10"
|
||||||
|
SmallChange="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Minimum="0"
|
||||||
|
Maximum="100" />
|
||||||
|
<TextBlock Margin="5,0"
|
||||||
|
Width="40"
|
||||||
|
IsVisible="{Binding IsScalingFilterActive}"
|
||||||
|
Text="{Binding ScalingFilterLevelText}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center"
|
<TextBlock VerticalAlignment="Center"
|
||||||
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
|
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
|
||||||
|
@ -5,8 +5,8 @@ using FluentAvalonia.UI.Controls;
|
|||||||
using FluentAvalonia.UI.Navigation;
|
using FluentAvalonia.UI.Navigation;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
using Ryujinx.Ava.UI.Controls;
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using System;
|
using System;
|
||||||
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
|
||||||
|
@ -76,7 +76,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
|||||||
|
|
||||||
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||||
|
|
||||||
_horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
_horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
|
||||||
|
|
||||||
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
using UniqueRef<IFile> ncaFile = new();
|
using UniqueRef<IFile> ncaFile = new();
|
||||||
|
|
||||||
partitionFileSystem.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
partitionFileSystem.OpenFile(ref ncaFile.Ref, downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
|
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
|
||||||
if (nca != null)
|
if (nca != null)
|
||||||
@ -158,7 +158,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
using var ncaFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
|
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
|
||||||
if (nca == null)
|
if (nca == null)
|
||||||
|
12
Ryujinx.Common/Configuration/AntiAliasing.cs
Normal file
12
Ryujinx.Common/Configuration/AntiAliasing.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
public enum AntiAliasing
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Fxaa,
|
||||||
|
SmaaLow,
|
||||||
|
SmaaMedium,
|
||||||
|
SmaaHigh,
|
||||||
|
SmaaUltra
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
9
Ryujinx.Common/Configuration/ScalingFilter.cs
Normal file
9
Ryujinx.Common/Configuration/ScalingFilter.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
public enum ScalingFilter
|
||||||
|
{
|
||||||
|
Bilinear,
|
||||||
|
Nearest,
|
||||||
|
Fsr
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.SystemInterop;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -14,6 +15,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
private static readonly List<ILogTarget> m_LogTargets;
|
private static readonly List<ILogTarget> m_LogTargets;
|
||||||
|
|
||||||
|
private static readonly StdErrAdapter _stdErrAdapter;
|
||||||
|
|
||||||
public static event EventHandler<LogEventArgs> Updated;
|
public static event EventHandler<LogEventArgs> Updated;
|
||||||
|
|
||||||
public readonly struct Log
|
public readonly struct Log
|
||||||
@ -79,6 +82,12 @@ namespace Ryujinx.Common.Logging
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void PrintRawMsg(string message)
|
||||||
|
{
|
||||||
|
Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, message));
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static string FormatMessage(LogClass Class, string Caller, string Message) => $"{Class} {Caller}: {Message}";
|
private static string FormatMessage(LogClass Class, string Caller, string Message) => $"{Class} {Caller}: {Message}";
|
||||||
}
|
}
|
||||||
@ -119,6 +128,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
Warning = new Log(LogLevel.Warning);
|
Warning = new Log(LogLevel.Warning);
|
||||||
Info = new Log(LogLevel.Info);
|
Info = new Log(LogLevel.Info);
|
||||||
Trace = new Log(LogLevel.Trace);
|
Trace = new Log(LogLevel.Trace);
|
||||||
|
|
||||||
|
_stdErrAdapter = new StdErrAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RestartTime()
|
public static void RestartTime()
|
||||||
@ -164,6 +175,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
{
|
{
|
||||||
Updated = null;
|
Updated = null;
|
||||||
|
|
||||||
|
_stdErrAdapter.Dispose();
|
||||||
|
|
||||||
foreach (var target in m_LogTargets)
|
foreach (var target in m_LogTargets)
|
||||||
{
|
{
|
||||||
target.Dispose();
|
target.Dispose();
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,6 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.SystemInfo
|
namespace Ryujinx.Common.SystemInfo
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.SystemInfo
|
namespace Ryujinx.Common.SystemInfo
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using System;
|
using Ryujinx.Common.Logging;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.SystemInfo
|
namespace Ryujinx.Common.SystemInfo
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.SystemInfo
|
namespace Ryujinx.Common.SystemInfo
|
||||||
{
|
{
|
||||||
|
93
Ryujinx.Common/SystemInterop/StdErrAdapter.cs
Normal file
93
Ryujinx.Common/SystemInterop/StdErrAdapter.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.SystemInterop
|
||||||
|
{
|
||||||
|
public partial class StdErrAdapter : IDisposable
|
||||||
|
{
|
||||||
|
private bool _disposable = false;
|
||||||
|
private UnixStream _pipeReader;
|
||||||
|
private UnixStream _pipeWriter;
|
||||||
|
private Thread _worker;
|
||||||
|
|
||||||
|
public StdErrAdapter()
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
RegisterPosix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private void RegisterPosix()
|
||||||
|
{
|
||||||
|
const int stdErrFileno = 2;
|
||||||
|
|
||||||
|
(int readFd, int writeFd) = MakePipe();
|
||||||
|
dup2(writeFd, stdErrFileno);
|
||||||
|
|
||||||
|
_pipeReader = new UnixStream(readFd);
|
||||||
|
_pipeWriter = new UnixStream(writeFd);
|
||||||
|
|
||||||
|
_worker = new Thread(EventWorker);
|
||||||
|
_disposable = true;
|
||||||
|
_worker.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private void EventWorker()
|
||||||
|
{
|
||||||
|
TextReader reader = new StreamReader(_pipeReader);
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
Logger.Error?.PrintRawMsg(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposable)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
_pipeReader?.Close();
|
||||||
|
_pipeWriter?.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static partial int dup2(int fd, int fd2);
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static unsafe partial int pipe(int* pipefd);
|
||||||
|
|
||||||
|
private static unsafe (int, int) MakePipe()
|
||||||
|
{
|
||||||
|
int *pipefd = stackalloc int[2];
|
||||||
|
|
||||||
|
if (pipe(pipefd) == 0)
|
||||||
|
{
|
||||||
|
return (pipefd[0], pipefd[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
Ryujinx.Common/SystemInterop/UnixStream.cs
Normal file
155
Ryujinx.Common/SystemInterop/UnixStream.cs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.SystemInterop
|
||||||
|
{
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
public partial class UnixStream : Stream, IDisposable
|
||||||
|
{
|
||||||
|
private const int InvalidFd = -1;
|
||||||
|
|
||||||
|
private int _fd;
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static partial long read(int fd, IntPtr buf, ulong count);
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static partial long write(int fd, IntPtr buf, ulong count);
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static partial int close(int fd);
|
||||||
|
|
||||||
|
public UnixStream(int fd)
|
||||||
|
{
|
||||||
|
if (InvalidFd == fd)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid file descriptor");
|
||||||
|
}
|
||||||
|
|
||||||
|
_fd = fd;
|
||||||
|
|
||||||
|
CanRead = read(fd, IntPtr.Zero, 0) != -1;
|
||||||
|
CanWrite = write(fd, IntPtr.Zero, 0) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
~UnixStream()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanRead { get; }
|
||||||
|
public override bool CanWrite { get; }
|
||||||
|
public override bool CanSeek => false;
|
||||||
|
|
||||||
|
public override long Length => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => throw new NotSupportedException();
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override unsafe int Read([In, Out] byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.Length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long r = 0;
|
||||||
|
fixed (byte* buf = &buffer[offset])
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
r = read(_fd, (IntPtr)buf, (ulong)count);
|
||||||
|
} while (ShouldRetry(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override unsafe void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed (byte* buf = &buffer[offset])
|
||||||
|
{
|
||||||
|
long r = 0;
|
||||||
|
do {
|
||||||
|
r = write(_fd, (IntPtr)buf, (ulong)count);
|
||||||
|
} while (ShouldRetry(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
if (_fd == InvalidFd)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Flush();
|
||||||
|
|
||||||
|
int r;
|
||||||
|
do {
|
||||||
|
r = close(_fd);
|
||||||
|
} while (ShouldRetry(r));
|
||||||
|
|
||||||
|
_fd = InvalidFd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDisposable.Dispose()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldRetry(long r)
|
||||||
|
{
|
||||||
|
if (r == -1)
|
||||||
|
{
|
||||||
|
const int eintr = 4;
|
||||||
|
|
||||||
|
int errno = Marshal.GetLastPInvokeError();
|
||||||
|
|
||||||
|
if (errno == eintr)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new SystemException($"Operation failed with error 0x{errno:X}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
16
Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs
Normal file
16
Ryujinx.Graphics.GAL/AdvancedBlendDescriptor.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
Ryujinx.Graphics.GAL/AdvancedBlendOp.cs
Normal file
52
Ryujinx.Graphics.GAL/AdvancedBlendOp.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs
Normal file
9
Ryujinx.Graphics.GAL/AdvancedBlendOverlap.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum AdvancedBlendOverlap
|
||||||
|
{
|
||||||
|
Uncorrelated,
|
||||||
|
Disjoint,
|
||||||
|
Conjoint
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.GAL/AntiAliasing.cs
Normal file
12
Ryujinx.Graphics.GAL/AntiAliasing.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum AntiAliasing
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Fxaa,
|
||||||
|
SmaaLow,
|
||||||
|
SmaaMedium,
|
||||||
|
SmaaHigh,
|
||||||
|
SmaaUltra
|
||||||
|
}
|
||||||
|
}
|
@ -23,8 +23,10 @@ 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 SupportsGeometryShader;
|
||||||
public readonly bool SupportsGeometryShaderPassthrough;
|
public readonly bool SupportsGeometryShaderPassthrough;
|
||||||
public readonly bool SupportsImageLoadFormatted;
|
public readonly bool SupportsImageLoadFormatted;
|
||||||
public readonly bool SupportsLayerVertexTessellation;
|
public readonly bool SupportsLayerVertexTessellation;
|
||||||
@ -64,8 +66,10 @@ 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 supportsGeometryShader,
|
||||||
bool supportsGeometryShaderPassthrough,
|
bool supportsGeometryShaderPassthrough,
|
||||||
bool supportsImageLoadFormatted,
|
bool supportsImageLoadFormatted,
|
||||||
bool supportsLayerVertexTessellation,
|
bool supportsLayerVertexTessellation,
|
||||||
@ -102,8 +106,10 @@ 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;
|
||||||
|
SupportsGeometryShader = supportsGeometryShader;
|
||||||
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
||||||
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
||||||
SupportsLayerVertexTessellation = supportsLayerVertexTessellation;
|
SupportsLayerVertexTessellation = supportsLayerVertexTessellation;
|
||||||
|
@ -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);
|
||||||
|
@ -9,5 +9,9 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
void SetSize(int width, int height);
|
void SetSize(int width, int height);
|
||||||
|
|
||||||
void ChangeVSyncMode(bool vsyncEnabled);
|
void ChangeVSyncMode(bool vsyncEnabled);
|
||||||
|
|
||||||
|
void SetAntiAliasing(AntiAliasing antialiasing);
|
||||||
|
void SetScalingFilter(ScalingFilter type);
|
||||||
|
void SetScalingFilterLevel(float level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
EndHostConditionalRendering,
|
EndHostConditionalRendering,
|
||||||
EndTransformFeedback,
|
EndTransformFeedback,
|
||||||
SetAlphaTest,
|
SetAlphaTest,
|
||||||
|
SetBlendStateAdvanced,
|
||||||
SetBlendState,
|
SetBlendState,
|
||||||
SetDepthBias,
|
SetDepthBias,
|
||||||
SetDepthClamp,
|
SetDepthClamp,
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -32,5 +32,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
||||||
|
|
||||||
|
public void SetAntiAliasing(AntiAliasing effect) { }
|
||||||
|
|
||||||
|
public void SetScalingFilter(ScalingFilter type) { }
|
||||||
|
|
||||||
|
public void SetScalingFilterLevel(float level) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
Ryujinx.Graphics.GAL/UpscaleType.cs
Normal file
9
Ryujinx.Graphics.GAL/UpscaleType.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum ScalingFilter
|
||||||
|
{
|
||||||
|
Bilinear,
|
||||||
|
Nearest,
|
||||||
|
Fsr
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
4226
Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs
Normal file
4226
Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
126
Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs
Normal file
126
Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendUcode.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
305
Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs
Normal file
305
Ryujinx.Graphics.Gpu/Engine/Threed/Blender/UcodeAssembler.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -725,10 +725,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool clearDepth = (argument & 1) != 0;
|
||||||
|
bool clearStencil = (argument & 2) != 0;
|
||||||
|
uint componentMask = (uint)((argument >> 2) & 0xf);
|
||||||
int index = (argument >> 6) & 0xf;
|
int index = (argument >> 6) & 0xf;
|
||||||
int layer = (argument >> 10) & 0x3ff;
|
int layer = (argument >> 10) & 0x3ff;
|
||||||
|
|
||||||
engine.UpdateRenderTargetState(useControl: false, layered: layer != 0 || layerCount > 1, singleUse: index);
|
RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor;
|
||||||
|
|
||||||
|
if (layer != 0 || layerCount > 1)
|
||||||
|
{
|
||||||
|
updateFlags |= RenderTargetUpdateFlags.Layered;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearDepth || clearStencil)
|
||||||
|
{
|
||||||
|
updateFlags |= RenderTargetUpdateFlags.UpdateDepthStencil;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.UpdateRenderTargetState(updateFlags, singleUse: componentMask != 0 ? index : -1);
|
||||||
|
|
||||||
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
|
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
|
||||||
// on the screen scissor state, then we need to force only one texture to be bound to avoid
|
// on the screen scissor state, then we need to force only one texture to be bound to avoid
|
||||||
@ -788,18 +803,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
_context.Renderer.Pipeline.SetScissors(scissors);
|
_context.Renderer.Pipeline.SetScissors(scissors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clipMismatch)
|
_channel.TextureManager.UpdateRenderTargets();
|
||||||
{
|
|
||||||
_channel.TextureManager.UpdateRenderTarget(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_channel.TextureManager.UpdateRenderTargets();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool clearDepth = (argument & 1) != 0;
|
|
||||||
bool clearStencil = (argument & 2) != 0;
|
|
||||||
uint componentMask = (uint)((argument >> 2) & 0xf);
|
|
||||||
|
|
||||||
if (componentMask != 0)
|
if (componentMask != 0)
|
||||||
{
|
{
|
||||||
@ -841,7 +845,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
engine.UpdateScissorState();
|
engine.UpdateScissorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.UpdateRenderTargetState(useControl: true);
|
engine.UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
|
||||||
|
|
||||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Flags indicating how the render targets should be updated.
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
enum RenderTargetUpdateFlags
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No flags.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get render target index from the control register.
|
||||||
|
/// </summary>
|
||||||
|
UseControl = 1 << 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that all render targets are 2D array textures.
|
||||||
|
/// </summary>
|
||||||
|
Layered = 1 << 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that only a single color target will be used.
|
||||||
|
/// </summary>
|
||||||
|
SingleColor = 1 << 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the depth-stencil target will be used.
|
||||||
|
/// </summary>
|
||||||
|
UpdateDepthStencil = 1 << 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default update flags for draw.
|
||||||
|
/// </summary>
|
||||||
|
UpdateAll = UseControl | UpdateDepthStencil
|
||||||
|
}
|
||||||
|
}
|
@ -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),
|
||||||
@ -402,20 +414,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateRenderTargetState()
|
private void UpdateRenderTargetState()
|
||||||
{
|
{
|
||||||
UpdateRenderTargetState(true);
|
UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useControl">Use draw buffers information from render target control register</param>
|
/// <param name="updateFlags">Flags indicating which render targets should be updated and how</param>
|
||||||
/// <param name="layered">Indicates if the texture is layered</param>
|
|
||||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||||
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
|
public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1)
|
||||||
{
|
{
|
||||||
var memoryManager = _channel.MemoryManager;
|
var memoryManager = _channel.MemoryManager;
|
||||||
var rtControl = _state.State.RtControl;
|
var rtControl = _state.State.RtControl;
|
||||||
|
|
||||||
|
bool useControl = updateFlags.HasFlag(RenderTargetUpdateFlags.UseControl);
|
||||||
|
bool layered = updateFlags.HasFlag(RenderTargetUpdateFlags.Layered);
|
||||||
|
bool singleColor = updateFlags.HasFlag(RenderTargetUpdateFlags.SingleColor);
|
||||||
|
|
||||||
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
|
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
|
||||||
|
|
||||||
var msaaMode = _state.State.RtMsaaMode;
|
var msaaMode = _state.State.RtMsaaMode;
|
||||||
@ -424,7 +439,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
int samplesInY = msaaMode.SamplesInY();
|
int samplesInY = msaaMode.SamplesInY();
|
||||||
|
|
||||||
var scissor = _state.State.ScreenScissorState;
|
var scissor = _state.State.ScreenScissorState;
|
||||||
Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);
|
Size sizeHint = new Size((scissor.X + scissor.Width) * samplesInX, (scissor.Y + scissor.Height) * samplesInY, 1);
|
||||||
|
|
||||||
int clipRegionWidth = int.MaxValue;
|
int clipRegionWidth = int.MaxValue;
|
||||||
int clipRegionHeight = int.MaxValue;
|
int clipRegionHeight = int.MaxValue;
|
||||||
@ -438,7 +453,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
var colorState = _state.State.RtColorState[rtIndex];
|
var colorState = _state.State.RtColorState[rtIndex];
|
||||||
|
|
||||||
if (index >= count || !IsRtEnabled(colorState))
|
if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse))
|
||||||
{
|
{
|
||||||
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
|
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
|
||||||
|
|
||||||
@ -478,7 +493,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
Image.Texture depthStencil = null;
|
Image.Texture depthStencil = null;
|
||||||
|
|
||||||
if (dsEnable)
|
if (dsEnable && updateFlags.HasFlag(RenderTargetUpdateFlags.UpdateDepthStencil))
|
||||||
{
|
{
|
||||||
var dsState = _state.State.RtDepthStencilState;
|
var dsState = _state.State.RtDepthStencilState;
|
||||||
var dsSize = _state.State.RtDepthStencilSize;
|
var dsSize = _state.State.RtDepthStencilSize;
|
||||||
@ -1151,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;
|
||||||
|
|
||||||
|
@ -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.
|
||||||
@ -139,12 +144,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useControl">Use draw buffers information from render target control register</param>
|
/// <param name="updateFlags">Flags indicating which render targets should be updated and how</param>
|
||||||
/// <param name="layered">Indicates if the texture is layered</param>
|
|
||||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||||
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
|
public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1)
|
||||||
{
|
{
|
||||||
_stateUpdater.UpdateRenderTargetState(useControl, layered, singleUse);
|
_stateUpdater.UpdateRenderTargetState(updateFlags, singleUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -284,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
|
||||||
|
@ -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;
|
||||||
|
@ -33,10 +33,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class AutoDeleteCache : IEnumerable<Texture>
|
class AutoDeleteCache : IEnumerable<Texture>
|
||||||
{
|
{
|
||||||
|
private const int MinCountForDeletion = 32;
|
||||||
private const int MaxCapacity = 2048;
|
private const int MaxCapacity = 2048;
|
||||||
|
private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB;
|
||||||
|
|
||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
private ulong _totalSize;
|
||||||
|
|
||||||
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
||||||
private HashSet<ShortTextureCacheEntry> _shortCache;
|
private HashSet<ShortTextureCacheEntry> _shortCache;
|
||||||
@ -49,7 +51,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public AutoDeleteCache()
|
public AutoDeleteCache()
|
||||||
{
|
{
|
||||||
_textures = new LinkedList<Texture>();
|
_textures = new LinkedList<Texture>();
|
||||||
_deferredRemovals = new ConcurrentQueue<Texture>();
|
|
||||||
|
|
||||||
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
||||||
_shortCache = new HashSet<ShortTextureCacheEntry>();
|
_shortCache = new HashSet<ShortTextureCacheEntry>();
|
||||||
@ -67,37 +68,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="texture">The texture to be added to the cache</param>
|
/// <param name="texture">The texture to be added to the cache</param>
|
||||||
public void Add(Texture texture)
|
public void Add(Texture texture)
|
||||||
{
|
{
|
||||||
texture.IncrementReferenceCount();
|
_totalSize += texture.Size;
|
||||||
|
|
||||||
|
texture.IncrementReferenceCount();
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
|
|
||||||
if (_textures.Count > MaxCapacity)
|
if (_textures.Count > MaxCapacity ||
|
||||||
|
(_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
|
||||||
{
|
{
|
||||||
Texture oldestTexture = _textures.First.Value;
|
RemoveLeastUsedTexture();
|
||||||
|
|
||||||
if (!oldestTexture.CheckModified(false))
|
|
||||||
{
|
|
||||||
// The texture must be flushed if it falls out of the auto delete cache.
|
|
||||||
// Flushes out of the auto delete cache do not trigger write tracking,
|
|
||||||
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
|
||||||
|
|
||||||
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
|
||||||
oldestTexture.FlushModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_textures.RemoveFirst();
|
|
||||||
|
|
||||||
oldestTexture.DecrementReferenceCount();
|
|
||||||
|
|
||||||
oldestTexture.CacheNode = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_deferredRemovals.Count > 0)
|
|
||||||
{
|
|
||||||
while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
|
|
||||||
{
|
|
||||||
Remove(textureToRemove, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +99,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
|
||||||
|
{
|
||||||
|
RemoveLeastUsedTexture();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -127,6 +111,31 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the least used texture from the cache.
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveLeastUsedTexture()
|
||||||
|
{
|
||||||
|
Texture oldestTexture = _textures.First.Value;
|
||||||
|
|
||||||
|
_totalSize -= oldestTexture.Size;
|
||||||
|
|
||||||
|
if (!oldestTexture.CheckModified(false))
|
||||||
|
{
|
||||||
|
// The texture must be flushed if it falls out of the auto delete cache.
|
||||||
|
// Flushes out of the auto delete cache do not trigger write tracking,
|
||||||
|
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
||||||
|
|
||||||
|
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
||||||
|
oldestTexture.FlushModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_textures.RemoveFirst();
|
||||||
|
|
||||||
|
oldestTexture.DecrementReferenceCount();
|
||||||
|
oldestTexture.CacheNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a texture from the cache.
|
/// Removes a texture from the cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -148,20 +157,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
_textures.Remove(texture.CacheNode);
|
_textures.Remove(texture.CacheNode);
|
||||||
|
|
||||||
|
_totalSize -= texture.Size;
|
||||||
|
|
||||||
texture.CacheNode = null;
|
texture.CacheNode = null;
|
||||||
|
|
||||||
return texture.DecrementReferenceCount();
|
return texture.DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues removal of a texture from the cache in a thread safe way.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="texture">The texture to be removed from the cache</param>
|
|
||||||
public void RemoveDeferred(Texture texture)
|
|
||||||
{
|
|
||||||
_deferredRemovals.Enqueue(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to find a texture on the short duration cache.
|
/// Attempt to find a texture on the short duration cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
@ -89,12 +88,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TextureGroup Group { get; private set; }
|
public TextureGroup Group { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set when a texture has been changed size. This indicates that it may need to be
|
|
||||||
/// changed again when obtained as a sampler.
|
|
||||||
/// </summary>
|
|
||||||
public bool ChangedSize { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set when a texture's GPU VA has ever been partially or fully unmapped.
|
/// Set when a texture's GPU VA has ever been partially or fully unmapped.
|
||||||
/// This indicates that the range must be fully checked when matching the texture.
|
/// This indicates that the range must be fully checked when matching the texture.
|
||||||
@ -410,122 +403,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
|
Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the texture size.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This operation may also change the size of all mipmap levels, including from the parent
|
|
||||||
/// and other possible child textures, to ensure that all sizes are consistent.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
public void ChangeSize(int width, int height, int depthOrLayers)
|
|
||||||
{
|
|
||||||
int blockWidth = Info.FormatInfo.BlockWidth;
|
|
||||||
int blockHeight = Info.FormatInfo.BlockHeight;
|
|
||||||
|
|
||||||
width <<= FirstLevel;
|
|
||||||
height <<= FirstLevel;
|
|
||||||
|
|
||||||
if (Target == Target.Texture3D)
|
|
||||||
{
|
|
||||||
depthOrLayers <<= FirstLevel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
depthOrLayers = _viewStorage.Info.DepthOrLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
_viewStorage.RecreateStorageOrView(width, height, blockWidth, blockHeight, depthOrLayers);
|
|
||||||
|
|
||||||
foreach (Texture view in _viewStorage._views)
|
|
||||||
{
|
|
||||||
int viewWidth = Math.Max(1, width >> view.FirstLevel);
|
|
||||||
int viewHeight = Math.Max(1, height >> view.FirstLevel);
|
|
||||||
|
|
||||||
int viewDepthOrLayers;
|
|
||||||
|
|
||||||
if (view.Info.Target == Target.Texture3D)
|
|
||||||
{
|
|
||||||
viewDepthOrLayers = Math.Max(1, depthOrLayers >> view.FirstLevel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
viewDepthOrLayers = view.Info.DepthOrLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
view.RecreateStorageOrView(viewWidth, viewHeight, blockWidth, blockHeight, viewDepthOrLayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Recreates the texture storage (or view, in the case of child textures) of this texture.
|
|
||||||
/// This allows recreating the texture with a new size.
|
|
||||||
/// A copy is automatically performed from the old to the new texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="width">The block width related to the given width</param>
|
|
||||||
/// <param name="height">The block height related to the given height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
private void RecreateStorageOrView(int width, int height, int blockWidth, int blockHeight, int depthOrLayers)
|
|
||||||
{
|
|
||||||
RecreateStorageOrView(
|
|
||||||
BitUtils.DivRoundUp(width * Info.FormatInfo.BlockWidth, blockWidth),
|
|
||||||
BitUtils.DivRoundUp(height * Info.FormatInfo.BlockHeight, blockHeight),
|
|
||||||
depthOrLayers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Recreates the texture storage (or view, in the case of child textures) of this texture.
|
|
||||||
/// This allows recreating the texture with a new size.
|
|
||||||
/// A copy is automatically performed from the old to the new texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
private void RecreateStorageOrView(int width, int height, int depthOrLayers)
|
|
||||||
{
|
|
||||||
ChangedSize = true;
|
|
||||||
|
|
||||||
SetInfo(new TextureInfo(
|
|
||||||
Info.GpuAddress,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
depthOrLayers,
|
|
||||||
Info.Levels,
|
|
||||||
Info.SamplesInX,
|
|
||||||
Info.SamplesInY,
|
|
||||||
Info.Stride,
|
|
||||||
Info.IsLinear,
|
|
||||||
Info.GobBlocksInY,
|
|
||||||
Info.GobBlocksInZ,
|
|
||||||
Info.GobBlocksInTileX,
|
|
||||||
Info.Target,
|
|
||||||
Info.FormatInfo,
|
|
||||||
Info.DepthStencilMode,
|
|
||||||
Info.SwizzleR,
|
|
||||||
Info.SwizzleG,
|
|
||||||
Info.SwizzleB,
|
|
||||||
Info.SwizzleA));
|
|
||||||
|
|
||||||
TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
|
|
||||||
|
|
||||||
if (_viewStorage != this)
|
|
||||||
{
|
|
||||||
ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, FirstLayer, FirstLevel));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ITexture newStorage = _context.Renderer.CreateTexture(createInfo, ScaleFactor);
|
|
||||||
|
|
||||||
HostTexture.CopyTo(newStorage, 0, 0);
|
|
||||||
|
|
||||||
ReplaceStorage(newStorage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers when a texture has had its data set after being scaled, and
|
/// Registers when a texture has had its data set after being scaled, and
|
||||||
/// determines if it should be blacklisted from scaling to improve performance.
|
/// determines if it should be blacklisted from scaling to improve performance.
|
||||||
@ -1215,7 +1092,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>A value indicating how well this texture matches the given info</returns>
|
/// <returns>A value indicating how well this texture matches the given info</returns>
|
||||||
public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
|
public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
|
||||||
{
|
{
|
||||||
TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, (flags & TextureSearchFlags.ForSampler) != 0, (flags & TextureSearchFlags.ForCopy) != 0);
|
bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0;
|
||||||
|
|
||||||
|
TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0);
|
||||||
|
|
||||||
if (matchQuality == TextureMatchQuality.NoMatch)
|
if (matchQuality == TextureMatchQuality.NoMatch)
|
||||||
{
|
{
|
||||||
@ -1227,12 +1106,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return TextureMatchQuality.NoMatch;
|
return TextureMatchQuality.NoMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0, FirstLevel))
|
if (!TextureCompatibility.SizeMatches(Info, info, forSampler))
|
||||||
{
|
{
|
||||||
return TextureMatchQuality.NoMatch;
|
return TextureMatchQuality.NoMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0)
|
if ((flags & TextureSearchFlags.ForSampler) != 0)
|
||||||
{
|
{
|
||||||
if (!TextureCompatibility.SamplerParamsMatches(Info, info))
|
if (!TextureCompatibility.SamplerParamsMatches(Info, info))
|
||||||
{
|
{
|
||||||
@ -1262,12 +1141,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">Texture view information</param>
|
/// <param name="info">Texture view information</param>
|
||||||
/// <param name="range">Texture view physical memory ranges</param>
|
/// <param name="range">Texture view physical memory ranges</param>
|
||||||
|
/// <param name="exactSize">Indicates if the texture sizes must be exactly equal, or width is allowed to differ</param>
|
||||||
/// <param name="layerSize">Layer size on the given texture</param>
|
/// <param name="layerSize">Layer size on the given texture</param>
|
||||||
/// <param name="caps">Host GPU capabilities</param>
|
/// <param name="caps">Host GPU capabilities</param>
|
||||||
/// <param name="firstLayer">Texture view initial layer on this texture</param>
|
/// <param name="firstLayer">Texture view initial layer on this texture</param>
|
||||||
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
|
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
|
||||||
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
|
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
|
||||||
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel)
|
public TextureViewCompatibility IsViewCompatible(
|
||||||
|
TextureInfo info,
|
||||||
|
MultiRange range,
|
||||||
|
bool exactSize,
|
||||||
|
int layerSize,
|
||||||
|
Capabilities caps,
|
||||||
|
out int firstLayer,
|
||||||
|
out int firstLevel)
|
||||||
{
|
{
|
||||||
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||||
|
|
||||||
@ -1317,7 +1204,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return TextureViewCompatibility.LayoutIncompatible;
|
return TextureViewCompatibility.LayoutIncompatible;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel));
|
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, exactSize, firstLevel));
|
||||||
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
|
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -1750,13 +1637,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoveFromPools(true);
|
RemoveFromPools(true);
|
||||||
|
|
||||||
// We only want to remove if there's no mapped region of the texture that was modified by the GPU,
|
|
||||||
// otherwise we could lose data.
|
|
||||||
if (!Group.AnyModified(this))
|
|
||||||
{
|
|
||||||
_physicalMemory.TextureCache.QueueAutoDeleteCacheRemoval(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -210,8 +210,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
ulong offset,
|
ulong offset,
|
||||||
FormatInfo formatInfo,
|
FormatInfo formatInfo,
|
||||||
bool shouldCreate,
|
bool shouldCreate,
|
||||||
bool preferScaling = true,
|
bool preferScaling,
|
||||||
Size? sizeHint = null)
|
Size sizeHint)
|
||||||
{
|
{
|
||||||
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
||||||
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
copyTexture.Address.Pack() + offset,
|
copyTexture.Address.Pack() + offset,
|
||||||
width,
|
GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, copyTexture.LinearLayout),
|
||||||
copyTexture.Height,
|
copyTexture.Height,
|
||||||
copyTexture.Depth,
|
copyTexture.Depth,
|
||||||
1,
|
1,
|
||||||
@ -255,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
flags |= TextureSearchFlags.NoCreate;
|
flags |= TextureSearchFlags.NoCreate;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
colorState.Address.Pack(),
|
colorState.Address.Pack(),
|
||||||
width,
|
GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, isLinear),
|
||||||
colorState.Height,
|
colorState.Height,
|
||||||
colorState.Depth,
|
colorState.Depth,
|
||||||
1,
|
1,
|
||||||
@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
|
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
@ -395,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
dsState.Address.Pack(),
|
dsState.Address.Pack(),
|
||||||
size.Width,
|
GetMinimumWidthInGob(size.Width, sizeHint.Width, formatInfo.BytesPerPixel, false),
|
||||||
size.Height,
|
size.Height,
|
||||||
size.Depth,
|
size.Depth,
|
||||||
1,
|
1,
|
||||||
@ -409,13 +409,41 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
target,
|
target,
|
||||||
formatInfo);
|
formatInfo);
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For block linear textures, gets the minimum width of the texture
|
||||||
|
/// that would still have the same number of GOBs per row as the original width.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">The possibly aligned texture width</param>
|
||||||
|
/// <param name="minimumWidth">The minimum width that the texture may have without losing data</param>
|
||||||
|
/// <param name="bytesPerPixel">Bytes per pixel of the texture format</param>
|
||||||
|
/// <param name="isLinear">True if the texture is linear, false for block linear</param>
|
||||||
|
/// <returns>The minimum width of the texture with the same amount of GOBs per row</returns>
|
||||||
|
private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPerPixel, bool isLinear)
|
||||||
|
{
|
||||||
|
if (isLinear || (uint)minimumWidth >= (uint)width)
|
||||||
|
{
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the minimum possible that would not cause data loss
|
||||||
|
// and would be still within the same GOB (aligned size would be the same).
|
||||||
|
// This is useful for render and copy operations, where we don't know the
|
||||||
|
// exact width of the texture, but it doesn't matter, as long the texture is
|
||||||
|
// at least as large as the region being rendered or copied.
|
||||||
|
|
||||||
|
int alignment = 64 / bytesPerPixel;
|
||||||
|
int widthAligned = BitUtils.AlignUp(width, alignment);
|
||||||
|
|
||||||
|
return Math.Clamp(widthAligned - alignment + 1, minimumWidth, widthAligned);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture, or create a new one if not found.
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -423,7 +451,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
||||||
/// <param name="info">Texture information of the texture to be found or created</param>
|
/// <param name="info">Texture information of the texture to be found or created</param>
|
||||||
/// <param name="layerSize">Size in bytes of a single texture layer</param>
|
/// <param name="layerSize">Size in bytes of a single texture layer</param>
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
|
||||||
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
|
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
|
||||||
/// <returns>The texture</returns>
|
/// <returns>The texture</returns>
|
||||||
public Texture FindOrCreateTexture(
|
public Texture FindOrCreateTexture(
|
||||||
@ -431,7 +458,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
TextureSearchFlags flags,
|
TextureSearchFlags flags,
|
||||||
TextureInfo info,
|
TextureInfo info,
|
||||||
int layerSize = 0,
|
int layerSize = 0,
|
||||||
Size? sizeHint = null,
|
|
||||||
MultiRange? range = null)
|
MultiRange? range = null)
|
||||||
{
|
{
|
||||||
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
|
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
|
||||||
@ -448,6 +474,29 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
address = memoryManager.Translate(info.GpuAddress);
|
address = memoryManager.Translate(info.GpuAddress);
|
||||||
|
|
||||||
|
// If the start address is unmapped, let's try to find a page of memory that is mapped.
|
||||||
|
if (address == MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
// 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 == MemoryManager.PteUnmapped)
|
if (address == MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@ -512,8 +561,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);
|
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
@ -568,6 +615,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
|
TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
|
||||||
info,
|
info,
|
||||||
range.Value,
|
range.Value,
|
||||||
|
isSamplerTexture,
|
||||||
sizeInfo.LayerSize,
|
sizeInfo.LayerSize,
|
||||||
_context.Capabilities,
|
_context.Capabilities,
|
||||||
out int firstLayer,
|
out int firstLayer,
|
||||||
@ -598,17 +646,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (oInfo.Compatibility == TextureViewCompatibility.Full)
|
if (oInfo.Compatibility == TextureViewCompatibility.Full)
|
||||||
{
|
{
|
||||||
TextureInfo adjInfo = AdjustSizes(overlap, info, oInfo.FirstLevel);
|
|
||||||
|
|
||||||
if (!isSamplerTexture)
|
if (!isSamplerTexture)
|
||||||
{
|
{
|
||||||
info = adjInfo;
|
// If this is not a sampler texture, the size might be different from the requested size,
|
||||||
|
// so we need to make sure the texture information has the correct size for this base texture,
|
||||||
|
// before creating the view.
|
||||||
|
info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
texture = overlap.CreateView(adjInfo, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
|
texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
|
||||||
|
|
||||||
ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);
|
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -682,6 +728,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
TextureViewCompatibility compatibility = texture.IsViewCompatible(
|
TextureViewCompatibility compatibility = texture.IsViewCompatible(
|
||||||
overlap.Info,
|
overlap.Info,
|
||||||
overlap.Range,
|
overlap.Range,
|
||||||
|
exactSize: true,
|
||||||
overlap.LayerSize,
|
overlap.LayerSize,
|
||||||
_context.Capabilities,
|
_context.Capabilities,
|
||||||
out int firstLayer,
|
out int firstLayer,
|
||||||
@ -792,7 +839,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel);
|
// Note: If we allow different sizes for those overlaps,
|
||||||
|
// we need to make sure that the "info" has the correct size for the parent texture here.
|
||||||
|
// Since this is not allowed right now, we don't need to do it.
|
||||||
|
|
||||||
|
TextureInfo overlapInfo = overlap.Info;
|
||||||
|
|
||||||
if (texture.ScaleFactor != overlap.ScaleFactor)
|
if (texture.ScaleFactor != overlap.ScaleFactor)
|
||||||
{
|
{
|
||||||
@ -856,44 +907,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes a texture's size to match the desired size for samplers,
|
|
||||||
/// or increases a texture's size to fit the region indicated by a size hint.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The desired texture info</param>
|
|
||||||
/// <param name="texture">The texture to resize</param>
|
|
||||||
/// <param name="isSamplerTexture">True if the texture will be used for a sampler, false otherwise</param>
|
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
|
||||||
private void ChangeSizeIfNeeded(TextureInfo info, Texture texture, bool isSamplerTexture, Size? sizeHint)
|
|
||||||
{
|
|
||||||
if (isSamplerTexture)
|
|
||||||
{
|
|
||||||
// If this is used for sampling, the size must match,
|
|
||||||
// otherwise the shader would sample garbage data.
|
|
||||||
// To fix that, we create a new texture with the correct
|
|
||||||
// size, and copy the data from the old one to the new one.
|
|
||||||
|
|
||||||
if (!TextureCompatibility.SizeMatches(texture.Info, info))
|
|
||||||
{
|
|
||||||
texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (sizeHint != null)
|
|
||||||
{
|
|
||||||
// A size hint indicates that data will be used within that range, at least.
|
|
||||||
// If the texture is smaller than the size hint, it must be enlarged to meet it.
|
|
||||||
// The maximum size is provided by the requested info, which generally has an aligned size.
|
|
||||||
|
|
||||||
int width = Math.Max(texture.Info.Width, Math.Min(sizeHint.Value.Width, info.Width));
|
|
||||||
int height = Math.Max(texture.Info.Height, Math.Min(sizeHint.Value.Height, info.Height));
|
|
||||||
|
|
||||||
if (texture.Info.Width != width || texture.Info.Height != height)
|
|
||||||
{
|
|
||||||
texture.ChangeSize(width, height, info.DepthOrLayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to find a texture on the short duration cache.
|
/// Attempt to find a texture on the short duration cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1000,92 +1013,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adjusts the size of the texture information for a given mipmap level,
|
|
||||||
/// based on the size of a parent texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parent">The parent texture</param>
|
|
||||||
/// <param name="info">The texture information to be adjusted</param>
|
|
||||||
/// <param name="firstLevel">The first level of the texture view</param>
|
|
||||||
/// <returns>The adjusted texture information with the new size</returns>
|
|
||||||
private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
|
|
||||||
{
|
|
||||||
// When the texture is used as view of another texture, we must
|
|
||||||
// ensure that the sizes are valid, otherwise data uploads would fail
|
|
||||||
// (and the size wouldn't match the real size used on the host API).
|
|
||||||
// Given a parent texture from where the view is created, we have the
|
|
||||||
// following rules:
|
|
||||||
// - The view size must be equal to the parent size, divided by (2 ^ l),
|
|
||||||
// where l is the first mipmap level of the view. The division result must
|
|
||||||
// be rounded down, and the result must be clamped to 1.
|
|
||||||
// - If the parent format is compressed, and the view format isn't, the
|
|
||||||
// view size is calculated as above, but the width and height of the
|
|
||||||
// view must be also divided by the compressed format block width and height.
|
|
||||||
// - If the parent format is not compressed, and the view is, the view
|
|
||||||
// size is calculated as described on the first point, but the width and height
|
|
||||||
// of the view must be also multiplied by the block width and height.
|
|
||||||
int width = Math.Max(1, parent.Info.Width >> firstLevel);
|
|
||||||
int height = Math.Max(1, parent.Info.Height >> firstLevel);
|
|
||||||
|
|
||||||
if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed)
|
|
||||||
{
|
|
||||||
width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
|
|
||||||
height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
|
|
||||||
}
|
|
||||||
else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed)
|
|
||||||
{
|
|
||||||
width *= info.FormatInfo.BlockWidth;
|
|
||||||
height *= info.FormatInfo.BlockHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
int depthOrLayers;
|
|
||||||
|
|
||||||
if (info.Target == Target.Texture3D)
|
|
||||||
{
|
|
||||||
depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
depthOrLayers = info.DepthOrLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2D and 2D multisample textures are not considered compatible.
|
|
||||||
// This specific case is required for copies, where the source texture might be multisample.
|
|
||||||
// In this case, we inherit the parent texture multisample state.
|
|
||||||
Target target = info.Target;
|
|
||||||
int samplesInX = info.SamplesInX;
|
|
||||||
int samplesInY = info.SamplesInY;
|
|
||||||
|
|
||||||
if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample)
|
|
||||||
{
|
|
||||||
target = Target.Texture2DMultisample;
|
|
||||||
samplesInX = parent.Info.SamplesInX;
|
|
||||||
samplesInY = parent.Info.SamplesInY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TextureInfo(
|
|
||||||
info.GpuAddress,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
depthOrLayers,
|
|
||||||
info.Levels,
|
|
||||||
samplesInX,
|
|
||||||
samplesInY,
|
|
||||||
info.Stride,
|
|
||||||
info.IsLinear,
|
|
||||||
info.GobBlocksInY,
|
|
||||||
info.GobBlocksInZ,
|
|
||||||
info.GobBlocksInTileX,
|
|
||||||
target,
|
|
||||||
info.FormatInfo,
|
|
||||||
info.DepthStencilMode,
|
|
||||||
info.SwizzleR,
|
|
||||||
info.SwizzleG,
|
|
||||||
info.SwizzleB,
|
|
||||||
info.SwizzleA);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a texture creation information from texture information.
|
/// Gets a texture creation information from texture information.
|
||||||
/// This can be used to create new host textures.
|
/// This can be used to create new host textures.
|
||||||
@ -1175,19 +1102,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues the removal of a texture from the auto delete cache.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This function is thread safe and can be called from any thread.
|
|
||||||
/// The texture will be deleted on the next time the cache is used.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="texture">The texture to be removed</param>
|
|
||||||
public void QueueAutoDeleteCacheRemoval(Texture texture)
|
|
||||||
{
|
|
||||||
_cache.RemoveDeferred(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -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>
|
||||||
@ -380,42 +345,44 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lhs">Texture information of the texture view</param>
|
/// <param name="lhs">Texture information of the texture view</param>
|
||||||
/// <param name="rhs">Texture information of the texture view to match against</param>
|
/// <param name="rhs">Texture information of the texture view to match against</param>
|
||||||
|
/// <param name="exact">Indicates if the sizes must be exactly equal</param>
|
||||||
/// <param name="level">Mipmap level of the texture view in relation to this texture</param>
|
/// <param name="level">Mipmap level of the texture view in relation to this texture</param>
|
||||||
/// <returns>The view compatibility level of the view sizes</returns>
|
/// <returns>The view compatibility level of the view sizes</returns>
|
||||||
public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, int level)
|
public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact, int level)
|
||||||
{
|
{
|
||||||
Size size = GetAlignedSize(lhs, level);
|
Size lhsAlignedSize = GetAlignedSize(lhs, level);
|
||||||
|
Size rhsAlignedSize = GetAlignedSize(rhs);
|
||||||
|
|
||||||
Size otherSize = GetAlignedSize(rhs);
|
Size lhsSize = GetSizeInBlocks(lhs, level);
|
||||||
|
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,
|
||||||
// so the depth may be different in this case.
|
// so the depth may be different in this case.
|
||||||
if (rhs.Target == Target.Texture3D && size.Depth != otherSize.Depth)
|
if (rhs.Target == Target.Texture3D && lhsSize.Depth != rhsSize.Depth)
|
||||||
{
|
{
|
||||||
result = TextureViewCompatibility.CopyOnly;
|
result = TextureViewCompatibility.CopyOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size.Width == otherSize.Width && size.Height == otherSize.Height)
|
// Some APIs align the width for copy and render target textures,
|
||||||
|
// 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.
|
||||||
|
// We expect height to always match exactly, if the texture is the same.
|
||||||
|
if (alignedWidthMatches && lhsSize.Height == rhsSize.Height)
|
||||||
{
|
{
|
||||||
if (level > 0 && result == TextureViewCompatibility.Full)
|
return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width
|
||||||
{
|
? TextureViewCompatibility.CopyOnly
|
||||||
// A resize should not change the aligned size of the largest mip.
|
: result;
|
||||||
// If it would, then create a copy dependency rather than a full view.
|
|
||||||
|
|
||||||
Size mip0SizeLhs = GetAlignedSize(lhs);
|
|
||||||
Size mip0SizeRhs = GetLargestAlignedSize(rhs, level);
|
|
||||||
|
|
||||||
if (mip0SizeLhs.Width != mip0SizeRhs.Width || mip0SizeLhs.Height != mip0SizeRhs.Height)
|
|
||||||
{
|
|
||||||
result = TextureViewCompatibility.CopyOnly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
else if (lhs.IsLinear && rhs.IsLinear)
|
else if (lhs.IsLinear && rhs.IsLinear && lhsSize.Height == rhsSize.Height)
|
||||||
{
|
{
|
||||||
// Copy between linear textures with matching stride.
|
// Copy between linear textures with matching stride.
|
||||||
int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
|
int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
|
||||||
@ -454,57 +421,33 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
/// <param name="lhs">Texture information to compare</param>
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
/// <param name="rhs">Texture information to compare with</param>
|
||||||
/// <returns>True if the size matches, false otherwise</returns>
|
/// <param name="exact">Indicates if the size must be exactly equal between the textures, or if <paramref name="rhs"/> is allowed to be larger</param>
|
||||||
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs)
|
|
||||||
{
|
|
||||||
return SizeMatches(lhs, rhs, alignSizes: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the texture sizes of the supplied texture informations match the given level
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
|
||||||
/// <param name="level">Mipmap level of this texture to compare with</param>
|
|
||||||
/// <returns>True if the size matches with the level, false otherwise</returns>
|
|
||||||
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, int level)
|
|
||||||
{
|
|
||||||
return Math.Max(1, lhs.Width >> level) == rhs.Width &&
|
|
||||||
Math.Max(1, lhs.Height >> level) == rhs.Height &&
|
|
||||||
Math.Max(1, lhs.GetDepth() >> level) == rhs.GetDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the texture sizes of the supplied texture informations match.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
|
||||||
/// <param name="alignSizes">True to align the sizes according to the texture layout for comparison</param>
|
|
||||||
/// <param name="lhsLevel">Mip level of the lhs texture. Aligned sizes are compared for the largest mip</param>
|
|
||||||
/// <returns>True if the sizes matches, false otherwise</returns>
|
/// <returns>True if the sizes matches, false otherwise</returns>
|
||||||
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool alignSizes, int lhsLevel = 0)
|
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact)
|
||||||
{
|
{
|
||||||
if (lhs.GetLayers() != rhs.GetLayers())
|
if (lhs.GetLayers() != rhs.GetLayers())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isTextureBuffer = lhs.Target == Target.TextureBuffer || rhs.Target == Target.TextureBuffer;
|
Size lhsSize = GetSizeInBlocks(lhs);
|
||||||
|
Size rhsSize = GetSizeInBlocks(rhs);
|
||||||
|
|
||||||
if (alignSizes && !isTextureBuffer)
|
if (exact || lhs.IsLinear || rhs.IsLinear)
|
||||||
{
|
{
|
||||||
Size size0 = GetLargestAlignedSize(lhs, lhsLevel);
|
return lhsSize.Width == rhsSize.Width &&
|
||||||
Size size1 = GetLargestAlignedSize(rhs, lhsLevel);
|
lhsSize.Height == rhsSize.Height &&
|
||||||
|
lhsSize.Depth == rhsSize.Depth;
|
||||||
return size0.Width == size1.Width &&
|
|
||||||
size0.Height == size1.Height &&
|
|
||||||
size0.Depth == size1.Depth;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return lhs.Width == rhs.Width &&
|
Size lhsAlignedSize = GetAlignedSize(lhs);
|
||||||
lhs.Height == rhs.Height &&
|
Size rhsAlignedSize = GetAlignedSize(rhs);
|
||||||
lhs.GetDepth() == rhs.GetDepth();
|
|
||||||
|
return lhsAlignedSize.Width == rhsAlignedSize.Width &&
|
||||||
|
lhsSize.Width >= rhsSize.Width &&
|
||||||
|
lhsSize.Height == rhsSize.Height &&
|
||||||
|
lhsSize.Depth == rhsSize.Depth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,22 +486,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the aligned sizes of the specified texture information, shifted to the largest mip from a given level.
|
|
||||||
/// The alignment depends on the texture layout and format bytes per pixel.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">Texture information to calculate the aligned size from</param>
|
|
||||||
/// <param name="level">Mipmap level for texture views. Shifts the aligned size to represent the largest mip level</param>
|
|
||||||
/// <returns>The aligned texture size of the largest mip level</returns>
|
|
||||||
public static Size GetLargestAlignedSize(TextureInfo info, int level)
|
|
||||||
{
|
|
||||||
int width = info.Width << level;
|
|
||||||
int height = info.Height << level;
|
|
||||||
int depth = info.GetDepth() << level;
|
|
||||||
|
|
||||||
return GetAlignedSize(info, width, height, depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the aligned sizes of the specified texture information.
|
/// Gets the aligned sizes of the specified texture information.
|
||||||
/// The alignment depends on the texture layout and format bytes per pixel.
|
/// The alignment depends on the texture layout and format bytes per pixel.
|
||||||
@ -575,6 +502,25 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return GetAlignedSize(info, width, height, depth);
|
return GetAlignedSize(info, width, height, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size in blocks for the given texture information.
|
||||||
|
/// For non-compressed formats, that's the same as the regular size.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">Texture information to calculate the aligned size from</param>
|
||||||
|
/// <param name="level">Mipmap level for texture views</param>
|
||||||
|
/// <returns>The texture size in blocks</returns>
|
||||||
|
public static Size GetSizeInBlocks(TextureInfo info, int level = 0)
|
||||||
|
{
|
||||||
|
int width = Math.Max(1, info.Width >> level);
|
||||||
|
int height = Math.Max(1, info.Height >> level);
|
||||||
|
int depth = Math.Max(1, info.GetDepth() >> level);
|
||||||
|
|
||||||
|
return new Size(
|
||||||
|
BitUtils.DivRoundUp(width, info.FormatInfo.BlockWidth),
|
||||||
|
BitUtils.DivRoundUp(height, info.FormatInfo.BlockHeight),
|
||||||
|
depth);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if it's possible to create a view with the layout of the second texture information from the first.
|
/// Check if it's possible to create a view with the layout of the second texture information from the first.
|
||||||
/// The layout information is composed of the Stride for linear textures, or GOB block size
|
/// The layout information is composed of the Stride for linear textures, or GOB block size
|
||||||
@ -685,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.
|
||||||
|
@ -336,24 +336,23 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (_loadNeeded[baseHandle + i])
|
if (_loadNeeded[baseHandle + i])
|
||||||
{
|
{
|
||||||
var info = GetHandleInformation(baseHandle + i);
|
var info = GetHandleInformation(baseHandle + i);
|
||||||
int offsetIndex = info.Index;
|
|
||||||
|
|
||||||
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
|
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
|
||||||
for (int layer = 0; layer < info.Layers; layer++)
|
for (int layer = 0; layer < info.Layers; layer++)
|
||||||
{
|
{
|
||||||
for (int level = 0; level < info.Levels; level++)
|
for (int level = 0; level < info.Levels; level++)
|
||||||
{
|
{
|
||||||
|
int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
|
||||||
|
|
||||||
int offset = _allOffsets[offsetIndex];
|
int offset = _allOffsets[offsetIndex];
|
||||||
int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size);
|
int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size);
|
||||||
int size = endOffset - offset;
|
int size = endOffset - offset;
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
||||||
|
|
||||||
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
|
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
|
||||||
|
|
||||||
Storage.SetData(result, info.BaseLayer, info.BaseLevel);
|
Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
|
||||||
|
|
||||||
offsetIndex++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -434,32 +433,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a texture was modified by the GPU.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="texture">The texture to be checked</param>
|
|
||||||
/// <returns>True if any region of the texture was modified by the GPU, false otherwise</returns>
|
|
||||||
public bool AnyModified(Texture texture)
|
|
||||||
{
|
|
||||||
bool anyModified = false;
|
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < regionCount; i++)
|
|
||||||
{
|
|
||||||
TextureGroupHandle group = _handles[baseHandle + i];
|
|
||||||
|
|
||||||
if (group.Modified)
|
|
||||||
{
|
|
||||||
anyModified = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return anyModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flush modified ranges for a given texture.
|
/// Flush modified ranges for a given texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -881,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>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
@ -292,5 +294,88 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
layerSize);
|
layerSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates texture information for a given mipmap level of the specified parent texture and this information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">The parent texture</param>
|
||||||
|
/// <param name="firstLevel">The first level of the texture view</param>
|
||||||
|
/// <returns>The adjusted texture information with the new size</returns>
|
||||||
|
public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel)
|
||||||
|
{
|
||||||
|
// When the texture is used as view of another texture, we must
|
||||||
|
// ensure that the sizes are valid, otherwise data uploads would fail
|
||||||
|
// (and the size wouldn't match the real size used on the host API).
|
||||||
|
// Given a parent texture from where the view is created, we have the
|
||||||
|
// following rules:
|
||||||
|
// - The view size must be equal to the parent size, divided by (2 ^ l),
|
||||||
|
// where l is the first mipmap level of the view. The division result must
|
||||||
|
// be rounded down, and the result must be clamped to 1.
|
||||||
|
// - If the parent format is compressed, and the view format isn't, the
|
||||||
|
// view size is calculated as above, but the width and height of the
|
||||||
|
// view must be also divided by the compressed format block width and height.
|
||||||
|
// - If the parent format is not compressed, and the view is, the view
|
||||||
|
// size is calculated as described on the first point, but the width and height
|
||||||
|
// of the view must be also multiplied by the block width and height.
|
||||||
|
int width = Math.Max(1, parent.Info.Width >> firstLevel);
|
||||||
|
int height = Math.Max(1, parent.Info.Height >> firstLevel);
|
||||||
|
|
||||||
|
if (parent.Info.FormatInfo.IsCompressed && !FormatInfo.IsCompressed)
|
||||||
|
{
|
||||||
|
width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
|
||||||
|
height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
|
||||||
|
}
|
||||||
|
else if (!parent.Info.FormatInfo.IsCompressed && FormatInfo.IsCompressed)
|
||||||
|
{
|
||||||
|
width *= FormatInfo.BlockWidth;
|
||||||
|
height *= FormatInfo.BlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int depthOrLayers;
|
||||||
|
|
||||||
|
if (Target == Target.Texture3D)
|
||||||
|
{
|
||||||
|
depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
depthOrLayers = DepthOrLayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2D and 2D multisample textures are not considered compatible.
|
||||||
|
// This specific case is required for copies, where the source texture might be multisample.
|
||||||
|
// In this case, we inherit the parent texture multisample state.
|
||||||
|
Target target = Target;
|
||||||
|
int samplesInX = SamplesInX;
|
||||||
|
int samplesInY = SamplesInY;
|
||||||
|
|
||||||
|
if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample)
|
||||||
|
{
|
||||||
|
target = Target.Texture2DMultisample;
|
||||||
|
samplesInX = parent.Info.SamplesInX;
|
||||||
|
samplesInY = parent.Info.SamplesInY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextureInfo(
|
||||||
|
GpuAddress,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depthOrLayers,
|
||||||
|
Levels,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY,
|
||||||
|
Stride,
|
||||||
|
IsLinear,
|
||||||
|
GobBlocksInY,
|
||||||
|
GobBlocksInZ,
|
||||||
|
GobBlocksInTileX,
|
||||||
|
target,
|
||||||
|
FormatInfo,
|
||||||
|
DepthStencilMode,
|
||||||
|
SwizzleR,
|
||||||
|
SwizzleG,
|
||||||
|
SwizzleB,
|
||||||
|
SwizzleA);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -437,22 +437,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update host framebuffer attachments based on currently bound render target buffers.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// All attachments other than <paramref name="index"/> will be unbound.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="index">Index of the render target color to be updated</param>
|
|
||||||
public void UpdateRenderTarget(int index)
|
|
||||||
{
|
|
||||||
new Span<ITexture>(_rtHostColors).Fill(null);
|
|
||||||
_rtHostColors[index] = _rtColors[index]?.HostTexture;
|
|
||||||
_rtHostDs = null;
|
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update host framebuffer attachments based on currently bound render target buffers.
|
/// Update host framebuffer attachments based on currently bound render target buffers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -77,22 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (texture.ChangedSize)
|
// On the path above (texture not yet in the pool), memory is automatically synchronized on texture creation.
|
||||||
{
|
|
||||||
// Texture changed size at one point - it may be a different size than the sampler expects.
|
|
||||||
// This can be triggered when the size is changed by a size hint on copy or draw, but the texture has been sampled before.
|
|
||||||
|
|
||||||
int baseLevel = descriptor.UnpackBaseLevel();
|
|
||||||
int width = Math.Max(1, descriptor.UnpackWidth() >> baseLevel);
|
|
||||||
int height = Math.Max(1, descriptor.UnpackHeight() >> baseLevel);
|
|
||||||
|
|
||||||
if (texture.Info.Width != width || texture.Info.Height != height)
|
|
||||||
{
|
|
||||||
texture.ChangeSize(width, height, texture.Info.DepthOrLayers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memory is automatically synchronized on texture creation.
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
enum TextureSearchFlags
|
enum TextureSearchFlags
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Strict = 1 << 0,
|
|
||||||
ForSampler = 1 << 1,
|
ForSampler = 1 << 1,
|
||||||
ForCopy = 1 << 2,
|
ForCopy = 1 << 2,
|
||||||
WithUpscale = 1 << 3,
|
WithUpscale = 1 << 3,
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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>
|
||||||
|
@ -583,6 +583,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return UnpackPaFromPte(pte) + (va & PageMask);
|
return UnpackPaFromPte(pte) + (va & PageMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Translates a GPU virtual address to a CPU virtual address on the first mapped page of memory
|
||||||
|
/// on the specified region.
|
||||||
|
/// If no page is mapped on the specified region, <see cref="PteUnmapped"/> is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address to be translated</param>
|
||||||
|
/// <param name="size">Size of the range to be translated</param>
|
||||||
|
/// <returns>CPU virtual address, or <see cref="PteUnmapped"/> if unmapped</returns>
|
||||||
|
public ulong TranslateFirstMapped(ulong va, ulong size)
|
||||||
|
{
|
||||||
|
if (!ValidateAddress(va))
|
||||||
|
{
|
||||||
|
return PteUnmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong endVa = va + size;
|
||||||
|
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
for (; va < endVa && pte == PteUnmapped; va += PageSize - (va & PageMask))
|
||||||
|
{
|
||||||
|
pte = GetPte(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return PteUnmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnpackPaFromPte(pte) + (va & PageMask);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the kind of a given memory page.
|
/// Gets the kind of a given memory page.
|
||||||
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
|
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
|
||||||
|
@ -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>
|
||||||
|
13
Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs
Normal file
13
Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Kind of a GPU resource.
|
||||||
|
/// </summary>
|
||||||
|
enum ResourceKind
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Buffer,
|
||||||
|
Texture,
|
||||||
|
Pool
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 4369;
|
private const uint CodeGenVersion = 4368;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
@ -774,6 +774,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
sBuffers,
|
sBuffers,
|
||||||
textures,
|
textures,
|
||||||
images,
|
images,
|
||||||
|
ShaderIdentification.None,
|
||||||
|
0,
|
||||||
dataInfo.Stage,
|
dataInfo.Stage,
|
||||||
dataInfo.UsesInstanceId,
|
dataInfo.UsesInstanceId,
|
||||||
dataInfo.UsesDrawParameters,
|
dataInfo.UsesDrawParameters,
|
||||||
|
@ -633,6 +633,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_context.Capabilities.SupportsGeometryShader)
|
||||||
|
{
|
||||||
|
ShaderCache.TryRemoveGeometryStage(translatorContexts);
|
||||||
|
}
|
||||||
|
|
||||||
CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
|
CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
|
||||||
List<ShaderProgram> translatedStages = new List<ShaderProgram>();
|
List<ShaderProgram> translatedStages = new List<ShaderProgram>();
|
||||||
|
|
||||||
|
@ -126,6 +126,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
|
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
|
||||||
|
|
||||||
|
public bool QueryHostSupportsGeometryShader() => _context.Capabilities.SupportsGeometryShader;
|
||||||
|
|
||||||
public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough;
|
public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough;
|
||||||
|
|
||||||
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
|
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
|
||||||
|
@ -353,6 +353,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_context.Capabilities.SupportsGeometryShader)
|
||||||
|
{
|
||||||
|
TryRemoveGeometryStage(translatorContexts);
|
||||||
|
}
|
||||||
|
|
||||||
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
||||||
List<ShaderSource> shaderSources = new List<ShaderSource>();
|
List<ShaderSource> shaderSources = new List<ShaderSource>();
|
||||||
|
|
||||||
@ -421,6 +426,39 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return gpShaders;
|
return gpShaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to eliminate the geometry stage from the array of translator contexts.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="translatorContexts">Array of translator contexts</param>
|
||||||
|
public static void TryRemoveGeometryStage(TranslatorContext[] translatorContexts)
|
||||||
|
{
|
||||||
|
if (translatorContexts[4] != null)
|
||||||
|
{
|
||||||
|
// We have a geometry shader, but geometry shaders are not supported.
|
||||||
|
// Try to eliminate the geometry shader.
|
||||||
|
|
||||||
|
ShaderProgramInfo info = translatorContexts[4].Translate().Info;
|
||||||
|
|
||||||
|
if (info.Identification == ShaderIdentification.GeometryLayerPassthrough)
|
||||||
|
{
|
||||||
|
// We managed to identify that this geometry shader is only used to set the output Layer value,
|
||||||
|
// we can set the Layer on the previous stage instead (usually the vertex stage) and eliminate it.
|
||||||
|
|
||||||
|
for (int i = 3; i >= 1; i--)
|
||||||
|
{
|
||||||
|
if (translatorContexts[i] != null)
|
||||||
|
{
|
||||||
|
translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute);
|
||||||
|
translatorContexts[i].SetLastInVertexPipeline(translatorContexts[5] != null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
translatorContexts[4] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a shader source for use with the backend from a translated shader program.
|
/// Creates a shader source for use with the backend from a translated shader program.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
|
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
|
@ -202,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
{
|
{
|
||||||
pt.AcquireCallback(_context, pt.UserObj);
|
pt.AcquireCallback(_context, pt.UserObj);
|
||||||
|
|
||||||
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
|
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, pt.Range);
|
||||||
|
|
||||||
pt.Cache.Tick();
|
pt.Cache.Tick();
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using Ryujinx.Graphics.Nvdec.Vp9.Common;
|
||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Ryujinx.Graphics.Nvdec.Vp9.Common;
|
|
||||||
using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.TxfmCommon;
|
using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.TxfmCommon;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
|
namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
|
namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
|
||||||
{
|
{
|
||||||
|
177
Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs
Normal file
177
Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.OpenGL.Image;
|
||||||
|
using System;
|
||||||
|
using static Ryujinx.Graphics.OpenGL.Effects.ShaderHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
|
{
|
||||||
|
internal class FsrScalingFilter : IScalingFilter
|
||||||
|
{
|
||||||
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
private int _inputUniform;
|
||||||
|
private int _outputUniform;
|
||||||
|
private int _sharpeningUniform;
|
||||||
|
private int _srcX0Uniform;
|
||||||
|
private int _srcX1Uniform;
|
||||||
|
private int _srcY0Uniform;
|
||||||
|
private int _scalingShaderProgram;
|
||||||
|
private int _sharpeningShaderProgram;
|
||||||
|
private float _scale = 1;
|
||||||
|
private int _srcY1Uniform;
|
||||||
|
private int _dstX0Uniform;
|
||||||
|
private int _dstX1Uniform;
|
||||||
|
private int _dstY0Uniform;
|
||||||
|
private int _dstY1Uniform;
|
||||||
|
private int _scaleXUniform;
|
||||||
|
private int _scaleYUniform;
|
||||||
|
private TextureStorage _intermediaryTexture;
|
||||||
|
|
||||||
|
public float Level
|
||||||
|
{
|
||||||
|
get => _scale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_scale = MathF.Max(0.01f, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FsrScalingFilter(OpenGLRenderer renderer, IPostProcessingEffect filter)
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
_renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_scalingShaderProgram != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteProgram(_scalingShaderProgram);
|
||||||
|
GL.DeleteProgram(_sharpeningShaderProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
_intermediaryTexture?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
var scalingShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl");
|
||||||
|
var sharpeningShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl");
|
||||||
|
var fsrA = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h");
|
||||||
|
var fsr1 = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h");
|
||||||
|
|
||||||
|
scalingShader = scalingShader.Replace("#include \"ffx_a.h\"", fsrA);
|
||||||
|
scalingShader = scalingShader.Replace("#include \"ffx_fsr1.h\"", fsr1);
|
||||||
|
sharpeningShader = sharpeningShader.Replace("#include \"ffx_a.h\"", fsrA);
|
||||||
|
sharpeningShader = sharpeningShader.Replace("#include \"ffx_fsr1.h\"", fsr1);
|
||||||
|
|
||||||
|
_scalingShaderProgram = CompileProgram(scalingShader, ShaderType.ComputeShader);
|
||||||
|
_sharpeningShaderProgram = CompileProgram(sharpeningShader, ShaderType.ComputeShader);
|
||||||
|
|
||||||
|
_inputUniform = GL.GetUniformLocation(_scalingShaderProgram, "Source");
|
||||||
|
_outputUniform = GL.GetUniformLocation(_scalingShaderProgram, "imgOutput");
|
||||||
|
_sharpeningUniform = GL.GetUniformLocation(_sharpeningShaderProgram, "sharpening");
|
||||||
|
|
||||||
|
_srcX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX0");
|
||||||
|
_srcX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX1");
|
||||||
|
_srcY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY0");
|
||||||
|
_srcY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY1");
|
||||||
|
_dstX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX0");
|
||||||
|
_dstX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX1");
|
||||||
|
_dstY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY0");
|
||||||
|
_dstY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY1");
|
||||||
|
_scaleXUniform = GL.GetUniformLocation(_scalingShaderProgram, "scaleX");
|
||||||
|
_scaleYUniform = GL.GetUniformLocation(_scalingShaderProgram, "scaleY");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(
|
||||||
|
TextureView view,
|
||||||
|
TextureView destinationTexture,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
Extents2D source,
|
||||||
|
Extents2D destination)
|
||||||
|
{
|
||||||
|
if (_intermediaryTexture == null || _intermediaryTexture.Info.Width != width || _intermediaryTexture.Info.Height != height)
|
||||||
|
{
|
||||||
|
_intermediaryTexture?.Dispose();
|
||||||
|
var originalInfo = view.Info;
|
||||||
|
var info = new TextureCreateInfo(width,
|
||||||
|
height,
|
||||||
|
originalInfo.Depth,
|
||||||
|
originalInfo.Levels,
|
||||||
|
originalInfo.Samples,
|
||||||
|
originalInfo.BlockWidth,
|
||||||
|
originalInfo.BlockHeight,
|
||||||
|
originalInfo.BytesPerPixel,
|
||||||
|
originalInfo.Format,
|
||||||
|
originalInfo.DepthStencilMode,
|
||||||
|
originalInfo.Target,
|
||||||
|
originalInfo.SwizzleR,
|
||||||
|
originalInfo.SwizzleG,
|
||||||
|
originalInfo.SwizzleB,
|
||||||
|
originalInfo.SwizzleA);
|
||||||
|
|
||||||
|
_intermediaryTexture = new TextureStorage(_renderer, info, view.ScaleFactor);
|
||||||
|
_intermediaryTexture.CreateDefaultView();
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureView = _intermediaryTexture.CreateView(_intermediaryTexture.Info, 0, 0) as TextureView;
|
||||||
|
|
||||||
|
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||||
|
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D);
|
||||||
|
|
||||||
|
GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
|
||||||
|
int threadGroupWorkRegionDim = 16;
|
||||||
|
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
|
||||||
|
// Scaling pass
|
||||||
|
float srcWidth = Math.Abs(source.X2 - source.X1);
|
||||||
|
float srcHeight = Math.Abs(source.Y2 - source.Y1);
|
||||||
|
float scaleX = srcWidth / view.Width;
|
||||||
|
float scaleY = srcHeight / view.Height;
|
||||||
|
GL.UseProgram(_scalingShaderProgram);
|
||||||
|
view.Bind(0);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform1(_srcX0Uniform, (float)source.X1);
|
||||||
|
GL.Uniform1(_srcX1Uniform, (float)source.X2);
|
||||||
|
GL.Uniform1(_srcY0Uniform, (float)source.Y1);
|
||||||
|
GL.Uniform1(_srcY1Uniform, (float)source.Y2);
|
||||||
|
GL.Uniform1(_dstX0Uniform, (float)destination.X1);
|
||||||
|
GL.Uniform1(_dstX1Uniform, (float)destination.X2);
|
||||||
|
GL.Uniform1(_dstY0Uniform, (float)destination.Y1);
|
||||||
|
GL.Uniform1(_dstY1Uniform, (float)destination.Y2);
|
||||||
|
GL.Uniform1(_scaleXUniform, scaleX);
|
||||||
|
GL.Uniform1(_scaleYUniform, scaleY);
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
// Sharpening Pass
|
||||||
|
GL.UseProgram(_sharpeningShaderProgram);
|
||||||
|
GL.BindImageTexture(0, destinationTexture.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
textureView.Bind(0);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform1(_sharpeningUniform, 1.5f - (Level * 0.01f * 1.5f));
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
GL.UseProgram(previousProgram);
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
||||||
|
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding);
|
||||||
|
|
||||||
|
GL.ActiveTexture((TextureUnit)previousUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user