From 80d72504d4418e38990ee7034ad0db62d04cd169 Mon Sep 17 00:00:00 2001 From: gdk Date: Sat, 9 Apr 2022 19:05:55 -0300 Subject: [PATCH] Take componentMask and scissor into account when clearing framebuffer attachments --- .../Engine/Threed/StateUpdater.cs | 2 +- Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 19 ++- Ryujinx.Graphics.Vulkan/HelperShader.cs | 126 ++++++++++++++++-- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 16 ++- Ryujinx.Graphics.Vulkan/PipelineFull.cs | 39 ++++++ ...ipelineBlit.cs => PipelineHelperShader.cs} | 4 +- Ryujinx.Graphics.Vulkan/PipelineState.cs | 2 +- 7 files changed, 183 insertions(+), 25 deletions(-) rename Ryujinx.Graphics.Vulkan/{PipelineBlit.cs => PipelineHelperShader.cs} (96%) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 2560f57c1..c8deb84cc 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { public const int ShaderStateIndex = 16; public const int RasterizerStateIndex = 15; - public const int ScissorStateIndex = 17; + public const int ScissorStateIndex = 18; public const int VertexBufferStateIndex = 0; public const int PrimitiveRestartStateIndex = 12; diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 59e161a9c..5d9418491 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -121,14 +121,29 @@ namespace Ryujinx.Graphics.Vulkan AttachmentsCount = count; } + public Auto GetAttachment(int index) + { + if ((uint)index >= _attachments.Length) + { + return null; + } + + return _attachments[index]; + } + private static bool IsValidTextureView(ITexture texture) { return texture is TextureView view && view.Valid; } - public ClearRect GetClearRect() + public ClearRect GetClearRect(Rectangle scissor) { - return new ClearRect(new Rect2D(null, new Extent2D(Width, Height)), 0, Layers); + int x = scissor.X; + int y = scissor.Y; + int width = Math.Min((int)Width - scissor.X, scissor.Width); + int height = Math.Min((int)Height - scissor.Y, scissor.Height); + + return new ClearRect(new Rect2D(new Offset2D(x, y), new Extent2D((uint)width, (uint)height)), 0, Layers); } public unsafe Auto Create(Vk api, CommandBufferScoped cbs, Auto renderPass) diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index f7f0e77ba..1df69d34c 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Vulkan { class HelperShader : IDisposable { - private const string VertexShaderSource = @"#version 450 core + private const string ColorBlitVertexShaderSource = @"#version 450 core layout (std140, binding = 1) uniform tex_coord_in { @@ -41,7 +41,7 @@ void main() colour = texture(tex, tex_coord); }"; - private const string ClearAlphaFragmentShaderSource = @"#version 450 core + private const string ColorBlitClearAlphaFragmentShaderSource = @"#version 450 core layout (binding = 32, set = 2) uniform sampler2D tex; @@ -53,15 +53,47 @@ void main() colour = vec4(texture(tex, tex_coord).rgb, 1.0f); }"; - private readonly PipelineBlit _pipeline; + private const string ColorClearVertexShaderSource = @"#version 450 core + +layout (std140, binding = 1) uniform clear_colour_in +{ + vec4 clear_colour_in_data; +}; + +layout (location = 0) out vec4 clear_colour; + +void main() +{ + int low = gl_VertexIndex & 1; + int high = gl_VertexIndex >> 1; + clear_colour = clear_colour_in_data; + gl_Position.x = (float(low) - 0.5f) * 2.0f; + gl_Position.y = (float(high) - 0.5f) * 2.0f; + gl_Position.z = 0.0f; + gl_Position.w = 1.0f; +}"; + + private const string ColorClearFragmentShaderSource = @"#version 450 core + +layout (location = 0) in vec4 clear_colour; +layout (location = 0) out vec4 colour; + +void main() +{ + colour = clear_colour; +}"; + + + private readonly PipelineHelperShader _pipeline; private readonly ISampler _samplerLinear; private readonly ISampler _samplerNearest; private readonly IProgram _programColorBlit; - private readonly IProgram _programClearAlpha; + private readonly IProgram _programColorBlitClearAlpha; + private readonly IProgram _programColorClear; public HelperShader(VulkanGraphicsDevice gd, Device device) { - _pipeline = new PipelineBlit(gd, device); + _pipeline = new PipelineHelperShader(gd, device); static GAL.SamplerCreateInfo GetSamplerCreateInfo(MinFilter minFilter, MagFilter magFilter) { @@ -100,12 +132,25 @@ void main() Array.Empty(), Array.Empty()); - var vertexShader = gd.CompileShader(ShaderStage.Vertex, vertexBindings, VertexShaderSource); - var fragmentShaderColorBlit = gd.CompileShader(ShaderStage.Fragment, fragmentBindings, ColorBlitFragmentShaderSource); - var fragmentShaderClearAlpha = gd.CompileShader(ShaderStage.Fragment, fragmentBindings, ClearAlphaFragmentShaderSource); + var colorBlitVertexShader = gd.CompileShader(ShaderStage.Vertex, vertexBindings, ColorBlitVertexShaderSource); + var colorBlitFragmentShader = gd.CompileShader(ShaderStage.Fragment, fragmentBindings, ColorBlitFragmentShaderSource); + var colorBlitClearAlphaFragmentShader = gd.CompileShader(ShaderStage.Fragment, fragmentBindings, ColorBlitClearAlphaFragmentShaderSource); - _programColorBlit = gd.CreateProgram(new[] { vertexShader, fragmentShaderColorBlit }, new ShaderInfo(-1)); - _programClearAlpha = gd.CreateProgram(new[] { vertexShader, fragmentShaderClearAlpha }, new ShaderInfo(-1)); + _programColorBlit = gd.CreateProgram(new[] { colorBlitVertexShader, colorBlitFragmentShader }, new ShaderInfo(-1)); + _programColorBlitClearAlpha = gd.CreateProgram(new[] { colorBlitVertexShader, colorBlitClearAlphaFragmentShader }, new ShaderInfo(-1)); + + var fragmentBindings2 = new ShaderBindings( + Array.Empty(), + Array.Empty(), + Array.Empty(), + Array.Empty(), + Array.Empty(), + Array.Empty()); + + var colorClearVertexShader = gd.CompileShader(ShaderStage.Vertex, vertexBindings, ColorClearVertexShaderSource); + var colorClearFragmentShader = gd.CompileShader(ShaderStage.Fragment, fragmentBindings2, ColorClearFragmentShaderSource); + + _programColorClear = gd.CreateProgram(new[] { colorClearVertexShader, colorClearFragmentShader }, new ShaderInfo(-1)); } public void Blit( @@ -200,15 +245,70 @@ void main() scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - _pipeline.SetProgram(clearAlpha ? _programClearAlpha : _programColorBlit); + _pipeline.SetProgram(clearAlpha ? _programColorBlitClearAlpha : _programColorBlit); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }); + _pipeline.SetScissors(scissors); if (clearAlpha) { - _pipeline.ClearRenderTargetColor(0, 0xf, new ColorF(0f, 0f, 0f, 1f)); + _pipeline.ClearRenderTargetColor(0, new ColorF(0f, 0f, 0f, 1f)); } + _pipeline.SetViewports(0, viewports); + _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + _pipeline.Draw(4, 1, 0, 0); + _pipeline.Finish(); + + gd.BufferManager.Delete(bufferHandle); + } + + public void Clear( + VulkanGraphicsDevice gd, + Auto dst, + ReadOnlySpan clearColor, + uint componentMask, + int dstWidth, + int dstHeight, + VkFormat dstFormat, + Rectangle scissor) + { + gd.FlushAllCommands(); + + using var cbs = gd.CommandBufferPool.Rent(); + + _pipeline.SetCommandBuffer(cbs); + + const int ClearColorBufferSize = 16; + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize, false); + + gd.BufferManager.SetData(bufferHandle, 0, clearColor); + + Span bufferRanges = stackalloc BufferRange[1]; + + bufferRanges[0] = new BufferRange(bufferHandle, 0, ClearColorBufferSize); + + _pipeline.SetUniformBuffers(1, bufferRanges); + + Span viewports = stackalloc GAL.Viewport[1]; + + viewports[0] = new GAL.Viewport( + new Rectangle(0, 0, dstWidth, dstHeight), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + Span> scissors = stackalloc Rectangle[1]; + + scissors[0] = scissor; + + _pipeline.SetProgram(_programColorClear); + _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); + _pipeline.SetRenderTargetColorMasks(new uint[] { componentMask }); _pipeline.SetViewports(0, viewports); _pipeline.SetScissors(scissors); _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); @@ -335,7 +435,7 @@ void main() { if (disposing) { - _programClearAlpha.Dispose(); + _programColorBlitClearAlpha.Dispose(); _programColorBlit.Dispose(); _samplerNearest.Dispose(); _samplerLinear.Dispose(); diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index a3b2611cd..f2d1644b0 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -52,6 +52,7 @@ namespace Ryujinx.Graphics.Vulkan private BufferState _indexBuffer; private readonly BufferState[] _transformFeedbackBuffers; private readonly BufferState[] _vertexBuffers; + protected Rectangle ClearScissor; public SupportBufferUpdater SupportBufferUpdater; @@ -88,6 +89,8 @@ namespace Ryujinx.Graphics.Vulkan _vertexBuffers[0] = new BufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0UL); _needsVertexBuffersRebind = true; + ClearScissor = new Rectangle(0, 0, 0xffff, 0xffff); + var defaultScale = new Vector4 { X = 1f, Y = 0f, Z = 0f, W = 0f }; new Span>(_renderScale).Fill(defaultScale); @@ -140,10 +143,8 @@ namespace Ryujinx.Graphics.Vulkan Gd.Api.CmdFillBuffer(CommandBuffer, dst, (ulong)offset, (ulong)size, value); } - public unsafe void ClearRenderTargetColor(int index, uint componentMask, ColorF color) + public unsafe void ClearRenderTargetColor(int index, ColorF color) { - // TODO: Use componentMask - if (_framebuffer == null) { return; @@ -158,7 +159,7 @@ namespace Ryujinx.Graphics.Vulkan var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha)); var attachment = new ClearAttachment(ImageAspectFlags.ImageAspectColorBit, (uint)index, clearValue); - var clearRect = FramebufferParams?.GetClearRect() ?? default; + var clearRect = FramebufferParams?.GetClearRect(ClearScissor) ?? default; Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } @@ -188,7 +189,7 @@ namespace Ryujinx.Graphics.Vulkan } var attachment = new ClearAttachment(flags, 0, clearValue); - var clearRect = FramebufferParams?.GetClearRect() ?? default; + var clearRect = FramebufferParams?.GetClearRect(ClearScissor) ?? default; Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } @@ -618,6 +619,10 @@ namespace Ryujinx.Graphics.Vulkan public void SetScissors(ReadOnlySpan> regions) { int count = Math.Min(Constants.MaxViewports, regions.Length); + if (count > 0) + { + ClearScissor = regions[0]; + } for (int i = 0; i < count; i++) { @@ -1047,7 +1052,6 @@ namespace Ryujinx.Graphics.Vulkan private void RecreatePipelineIfNeeded(PipelineBindPoint pbp) { - // Take the opportunity to process any pending work requested by other threads. _dynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); // Commit changes to the support buffer before drawing. diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index e398a1fd2..3bd467bec 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -183,6 +183,45 @@ namespace Ryujinx.Graphics.Vulkan return layouts; } + public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) + { + if (FramebufferParams == null) + { + return; + } + + if (componentMask != 0xf) + { + // We can't use CmdClearAttachments if the clear has a custom scissor or is not writing all components, + // because on Vulkan, the pipeline state does not affect clears. + var dstTexture = FramebufferParams.GetAttachment(index); + if (dstTexture == null) + { + return; + } + + Span clearColor = stackalloc float[4]; + clearColor[0] = color.Red; + clearColor[1] = color.Green; + clearColor[2] = color.Blue; + clearColor[3] = color.Alpha; + + Gd.HelperShader.Clear( + Gd, + dstTexture, + clearColor, + componentMask, + (int)FramebufferParams.Width, + (int)FramebufferParams.Height, + FramebufferParams.AttachmentFormats[index], + ClearScissor); + } + else + { + ClearRenderTargetColor(index, color); + } + } + public void EndHostConditionalRendering() { if (Gd.Capabilities.SupportsConditionalRendering) diff --git a/Ryujinx.Graphics.Vulkan/PipelineBlit.cs b/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs similarity index 96% rename from Ryujinx.Graphics.Vulkan/PipelineBlit.cs rename to Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs index adf921118..f4d529643 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBlit.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineHelperShader.cs @@ -3,9 +3,9 @@ using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan { - class PipelineBlit : PipelineBase + class PipelineHelperShader : PipelineBase { - public PipelineBlit(VulkanGraphicsDevice gd, Device device) : base(gd, device) + public PipelineHelperShader(VulkanGraphicsDevice gd, Device device) : base(gd, device) { } diff --git a/Ryujinx.Graphics.Vulkan/PipelineState.cs b/Ryujinx.Graphics.Vulkan/PipelineState.cs index ab00052b6..d9c2e8c99 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -299,7 +299,7 @@ namespace Ryujinx.Graphics.Vulkan public NativeArray StageRequiredSubgroupSizes; public PipelineLayout PipelineLayout; - public unsafe void Initialize() + public void Initialize() { Stages = new NativeArray(Constants.MaxShaderStages); StageRequiredSubgroupSizes = new NativeArray(Constants.MaxShaderStages);