diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 83afcaa3a..da04362db 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -60,6 +60,8 @@ namespace Ryujinx.Graphics.GAL void SetLogicOpState(bool enable, LogicalOp op); + void SetMultisampleState(MultisampleDescriptor multisample); + void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel); void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin); diff --git a/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs b/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs new file mode 100644 index 000000000..76e569874 --- /dev/null +++ b/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Graphics.GAL +{ + public struct MultisampleDescriptor + { + public bool AlphaToCoverageEnable { get; } + public bool AlphaToCoverageDitherEnable { get; } + public bool AlphaToOneEnable { get; } + + public MultisampleDescriptor( + bool alphaToCoverageEnable, + bool alphaToCoverageDitherEnable, + bool alphaToOneEnable) + { + AlphaToCoverageEnable = alphaToCoverageEnable; + AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable; + AlphaToOneEnable = alphaToOneEnable; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs index 442a90459..95b33bc68 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs @@ -179,6 +179,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading SetLineParametersCommand.Run(ref GetCommand(memory), threaded, renderer); _lookup[(int)CommandType.SetLogicOpState] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => SetLogicOpStateCommand.Run(ref GetCommand(memory), threaded, renderer); + _lookup[(int)CommandType.SetMultisampleState] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => + SetMultisampleStateCommand.Run(ref GetCommand(memory), threaded, renderer); _lookup[(int)CommandType.SetPatchParameters] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => SetPatchParametersCommand.Run(ref GetCommand(memory), threaded, renderer); _lookup[(int)CommandType.SetPointParameters] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) => diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs index 5c42abd12..8f0a00952 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs @@ -71,6 +71,7 @@ SetIndexBuffer, SetLineParameters, SetLogicOpState, + SetMultisampleState, SetPatchParameters, SetPointParameters, SetPolygonMode, diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs new file mode 100644 index 000000000..f981c6ceb --- /dev/null +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetMultisampleStateCommand.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Multithreading.Commands +{ + struct SetMultisampleStateCommand : IGALCommand + { + public CommandType CommandType => CommandType.SetMultisampleState; + private MultisampleDescriptor _multisample; + + public void Set(MultisampleDescriptor multisample) + { + _multisample = multisample; + } + + public static void Run(ref SetMultisampleStateCommand command, ThreadedRenderer threaded, IRenderer renderer) + { + renderer.Pipeline.SetMultisampleState(command._multisample); + } + } +} diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 2a1f474a9..aebf210d9 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -184,6 +184,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } + public void SetMultisampleState(MultisampleDescriptor multisample) + { + _renderer.New().Set(multisample); + _renderer.QueueCommand(); + } + public void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel) { _renderer.New().Set(vertices, defaultOuterLevel, defaultInnerLevel); diff --git a/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs b/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs index da7a24613..5d73b45aa 100644 --- a/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs +++ b/Ryujinx.Graphics.GAL/SupportBufferUpdater.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.GAL { _renderer = renderer; Handle = renderer.CreateBuffer(SupportBuffer.RequiredSize); + renderer.Pipeline.ClearBuffer(Handle, 0, SupportBuffer.RequiredSize, 0); } private void MarkDirty(int startOffset, int byteSize) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index c64c760ae..2f5d4fc5f 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -166,7 +166,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed nameof(ThreedClassState.BlendEnable), nameof(ThreedClassState.BlendState)), - new StateUpdateCallbackEntry(UpdateLogicOpState, nameof(ThreedClassState.LogicOpState)) + new StateUpdateCallbackEntry(UpdateLogicOpState, nameof(ThreedClassState.LogicOpState)), + + new StateUpdateCallbackEntry(UpdateMultisampleState, + nameof(ThreedClassState.AlphaToCoverageDitherEnable), + nameof(ThreedClassState.MultisampleControl)) }); } @@ -1092,6 +1096,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp); } + /// + /// Updates multisample state, based on guest state. + /// + private void UpdateMultisampleState() + { + bool alphaToCoverageEnable = (_state.State.MultisampleControl & 1) != 0; + bool alphaToOneEnable = (_state.State.MultisampleControl & 0x10) != 0; + + _context.Renderer.Pipeline.SetMultisampleState(new MultisampleDescriptor( + alphaToCoverageEnable, + _state.State.AlphaToCoverageDitherEnable, + alphaToOneEnable)); + } + /// /// Updates host shaders based on the guest GPU state. /// @@ -1231,7 +1249,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.EarlyZForce, _drawState.Topology, _state.State.TessMode, - _state.State.ViewportTransformEnable == 0); + _state.State.ViewportTransformEnable == 0, + (_state.State.MultisampleControl & 1) != 0, + _state.State.AlphaToCoverageDitherEnable); } /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs index 81a228315..2a8313562 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs @@ -767,7 +767,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public SamplerIndex SamplerIndex; public fixed uint Reserved1238[37]; public Boolean32 DepthTestEnable; - public fixed uint Reserved12D0[5]; + public fixed uint Reserved12D0[4]; + public Boolean32 AlphaToCoverageDitherEnable; public Boolean32 BlendIndependent; public Boolean32 DepthWriteEnable; public Boolean32 AlphaTestEnable; @@ -802,9 +803,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public Boolean32 PointSpriteEnable; public fixed uint Reserved1524[3]; public uint ResetCounter; - public uint Reserved1534; + public Boolean32 MultisampleEnable; public Boolean32 RtDepthStencilEnable; - public fixed uint Reserved153C[5]; + public uint MultisampleControl; + public fixed uint Reserved1540[4]; public GpuVa RenderEnableAddress; public Condition RenderEnableCondition; public PoolState SamplerPoolState; diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs index 4de6eff91..885bcd09e 100644 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs +++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Migration.cs @@ -167,6 +167,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce), topology, tessMode, + false, + false, false); TransformFeedbackDescriptor[] tfdNew = null; diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs index bc63f714d..e54764263 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs @@ -67,6 +67,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return MemoryMarshal.Cast(_data.Span.Slice((int)address)); } + /// + public bool QueryAlphaToCoverageDitherEnable() + { + return _oldSpecState.GraphicsState.AlphaToCoverageEnable && _oldSpecState.GraphicsState.AlphaToCoverageDitherEnable; + } + /// public int QueryBindingConstantBuffer(int index) { diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 59801001a..37b25793f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 1; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 3424; + private const uint CodeGenVersion = 3069; 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 5cd966af7..5317aab95 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -66,6 +66,12 @@ namespace Ryujinx.Graphics.Gpu.Shader return MemoryMarshal.Cast(_channel.MemoryManager.GetSpan(address, size)); } + /// + public bool QueryAlphaToCoverageDitherEnable() + { + return _state.GraphicsState.AlphaToCoverageEnable && _state.GraphicsState.AlphaToCoverageDitherEnable; + } + /// public int QueryBindingConstantBuffer(int index) { diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 92ec117f3..fae670ea5 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -30,6 +30,16 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public readonly bool ViewportTransformDisable; + /// + /// Indicates whenever alpha-to-coverage is enabled. + /// + public readonly bool AlphaToCoverageEnable; + + /// + /// Indicates whenever alpha-to-coverage dithering is enabled. + /// + public readonly bool AlphaToCoverageDitherEnable; + /// /// Creates a new GPU graphics state. /// @@ -37,12 +47,22 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Primitive topology /// Tessellation mode /// Indicates whenever the viewport transform is disabled - public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode, bool viewportTransformDisable) + /// Indicates whenever alpha-to-coverage is enabled + /// Indicates whenever alpha-to-coverage dithering is enabled + public GpuChannelGraphicsState( + bool earlyZForce, + PrimitiveTopology topology, + TessMode tessellationMode, + bool viewportTransformDisable, + bool alphaToCoverageEnable, + bool alphaToCoverageDitherEnable) { EarlyZForce = earlyZForce; Topology = topology; TessellationMode = tessellationMode; ViewportTransformDisable = viewportTransformDisable; + AlphaToCoverageEnable = alphaToCoverageEnable; + AlphaToCoverageDitherEnable = alphaToCoverageDitherEnable; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 587d60a77..7e39c8a35 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -455,6 +455,14 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } + bool thisA2cDitherEnable = GraphicsState.AlphaToCoverageEnable && GraphicsState.AlphaToCoverageDitherEnable; + bool otherA2cDitherEnable = graphicsState.AlphaToCoverageEnable && graphicsState.AlphaToCoverageDitherEnable; + + if (otherA2cDitherEnable != thisA2cDitherEnable) + { + return false; + } + return Matches(channel, poolState, checkTextures, isCompute: false); } diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index 773c9f634..13b5b412e 100644 --- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -5,19 +5,20 @@ namespace Ryujinx.Graphics.OpenGL { static class HwCapabilities { - private static readonly Lazy _supportsAstcCompression = new Lazy(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); - private static readonly Lazy _supportsDrawTexture = new Lazy(() => HasExtension("GL_NV_draw_texture")); - private static readonly Lazy _supportsFragmentShaderInterlock = new Lazy(() => HasExtension("GL_ARB_fragment_shader_interlock")); - private static readonly Lazy _supportsFragmentShaderOrdering = new Lazy(() => HasExtension("GL_INTEL_fragment_shader_ordering")); - private static readonly Lazy _supportsImageLoadFormatted = new Lazy(() => HasExtension("GL_EXT_shader_image_load_formatted")); - private static readonly Lazy _supportsIndirectParameters = new Lazy(() => HasExtension("GL_ARB_indirect_parameters")); - private static readonly Lazy _supportsParallelShaderCompile = new Lazy(() => HasExtension("GL_ARB_parallel_shader_compile")); - private static readonly Lazy _supportsPolygonOffsetClamp = new Lazy(() => HasExtension("GL_EXT_polygon_offset_clamp")); - private static readonly Lazy _supportsQuads = new Lazy(SupportsQuadsCheck); - private static readonly Lazy _supportsSeamlessCubemapPerTexture = new Lazy(() => HasExtension("GL_ARB_seamless_cubemap_per_texture")); - private static readonly Lazy _supportsShaderBallot = new Lazy(() => HasExtension("GL_ARB_shader_ballot")); - private static readonly Lazy _supportsTextureShadowLod = new Lazy(() => HasExtension("GL_EXT_texture_shadow_lod")); - private static readonly Lazy _supportsViewportSwizzle = new Lazy(() => HasExtension("GL_NV_viewport_swizzle")); + private static readonly Lazy _supportsAlphaToCoverageDitherControl = new Lazy(() => HasExtension("GL_NV_alpha_to_coverage_dither_control")); + private static readonly Lazy _supportsAstcCompression = new Lazy(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); + private static readonly Lazy _supportsDrawTexture = new Lazy(() => HasExtension("GL_NV_draw_texture")); + private static readonly Lazy _supportsFragmentShaderInterlock = new Lazy(() => HasExtension("GL_ARB_fragment_shader_interlock")); + private static readonly Lazy _supportsFragmentShaderOrdering = new Lazy(() => HasExtension("GL_INTEL_fragment_shader_ordering")); + private static readonly Lazy _supportsImageLoadFormatted = new Lazy(() => HasExtension("GL_EXT_shader_image_load_formatted")); + private static readonly Lazy _supportsIndirectParameters = new Lazy(() => HasExtension("GL_ARB_indirect_parameters")); + private static readonly Lazy _supportsParallelShaderCompile = new Lazy(() => HasExtension("GL_ARB_parallel_shader_compile")); + private static readonly Lazy _supportsPolygonOffsetClamp = new Lazy(() => HasExtension("GL_EXT_polygon_offset_clamp")); + private static readonly Lazy _supportsQuads = new Lazy(SupportsQuadsCheck); + private static readonly Lazy _supportsSeamlessCubemapPerTexture = new Lazy(() => HasExtension("GL_ARB_seamless_cubemap_per_texture")); + private static readonly Lazy _supportsShaderBallot = new Lazy(() => HasExtension("GL_ARB_shader_ballot")); + private static readonly Lazy _supportsTextureShadowLod = new Lazy(() => HasExtension("GL_EXT_texture_shadow_lod")); + private static readonly Lazy _supportsViewportSwizzle = new Lazy(() => HasExtension("GL_NV_viewport_swizzle")); private static readonly Lazy _maximumComputeSharedMemorySize = new Lazy(() => GetLimit(All.MaxComputeSharedMemorySize)); private static readonly Lazy _storageBufferOffsetAlignment = new Lazy(() => GetLimit(All.ShaderStorageBufferOffsetAlignment)); @@ -43,19 +44,20 @@ namespace Ryujinx.Graphics.OpenGL public static bool UsePersistentBufferForFlush => _gpuVendor.Value == GpuVendor.AmdWindows || _gpuVendor.Value == GpuVendor.Nvidia; - public static bool SupportsAstcCompression => _supportsAstcCompression.Value; - public static bool SupportsDrawTexture => _supportsDrawTexture.Value; - public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value; - public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value; - public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value; - public static bool SupportsIndirectParameters => _supportsIndirectParameters.Value; - public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value; - public static bool SupportsPolygonOffsetClamp => _supportsPolygonOffsetClamp.Value; - public static bool SupportsQuads => _supportsQuads.Value; - public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value; - public static bool SupportsShaderBallot => _supportsShaderBallot.Value; - public static bool SupportsTextureShadowLod => _supportsTextureShadowLod.Value; - public static bool SupportsViewportSwizzle => _supportsViewportSwizzle.Value; + public static bool SupportsAlphaToCoverageDitherControl => _supportsAlphaToCoverageDitherControl.Value; + public static bool SupportsAstcCompression => _supportsAstcCompression.Value; + public static bool SupportsDrawTexture => _supportsDrawTexture.Value; + public static bool SupportsFragmentShaderInterlock => _supportsFragmentShaderInterlock.Value; + public static bool SupportsFragmentShaderOrdering => _supportsFragmentShaderOrdering.Value; + public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value; + public static bool SupportsIndirectParameters => _supportsIndirectParameters.Value; + public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value; + public static bool SupportsPolygonOffsetClamp => _supportsPolygonOffsetClamp.Value; + public static bool SupportsQuads => _supportsQuads.Value; + public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value; + public static bool SupportsShaderBallot => _supportsShaderBallot.Value; + public static bool SupportsTextureShadowLod => _supportsTextureShadowLod.Value; + public static bool SupportsViewportSwizzle => _supportsViewportSwizzle.Value; public static bool SupportsMismatchingViewFormat => _gpuVendor.Value != GpuVendor.AmdWindows && _gpuVendor.Value != GpuVendor.IntelWindows; public static bool SupportsNonConstantTextureOffset => _gpuVendor.Value == GpuVendor.Nvidia; diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 62d4dee90..fde864240 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -918,6 +918,34 @@ namespace Ryujinx.Graphics.OpenGL } } + public void SetMultisampleState(MultisampleDescriptor multisample) + { + if (multisample.AlphaToCoverageEnable) + { + GL.Enable(EnableCap.SampleAlphaToCoverage); + + if (multisample.AlphaToOneEnable) + { + GL.Enable(EnableCap.SampleAlphaToOne); + } + else + { + GL.Disable(EnableCap.SampleAlphaToOne); + } + + if (HwCapabilities.SupportsAlphaToCoverageDitherControl) + { + GL.NV.AlphaToCoverageDitherControl(multisample.AlphaToCoverageDitherEnable + ? NvAlphaToCoverageDitherControl.AlphaToCoverageDitherEnableNv + : NvAlphaToCoverageDitherControl.AlphaToCoverageDitherDisableNv); + } + } + else + { + GL.Disable(EnableCap.SampleAlphaToCoverage); + } + } + public void SetLineParameters(float width, bool smooth) { if (smooth) diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 59a7ccdca..54578b798 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -615,7 +615,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements) { - bool needsSupportBlock = stage == ShaderStage.Fragment || + bool needsSupportBlock = stage == ShaderStage.Fragment || (context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()); if (!needsSupportBlock && scaleElements == 0) diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index 42f210a54..878c71803 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -34,6 +34,15 @@ namespace Ryujinx.Graphics.Shader /// Span of the memory location ReadOnlySpan GetCode(ulong address, int minimumSize); + /// + /// Queries whenever the alpha-to-coverage dithering feature is enabled. + /// + /// True if the feature is enabled, false otherwise + bool QueryAlphaToCoverageDitherEnable() + { + return false; + } + /// /// Queries the binding number of a constant buffer. /// diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index ba3b551d9..332b3e022 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -205,6 +205,8 @@ namespace Ryujinx.Graphics.Shader.Translation } else if (Config.Stage == ShaderStage.Fragment) { + GenerateAlphaToCoverageDitherDiscard(); + if (Config.OmapDepth) { Operand dest = Attribute(AttributeConsts.FragmentOutputDepth); @@ -266,6 +268,35 @@ namespace Ryujinx.Graphics.Shader.Translation } } + private void GenerateAlphaToCoverageDitherDiscard() + { + // If the feature is disabled, or alpha is not written, then we're done. + if (!Config.GpuAccessor.QueryAlphaToCoverageDitherEnable() || (Config.OmapTargets & 8) == 0) + { + return; + } + + // 11 11 11 10 10 10 10 00 + // 11 01 01 01 01 00 00 00 + Operand ditherMask = Const(unchecked((int)0xfbb99110u)); + + Operand x = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionX)), Const(1)); + Operand y = this.BitwiseAnd(this.FP32ConvertToU32(Attribute(AttributeConsts.PositionY)), Const(1)); + Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1))); + + Operand alpha = Register(3, RegisterType.Gpr); + Operand scaledAlpha = this.FPMultiply(this.FPSaturate(alpha), ConstF(8)); + Operand quantizedAlpha = this.IMinimumU32(this.FP32ConvertToU32(scaledAlpha), Const(7)); + Operand shift = this.BitwiseOr(this.ShiftLeft(quantizedAlpha, Const(2)), xy); + Operand opaque = this.BitwiseAnd(this.ShiftRightU32(ditherMask, shift), Const(1)); + + Operand a2cDitherEndLabel = Label(); + + this.BranchIfTrue(a2cDitherEndLabel, opaque); + this.Discard(); + this.MarkLabel(a2cDitherEndLabel); + } + public Operation[] GetOperations() { return _operations.ToArray();