Merge branch 'master' into ava-dcm

This commit is contained in:
Isaac Marovitz 2023-03-05 11:48:18 +00:00 committed by GitHub
commit 22e64f8891
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
254 changed files with 30661 additions and 1216 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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
{ {

View file

@ -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
{ {

View file

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

View file

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

View file

@ -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,12 +44,12 @@
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.26.1" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" /> <PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" />
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-9c9356d" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -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**

View file

@ -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
{ {

View file

@ -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
{ {

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
@ -380,7 +381,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
return _normalCurveLut2F; return _normalCurveLut2F;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static void ResampleDefaultQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount, bool needPitch) private unsafe static void ResampleDefaultQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount, bool needPitch)
{ {
ReadOnlySpan<float> parameters = GetDefaultParameter(ratio); ReadOnlySpan<float> parameters = GetDefaultParameter(ratio);
@ -394,7 +394,6 @@ 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); Vector128<float> parameter = Sse.LoadVector128(pParameters);
@ -424,14 +423,12 @@ namespace Ryujinx.Audio.Renderer.Dsp
Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123)); Sse.Store(pOutput + (uint)i, Sse41.RoundToNearestInteger(mix0123));
} }
} }
}
inputBufferIndex = i; inputBufferIndex = i;
} }
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)
@ -490,7 +487,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
} }
} }
} }
}
for (; i < sampleCount; i++) for (; i < sampleCount; i++)
{ {
@ -526,15 +522,39 @@ 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)
{
// Fast path; assumes 256-bit vectors for simplicity because the filter is 8 taps
fixed (short* pInput = inputBuffer)
fixed (float* pParameters = parameters)
{
for (int i = 0; i < sampleCount; i++)
{
int baseIndex = (int)(fraction * 128) * 8;
Vector256<int> intInput = Avx2.ConvertToVector256Int32(pInput + inputBufferIndex);
Vector256<float> floatInput = Avx.ConvertToVector256Single(intInput);
Vector256<float> parameter = Avx.LoadVector256(pParameters + baseIndex);
Vector256<float> dp = Avx.DotProduct(floatInput, parameter, control: 0xFF);
// avx2 does an 8-element dot product piecewise so we have to sum up 2 intermediate results
outputBuffer[i] = (float)Math.Round(dp[0] + dp[4]);
fraction += ratio;
inputBufferIndex += (int)MathF.Truncate(fraction);
fraction -= (int)fraction;
}
}
}
else
{
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
int baseIndex = (int)(fraction * 128) * 8; int baseIndex = (int)(fraction * 128) * 8;
@ -556,6 +576,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
fraction -= (int)fraction; fraction -= (int)fraction;
} }
} }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ResampleLowQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount) public static void ResampleLowQuality(Span<float> outputBuffer, ReadOnlySpan<short> inputBuffer, float ratio, ref float fraction, int sampleCount)

View file

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

View file

@ -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;

View file

@ -171,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();
} }
@ -193,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()
{ {
@ -345,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);
@ -411,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;
@ -788,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;

View file

@ -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"
} }

View file

@ -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);

View file

@ -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;
if (!OperatingSystem.IsMacOS())
{
shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]); 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,42 +498,24 @@ 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 |
UnixFileMode.UserRead |
UnixFileMode.GroupRead |
UnixFileMode.GroupWrite |
UnixFileMode.OtherRead |
UnixFileMode.OtherWrite;
if (!OperatingSystem.IsWindows() && File.Exists(path))
{
File.SetUnixFileMode(path, ExecutableFileMode);
}
}
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
{
// Extract Update
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
if (OperatingSystem.IsLinux())
{
using Stream inStream = File.OpenRead(updateFile);
using GZipInputStream gzipStream = new(inStream); using GZipInputStream gzipStream = new(inStream);
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
await Task.Run(() =>
{
TarEntry tarEntry; TarEntry tarEntry;
while ((tarEntry = tarStream.GetNextEntry()) != null)
{
if (tarEntry.IsDirectory) continue;
string outPath = Path.Combine(UpdateDir, tarEntry.Name); while ((tarEntry = tarStream.GetNextEntry()) is not null)
{
if (tarEntry.IsDirectory)
{
continue;
}
string outPath = Path.Combine(outputDirectoryPath, tarEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath)); Directory.CreateDirectory(Path.GetDirectoryName(outPath));
@ -516,33 +524,33 @@ namespace Ryujinx.Modules
tarStream.CopyEntryContents(outStream); tarStream.CopyEntryContents(outStream);
} }
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
TarEntry entry = tarEntry;
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal); if (tarEntry is null)
});
}
});
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
}
else
{ {
using Stream inStream = File.OpenRead(updateFile); 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); using ZipFile zipFile = new(inStream);
await Task.Run(() =>
{
double count = 0; double count = 0;
foreach (ZipEntry zipEntry in zipFile) foreach (ZipEntry zipEntry in zipFile)
{ {
count++; count++;
if (zipEntry.IsDirectory) continue; if (zipEntry.IsDirectory) continue;
string outPath = Path.Combine(UpdateDir, zipEntry.Name); string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath)); Directory.CreateDirectory(Path.GetDirectoryName(outPath));
@ -559,9 +567,30 @@ namespace Ryujinx.Modules
taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
}); });
} }
});
} }
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
{
// Extract Update
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
await Task.Run(() =>
{
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
ExtractTarGzipFile(taskDialog, updateFile, UpdateDir);
}
else if (OperatingSystem.IsWindows())
{
ExtractZipFile(taskDialog, updateFile, UpdateDir);
}
else
{
throw new NotSupportedException();
}
});
// Delete downloaded zip // Delete downloaded zip
File.Delete(updateFile); File.Delete(updateFile);
@ -570,6 +599,9 @@ 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);
// NOTE: On macOS, replacement is delayed to the restart phase.
if (!OperatingSystem.IsMacOS())
{
// Replace old files // Replace old files
await Task.Run(() => await Task.Run(() =>
{ {
@ -602,8 +634,7 @@ namespace Ryujinx.Modules
}); });
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.

View file

@ -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];

