Take componentMask and scissor into account when clearing framebuffer attachments

This commit is contained in:
gdk 2022-04-09 19:05:55 -03:00 committed by riperiperi
parent 746eded2cf
commit 80d72504d4
7 changed files with 183 additions and 25 deletions

View file

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

View file

@ -121,14 +121,29 @@ namespace Ryujinx.Graphics.Vulkan
AttachmentsCount = count;
}
public Auto<DisposableImageView> 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<int> 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<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)

View file

@ -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<int>(),
Array.Empty<int>());
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<int>(),
Array.Empty<int>(),
Array.Empty<int>(),
Array.Empty<int>(),
Array.Empty<int>(),
Array.Empty<int>());
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<int>(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<DisposableImageView> dst,
ReadOnlySpan<float> clearColor,
uint componentMask,
int dstWidth,
int dstHeight,
VkFormat dstFormat,
Rectangle<int> 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<float>(bufferHandle, 0, clearColor);
Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
bufferRanges[0] = new BufferRange(bufferHandle, 0, ClearColorBufferSize);
_pipeline.SetUniformBuffers(1, bufferRanges);
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
viewports[0] = new GAL.Viewport(
new Rectangle<float>(0, 0, dstWidth, dstHeight),
ViewportSwizzle.PositiveX,
ViewportSwizzle.PositiveY,
ViewportSwizzle.PositiveZ,
ViewportSwizzle.PositiveW,
0f,
1f);
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[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();

View file

@ -52,6 +52,7 @@ namespace Ryujinx.Graphics.Vulkan
private BufferState _indexBuffer;
private readonly BufferState[] _transformFeedbackBuffers;
private readonly BufferState[] _vertexBuffers;
protected Rectangle<int> 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<int>(0, 0, 0xffff, 0xffff);
var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
new Span<Vector4<float>>(_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<Rectangle<int>> 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.

View file

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

View file

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

View file

@ -299,7 +299,7 @@ namespace Ryujinx.Graphics.Vulkan
public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes;
public PipelineLayout PipelineLayout;
public unsafe void Initialize()
public void Initialize()
{
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
StageRequiredSubgroupSizes = new NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT>(Constants.MaxShaderStages);