From 153b8bfc7c8c8711f8c2ce40f88085355d870b6a Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 18 Aug 2023 02:25:54 -0300 Subject: [PATCH] Implement support for masked stencil clears on Vulkan (#5589) * Implement support for masked stencil clears on Vulkan * PR feedback --- .../FramebufferParams.cs | 10 ++ src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 108 ++++++++++++------ src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 6 +- src/Ryujinx.Graphics.Vulkan/PipelineFull.cs | 36 ++++++ .../Ryujinx.Graphics.Vulkan.csproj | 1 + ...DepthStencilClearFragmentShaderSource.frag | 8 ++ .../DepthStencilClearFragment.spv | Bin 0 -> 468 bytes 7 files changed, 131 insertions(+), 38 deletions(-) create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/DepthStencilClearFragmentShaderSource.frag create mode 100644 src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthStencilClearFragment.spv diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 749d5929cc..7600c2d5ea 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -148,6 +148,16 @@ namespace Ryujinx.Graphics.Vulkan return _attachments[index]; } + public Auto GetDepthStencilAttachment() + { + if (!HasDepthStencil) + { + return null; + } + + return _attachments[AttachmentsCount - 1]; + } + public ComponentType GetAttachmentComponentType(int index) { if (_colors != null && (uint)index < _colors.Length) diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index 684ed068a4..a6d2376443 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly IProgram _programColorClearF; private readonly IProgram _programColorClearSI; private readonly IProgram _programColorClearUI; + private readonly IProgram _programDepthStencilClear; private readonly IProgram _programStrideChange; private readonly IProgram _programConvertD32S8ToD24S8; private readonly IProgram _programConvertIndexBuffer; @@ -105,6 +106,12 @@ namespace Ryujinx.Graphics.Vulkan new ShaderSource(ReadSpirv("ColorClearUIFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), }, colorClearResourceLayout); + _programDepthStencilClear = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ReadSpirv("DepthStencilClearFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv), + }, colorClearResourceLayout); + var strideChangeResourceLayout = new ResourceLayoutBuilder() .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) @@ -446,10 +453,6 @@ namespace Ryujinx.Graphics.Vulkan 0f, 1f); - Span> scissors = stackalloc Rectangle[1]; - - scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - if (dstIsDepthOrStencil) { _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); @@ -470,7 +473,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, dstIsDepthOrStencil, dstFormat); _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }); - _pipeline.SetScissors(scissors); + _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dstWidth, dstHeight) }); if (clearAlpha) { @@ -547,12 +550,8 @@ namespace Ryujinx.Graphics.Vulkan 0f, 1f); - Span> scissors = stackalloc Rectangle[1]; - - scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat); - _pipeline.SetScissors(scissors); + _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dstWidth, dstHeight) }); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); @@ -639,7 +638,11 @@ namespace Ryujinx.Graphics.Vulkan } } - private static StencilTestDescriptor CreateStencilTestDescriptor(bool enabled) + private static StencilTestDescriptor CreateStencilTestDescriptor( + bool enabled, + int refValue = 0, + int compareMask = 0xff, + int writeMask = 0xff) { return new StencilTestDescriptor( enabled, @@ -647,16 +650,16 @@ namespace Ryujinx.Graphics.Vulkan StencilOp.Replace, StencilOp.Replace, StencilOp.Replace, - 0, - 0xff, - 0xff, + refValue, + compareMask, + writeMask, CompareOp.Always, StencilOp.Replace, StencilOp.Replace, StencilOp.Replace, - 0, - 0xff, - 0xff); + refValue, + compareMask, + writeMask); } public void Clear( @@ -695,10 +698,6 @@ namespace Ryujinx.Graphics.Vulkan 0f, 1f); - Span> scissors = stackalloc Rectangle[1]; - - scissors[0] = scissor; - IProgram program; if (type == ComponentType.SignedInteger) @@ -718,7 +717,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); _pipeline.SetRenderTargetColorMasks(new[] { componentMask }); _pipeline.SetViewports(viewports); - _pipeline.SetScissors(scissors); + _pipeline.SetScissors(stackalloc Rectangle[] { scissor }); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); _pipeline.Finish(); @@ -726,6 +725,56 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.Delete(bufferHandle); } + public void Clear( + VulkanRenderer gd, + Auto dst, + float depthValue, + bool depthMask, + int stencilValue, + int stencilMask, + int dstWidth, + int dstHeight, + VkFormat dstFormat, + Rectangle scissor) + { + const int ClearColorBufferSize = 16; + + gd.FlushAllCommands(); + + using var cbs = gd.CommandBufferPool.Rent(); + + _pipeline.SetCommandBuffer(cbs); + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize); + + gd.BufferManager.SetData(bufferHandle, 0, stackalloc float[] { depthValue }); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) }); + + Span viewports = stackalloc Viewport[1]; + + viewports[0] = new Viewport( + new Rectangle(0, 0, dstWidth, dstHeight), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + _pipeline.SetProgram(_programDepthStencilClear); + _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat); + _pipeline.SetViewports(viewports); + _pipeline.SetScissors(stackalloc Rectangle[] { scissor }); + _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); + _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask)); + _pipeline.Draw(4, 1, 0, 0); + _pipeline.Finish(); + + gd.BufferManager.Delete(bufferHandle); + } + public void DrawTexture( VulkanRenderer gd, PipelineBase pipeline, @@ -778,8 +827,6 @@ namespace Ryujinx.Graphics.Vulkan 0f, 1f); - Span> scissors = stackalloc Rectangle[1]; - pipeline.SetProgram(_programColorBlit); pipeline.SetViewports(viewports); pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); @@ -1119,11 +1166,7 @@ namespace Ryujinx.Graphics.Vulkan 0f, 1f); - Span> scissors = stackalloc Rectangle[1]; - - scissors[0] = new Rectangle(0, 0, dst.Width, dst.Height); - - _pipeline.SetScissors(scissors); + _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dst.Width, dst.Height) }); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); @@ -1251,12 +1294,8 @@ namespace Ryujinx.Graphics.Vulkan 0f, 1f); - Span> scissors = stackalloc Rectangle[1]; - - scissors[0] = new Rectangle(0, 0, dst.Width, dst.Height); - _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }); - _pipeline.SetScissors(scissors); + _pipeline.SetScissors(stackalloc Rectangle[] { new Rectangle(0, 0, dst.Width, dst.Height) }); _pipeline.SetViewports(viewports); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); @@ -1731,6 +1770,7 @@ namespace Ryujinx.Graphics.Vulkan _programColorClearF.Dispose(); _programColorClearSI.Dispose(); _programColorClearUI.Dispose(); + _programDepthStencilClear.Dispose(); _programStrideChange.Dispose(); _programConvertIndexBuffer.Dispose(); _programConvertIndirectData.Dispose(); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 67b16ec96f..54b67f35e2 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -243,10 +243,8 @@ namespace Ryujinx.Graphics.Vulkan Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } - public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) + public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, bool stencilMask) { - // TODO: Use stencilMask (fully). - if (FramebufferParams == null || !FramebufferParams.HasDepthStencil) { return; @@ -255,7 +253,7 @@ namespace Ryujinx.Graphics.Vulkan var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue)); var flags = depthMask ? ImageAspectFlags.DepthBit : 0; - if (stencilMask != 0) + if (stencilMask) { flags |= ImageAspectFlags.StencilBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs index dcc6c53003..c3e6f37c37 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -81,6 +81,42 @@ namespace Ryujinx.Graphics.Vulkan } } + public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) + { + if (FramebufferParams == null) + { + return; + } + + if (stencilMask != 0 && stencilMask != 0xff) + { + // We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits, + // because on Vulkan, the pipeline state does not affect clears. + var dstTexture = FramebufferParams.GetDepthStencilAttachment(); + if (dstTexture == null) + { + return; + } + + // TODO: Clear only the specified layer. + Gd.HelperShader.Clear( + Gd, + dstTexture, + depthValue, + depthMask, + stencilValue, + stencilMask, + (int)FramebufferParams.Width, + (int)FramebufferParams.Height, + FramebufferParams.AttachmentFormats[FramebufferParams.AttachmentsCount - 1], + ClearScissor); + } + else + { + ClearRenderTargetDepthStencil(layer, layerCount, depthValue, depthMask, stencilValue, stencilMask != 0); + } + } + public void EndHostConditionalRendering() { if (Gd.Capabilities.SupportsConditionalRendering) diff --git a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index 3439511861..8d30457e22 100644 --- a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -42,6 +42,7 @@ + diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/DepthStencilClearFragmentShaderSource.frag b/src/Ryujinx.Graphics.Vulkan/Shaders/DepthStencilClearFragmentShaderSource.frag new file mode 100644 index 0000000000..689a0fffb9 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/Shaders/DepthStencilClearFragmentShaderSource.frag @@ -0,0 +1,8 @@ +#version 450 core + +layout (location = 0) in vec4 clear_colour; + +void main() +{ + gl_FragDepth = clear_colour.x; +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthStencilClearFragment.spv b/src/Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries/DepthStencilClearFragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..dcd3235b6c9a8eda2441711583ea42ad04b924bf GIT binary patch literal 468 zcmYk1%}T>i5QVQvlUA$!L99E8xD<*DErKWrVmICS043B?g=i(#7xbyz2!7vf6>pf# z%(-XInVTjj%@DRiJG4SStWQ5AAOS3oc|3WT4BuCe!}E(X8J&zj~KC