View file

@ -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
{ {

View file

@ -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);
}
}

View file

@ -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
} }
} }
} }

View file

@ -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

View file

@ -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())

View file

@ -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;

View file

@ -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;

View file

@ -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)
{ {

View file

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

View file

@ -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())

View file

@ -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;

View file

@ -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}"

View file

@ -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;

View file

@ -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];

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Common.Configuration
{
public enum AntiAliasing
{
None,
Fxaa,
SmaaLow,
SmaaMedium,
SmaaHigh,
SmaaUltra
}
}

View file

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

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Common.Configuration
{
public enum ScalingFilter
{
Bilinear,
Nearest,
Fsr
}
}

View file

@ -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();

View file

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

View file

@ -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>

View file

@ -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
{ {

View file

@ -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
{ {

View file

@ -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
{ {

View file

@ -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
{ {

View 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();
}
}
}
}

View 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;
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.GAL
{
public enum AntiAliasing
{
None,
Fxaa,
SmaaLow,
SmaaMedium,
SmaaHigh,
SmaaUltra
}
}

View file

@ -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;

View file

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

View file

@ -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);
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -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) { }
} }
} }

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.GAL
{
public enum ScalingFilter
{
Bilinear,
Nearest,
Fsr
}
}

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -474,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;

View file

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

View file

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

View file

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

View file

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

View file

@ -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.

View file

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

View file

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

View file

@ -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,

View file

@ -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>();

View file

@ -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;

View file

@ -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>

View file

@ -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;

View file

@ -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

View file

@ -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
{ {

View 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);
}
}
}

View file

@ -0,0 +1,81 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common;
using Ryujinx.Graphics.OpenGL.Image;
namespace Ryujinx.Graphics.OpenGL.Effects
{
internal class FxaaPostProcessingEffect : IPostProcessingEffect
{
private readonly OpenGLRenderer _renderer;
private int _resolutionUniform;
private int _inputUniform;
private int _outputUniform;
private int _shaderProgram;
private TextureStorage _textureStorage;
public FxaaPostProcessingEffect(OpenGLRenderer renderer)
{
Initialize();
_renderer = renderer;
}
public void Dispose()
{
if (_shaderProgram != 0)
{
GL.DeleteProgram(_shaderProgram);
_textureStorage?.Dispose();
}
}
private void Initialize()
{
_shaderProgram = ShaderHelper.CompileProgram(EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl"), ShaderType.ComputeShader);
_resolutionUniform = GL.GetUniformLocation(_shaderProgram, "invResolution");
_inputUniform = GL.GetUniformLocation(_shaderProgram, "inputTexture");
_outputUniform = GL.GetUniformLocation(_shaderProgram, "imgOutput");
}
public TextureView Run(TextureView view, int width, int height)
{
if (_textureStorage == null || _textureStorage.Info.Width != view.Width || _textureStorage.Info.Height != view.Height)
{
_textureStorage?.Dispose();
_textureStorage = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
_textureStorage.CreateDefaultView();
}
var textureView = _textureStorage.CreateView(view.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);
GL.UseProgram(_shaderProgram);
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
view.Bind(0);
GL.Uniform1(_inputUniform, 0);
GL.Uniform1(_outputUniform, 0);
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
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);
return textureView;
}
}
}

View file

@ -0,0 +1,11 @@
using Ryujinx.Graphics.OpenGL.Image;
using System;
namespace Ryujinx.Graphics.OpenGL.Effects
{
internal interface IPostProcessingEffect : IDisposable
{
const int LocalGroupSize = 64;
TextureView Run(TextureView view, int width, int height);
}
}

View file

