diff --git a/Ryujinx.Graphics.GAL/BlendFactor.cs b/Ryujinx.Graphics.GAL/BlendFactor.cs index 135873e99..4149ad514 100644 --- a/Ryujinx.Graphics.GAL/BlendFactor.cs +++ b/Ryujinx.Graphics.GAL/BlendFactor.cs @@ -38,4 +38,25 @@ namespace Ryujinx.Graphics.GAL Src1AlphaGl = 0xc902, OneMinusSrc1AlphaGl = 0xc903 } + + public static class BlendFactorExtensions + { + public static bool IsDualSource(this BlendFactor factor) + { + switch (factor) + { + case BlendFactor.Src1Color: + case BlendFactor.Src1ColorGl: + case BlendFactor.Src1Alpha: + case BlendFactor.Src1AlphaGl: + case BlendFactor.OneMinusSrc1Color: + case BlendFactor.OneMinusSrc1ColorGl: + case BlendFactor.OneMinusSrc1Alpha: + case BlendFactor.OneMinusSrc1AlphaGl: + return true; + } + + return false; + } + } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs index 62df15e79..a8af54970 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs @@ -328,5 +328,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Signal(); } } + + /// + /// Sets the dual-source blend enabled state. + /// + /// True if blending is enabled and using dual-source blend + public void SetDualSourceBlendEnabled(bool enabled) + { + if (enabled != _graphics.DualSourceBlendEnable) + { + _graphics.DualSourceBlendEnable = enabled; + + Signal(); + } + } } } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index d7d197adb..00e09a310 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -1183,6 +1183,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed bool blendIndependent = _state.State.BlendIndependent; ColorF blendConstant = _state.State.BlendConstant; + bool dualSourceBlendEnabled = false; + if (blendIndependent) { for (int index = 0; index < Constants.TotalRenderTargets; index++) @@ -1200,6 +1202,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed FilterBlendFactor(blend.AlphaSrcFactor, index), FilterBlendFactor(blend.AlphaDstFactor, index)); + if (enable && + (blend.ColorSrcFactor.IsDualSource() || + blend.ColorDstFactor.IsDualSource() || + blend.AlphaSrcFactor.IsDualSource() || + blend.AlphaDstFactor.IsDualSource())) + { + dualSourceBlendEnabled = true; + } + _pipeline.BlendDescriptors[index] = descriptor; _context.Renderer.Pipeline.SetBlendState(index, descriptor); } @@ -1219,12 +1230,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed FilterBlendFactor(blend.AlphaSrcFactor, 0), FilterBlendFactor(blend.AlphaDstFactor, 0)); + if (enable && + (blend.ColorSrcFactor.IsDualSource() || + blend.ColorDstFactor.IsDualSource() || + blend.AlphaSrcFactor.IsDualSource() || + blend.AlphaDstFactor.IsDualSource())) + { + dualSourceBlendEnabled = true; + } + for (int index = 0; index < Constants.TotalRenderTargets; index++) { _pipeline.BlendDescriptors[index] = descriptor; _context.Renderer.Pipeline.SetBlendState(index, descriptor); } } + + _currentSpecState.SetDualSourceBlendEnabled(dualSourceBlendEnabled); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index 97173c964..17639ca17 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -141,6 +141,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; } + /// + public bool QueryDualSourceBlendEnable() + { + return _oldSpecState.GraphicsState.DualSourceBlendEnable; + } + /// public InputTopology QueryPrimitiveTopology() { diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index edc5a8a08..0b87cc910 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4368; + private const uint CodeGenVersion = 4404; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 05631a210..3e8167331 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -157,6 +157,12 @@ namespace Ryujinx.Graphics.Gpu.Shader return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; } + /// + public bool QueryDualSourceBlendEnable() + { + return _state.GraphicsState.DualSourceBlendEnable; + } + /// public InputTopology QueryPrimitiveTopology() { diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 70ac50170..5247a096f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -92,6 +92,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public Array8 FragmentOutputTypes; + /// + /// Indicates whether dual source blend is enabled. + /// + public bool DualSourceBlendEnable; + /// /// Creates a new GPU graphics state. /// @@ -111,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0 /// Indicates that any storage buffer use is unaligned /// Type of the fragment shader outputs + /// Type of the vertex attributes consumed by the shader public GpuChannelGraphicsState( bool earlyZForce, PrimitiveTopology topology, @@ -127,7 +133,8 @@ namespace Ryujinx.Graphics.Gpu.Shader ref Array32 attributeTypes, bool hasConstantBufferDrawParameters, bool hasUnalignedStorageBuffer, - ref Array8 fragmentOutputTypes) + ref Array8 fragmentOutputTypes, + bool dualSourceBlendEnable) { EarlyZForce = earlyZForce; Topology = topology; @@ -145,6 +152,7 @@ namespace Ryujinx.Graphics.Gpu.Shader HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; FragmentOutputTypes = fragmentOutputTypes; + DualSourceBlendEnable = dualSourceBlendEnable; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 856507cd7..b2c4fccdb 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -535,6 +535,11 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } + if (graphicsState.DualSourceBlendEnable != GraphicsState.DualSourceBlendEnable) + { + return false; + } + return Matches(channel, ref poolState, checkTextures, isCompute: false); } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 62996bd0a..6b6d0289c 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -833,31 +833,13 @@ namespace Ryujinx.Graphics.OpenGL (BlendingFactorSrc)blend.AlphaSrcFactor.Convert(), (BlendingFactorDest)blend.AlphaDstFactor.Convert()); - static bool IsDualSource(BlendFactor factor) - { - switch (factor) - { - case BlendFactor.Src1Color: - case BlendFactor.Src1ColorGl: - case BlendFactor.Src1Alpha: - case BlendFactor.Src1AlphaGl: - case BlendFactor.OneMinusSrc1Color: - case BlendFactor.OneMinusSrc1ColorGl: - case BlendFactor.OneMinusSrc1Alpha: - case BlendFactor.OneMinusSrc1AlphaGl: - return true; - } - - return false; - } - EnsureFramebuffer(); _framebuffer.SetDualSourceBlend( - IsDualSource(blend.ColorSrcFactor) || - IsDualSource(blend.ColorDstFactor) || - IsDualSource(blend.AlphaSrcFactor) || - IsDualSource(blend.AlphaDstFactor)); + blend.ColorSrcFactor.IsDualSource() || + blend.ColorDstFactor.IsDualSource() || + blend.AlphaSrcFactor.IsDualSource() || + blend.AlphaDstFactor.IsDualSource()); if (_blendConstant != blend.BlendConstant) { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 996d312b7..9032ca59e 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -612,6 +612,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl else { int usedAttributes = context.Config.UsedOutputAttributes; + + if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable()) + { + int firstOutput = BitOperations.TrailingZeroCount(usedAttributes); + int mask = 3 << firstOutput; + + if ((usedAttributes & mask) == mask) + { + usedAttributes &= ~mask; + DeclareOutputDualSourceBlendAttribute(context, firstOutput); + } + } + while (usedAttributes != 0) { int index = BitOperations.TrailingZeroCount(usedAttributes); @@ -690,6 +703,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } } + private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int attr) + { + string name = $"{DefaultNames.OAttributePrefix}{attr}"; + string name2 = $"{DefaultNames.OAttributePrefix}{(attr + 1)}"; + + context.AppendLine($"layout (location = {attr}, index = 0) out vec4 {name};"); + context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};"); + } + private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet attrs) { foreach (int attr in attrs.Order()) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs index 5108d8619..df42a13cb 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Numerics; using static Spv.Specification; namespace Ryujinx.Graphics.Shader.CodeGen.Spirv @@ -622,7 +623,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) { int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16; - context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + + if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable()) + { + int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes); + int index = location - firstLocation; + int mask = 3 << firstLocation; + + if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask) + { + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation); + context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index); + } + else + { + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + } + } + else + { + context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location); + } } if (!isOutAttr) diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index f364437c7..ba5f2a92f 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -205,6 +205,15 @@ namespace Ryujinx.Graphics.Shader return false; } + /// + /// Queries dual source blend state. + /// + /// True if blending is enabled with a dual source blend equation, false otherwise + bool QueryDualSourceBlendEnable() + { + return false; + } + /// /// Queries host about the presence of the FrontFacing built-in variable bug. ///