@ -0,0 +1,18 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Image;
using System;
namespace Ryujinx.Graphics.OpenGL.Effects
{
internal interface IScalingFilter : IDisposable
{
float Level { get; set; }
void Run(
TextureView view,
TextureView destinationTexture,
int width,
int height,
Extents2D source,
Extents2D destination);
}
}

View file

@ -0,0 +1,40 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.OpenGL.Effects
{
internal static class ShaderHelper
{
public static int CompileProgram(string shaderCode, ShaderType shaderType)
{
var shader = GL.CreateShader(shaderType);
GL.ShaderSource(shader, shaderCode);
GL.CompileShader(shader);
var program = GL.CreateProgram();
GL.AttachShader(program, shader);
GL.LinkProgram(program);
GL.DetachShader(program, shader);
GL.DeleteShader(shader);
return program;
}
public static int CompileProgram(string[] shaders, ShaderType shaderType)
{
var shader = GL.CreateShader(shaderType);
GL.ShaderSource(shader, shaders.Length, shaders, (int[])null);
GL.CompileShader(shader);
var program = GL.CreateProgram();
GL.AttachShader(program, shader);
GL.LinkProgram(program);
GL.DetachShader(program, shader);
GL.DeleteShader(shader);
return program;
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
#version 430 core
precision mediump float;
layout (local_size_x = 64) in;
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
layout( location=1 ) uniform sampler2D Source;
layout( location=2 ) uniform float srcX0;
layout( location=3 ) uniform float srcX1;
layout( location=4 ) uniform float srcY0;
layout( location=5 ) uniform float srcY1;
layout( location=6 ) uniform float dstX0;
layout( location=7 ) uniform float dstX1;
layout( location=8 ) uniform float dstY0;
layout( location=9 ) uniform float dstY1;
layout( location=10 ) uniform float scaleX;
layout( location=11 ) uniform float scaleY;
#define A_GPU 1
#define A_GLSL 1
#include "ffx_a.h"
#define FSR_EASU_F 1
AU4 con0, con1, con2, con3;
float srcW, srcH, dstW, dstH;
vec2 bLeft, tRight;
AF2 translate(AF2 pos) {
return AF2(pos.x * scaleX, pos.y * scaleY);
}
void setBounds(vec2 bottomLeft, vec2 topRight) {
bLeft = bottomLeft;
tRight = topRight;
}
AF2 translateDest(AF2 pos) {
AF2 translatedPos = AF2(pos.x, pos.y);
translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1: translatedPos.y;
return translatedPos;
}
AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(Source, translate(p), 0); return res; }
AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(Source, translate(p), 1); return res; }
AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(Source, translate(p), 2); return res; }
#include "ffx_fsr1.h"
float insideBox(vec2 v) {
vec2 s = step(bLeft, v) - step(tRight, v);
return s.x * s.y;
}
void CurrFilter(AU2 pos)
{
if((insideBox(vec2(pos.x, pos.y))) == 0) {
imageStore(imgOutput, ASU2(pos.x, pos.y), AF4(0,0,0,1));
return;
}
AF3 c;
FsrEasuF(c, AU2(pos.x - bLeft.x, pos.y - bLeft.y), con0, con1, con2, con3);
imageStore(imgOutput, ASU2(translateDest(pos)), AF4(c, 1));
}
void main() {
srcW = abs(srcX1 - srcX0);
srcH = abs(srcY1 - srcY0);
dstW = abs(dstX1 - dstX0);
dstH = abs(dstY1 - dstY0);
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
setBounds(vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1),
vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0));
// Upscaling
FsrEasuCon(con0, con1, con2, con3,
srcW, srcH, // Viewport size (top left aligned) in the input image which is to be scaled.
srcW, srcH, // The size of the input image.
dstW, dstH); // The output resolution.
CurrFilter(gxy);
gxy.x += 8u;
CurrFilter(gxy);
gxy.y += 8u;
CurrFilter(gxy);
gxy.x -= 8u;
CurrFilter(gxy);
}

View file

@ -0,0 +1,37 @@
#version 430 core
precision mediump float;
layout (local_size_x = 64) in;
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
layout( location=1 ) uniform sampler2D source;
layout( location=2 ) uniform float sharpening;
#define A_GPU 1
#define A_GLSL 1
#include "ffx_a.h"
#define FSR_RCAS_F 1
AU4 con0;
AF4 FsrRcasLoadF(ASU2 p) { return AF4(texelFetch(source, p, 0)); }
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
#include "ffx_fsr1.h"
void CurrFilter(AU2 pos)
{
AF3 c;
FsrRcasF(c.r, c.g, c.b, pos, con0);
imageStore(imgOutput, ASU2(pos), AF4(c, 1));
}
void main() {
FsrRcasCon(con0, sharpening);
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
CurrFilter(gxy);
gxy.x += 8u;
CurrFilter(gxy);
gxy.y += 8u;
CurrFilter(gxy);
gxy.x -= 8u;
CurrFilter(gxy);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

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