Implement safe depth-stencil blit using stencil export extension (#4356)

* Implement safe depth-stencil blit using stencil export extension

* Delete depth-stencil blit with buffer path
This commit is contained in:
gdkchan 2023-02-06 00:19:31 -03:00 committed by GitHub
parent 43081c16c4
commit 7528f94536
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 330 additions and 213 deletions

View file

@ -33,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programConvertIndirectData;
private readonly IProgram _programColorCopyToNonMs;
private readonly IProgram _programColorDrawToMs;
private readonly IProgram _programDepthBlit;
private readonly IProgram _programStencilBlit;
public HelperShader(VulkanRenderer gd, Device device)
{
@ -42,13 +44,13 @@ namespace Ryujinx.Graphics.Vulkan
_samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
_samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
var colorBlitVertexBindings = new ShaderBindings(
var blitVertexBindings = new ShaderBindings(
new[] { 1 },
Array.Empty<int>(),
Array.Empty<int>(),
Array.Empty<int>());
var colorBlitFragmentBindings = new ShaderBindings(
var blitFragmentBindings = new ShaderBindings(
Array.Empty<int>(),
Array.Empty<int>(),
new[] { 0 },
@ -56,14 +58,14 @@ namespace Ryujinx.Graphics.Vulkan
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, colorBlitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, colorBlitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
var colorClearFragmentBindings = new ShaderBindings(
@ -74,19 +76,19 @@ namespace Ryujinx.Graphics.Vulkan
_programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
@ -151,6 +153,21 @@ namespace Ryujinx.Graphics.Vulkan
{
new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv),
});
_programDepthBlit = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
if (gd.Capabilities.SupportsShaderStencilExport)
{
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
}
}
public void Blit(
@ -162,6 +179,7 @@ namespace Ryujinx.Graphics.Vulkan
VkFormat dstFormat,
Extents2D srcRegion,
Extents2D dstRegion,
bool isDepthOrStencil,
bool linearFilter,
bool clearAlpha = false)
{
@ -169,10 +187,17 @@ namespace Ryujinx.Graphics.Vulkan
using var cbs = gd.CommandBufferPool.Rent();
Blit(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha);
if (isDepthOrStencil)
{
BlitDepthStencil(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion);
}
else
{
BlitColor(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha);
}
}
public void Blit(
public void BlitColor(
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
@ -255,6 +280,173 @@ namespace Ryujinx.Graphics.Vulkan
gd.BufferManager.Delete(bufferHandle);
}
private void BlitDepthStencil(
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
Auto<DisposableImageView> dst,
int dstWidth,
int dstHeight,
VkFormat dstFormat,
Extents2D srcRegion,
Extents2D dstRegion)
{
_pipeline.SetCommandBuffer(cbs);
const int RegionBufferSize = 16;
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
region[0] = (float)srcRegion.X1 / src.Width;
region[1] = (float)srcRegion.X2 / src.Width;
region[2] = (float)srcRegion.Y1 / src.Height;
region[3] = (float)srcRegion.Y2 / src.Height;
if (dstRegion.X1 > dstRegion.X2)
{
(region[0], region[1]) = (region[1], region[0]);
}
if (dstRegion.Y1 > dstRegion.Y2)
{
(region[2], region[3]) = (region[3], region[2]);
}
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) });
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
var rect = new Rectangle<float>(
MathF.Min(dstRegion.X1, dstRegion.X2),
MathF.Min(dstRegion.Y1, dstRegion.Y2),
MathF.Abs(dstRegion.X2 - dstRegion.X1),
MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
viewports[0] = new GAL.Viewport(
rect,
ViewportSwizzle.PositiveX,
ViewportSwizzle.PositiveY,
ViewportSwizzle.PositiveZ,
ViewportSwizzle.PositiveW,
0f,
1f);
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat);
_pipeline.SetScissors(scissors);
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
var aspectFlags = src.Info.Format.ConvertAspectFlags();
if (aspectFlags.HasFlag(ImageAspectFlags.DepthBit))
{
var depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth);
BlitDepthStencilDraw(depthTexture, isDepth: true);
if (depthTexture != src)
{
depthTexture.Release();
}
}
if (aspectFlags.HasFlag(ImageAspectFlags.StencilBit) && _programStencilBlit != null)
{
var stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil);
BlitDepthStencilDraw(stencilTexture, isDepth: false);
if (stencilTexture != src)
{
stencilTexture.Release();
}
}
_pipeline.Finish(gd, cbs);
gd.BufferManager.Delete(bufferHandle);
}
private static TextureView CreateDepthOrStencilView(TextureView depthStencilTexture, DepthStencilMode depthStencilMode)
{
if (depthStencilTexture.Info.DepthStencilMode == depthStencilMode)
{
return depthStencilTexture;
}
return (TextureView)depthStencilTexture.CreateView(new TextureCreateInfo(
depthStencilTexture.Info.Width,
depthStencilTexture.Info.Height,
depthStencilTexture.Info.Depth,
depthStencilTexture.Info.Levels,
depthStencilTexture.Info.Samples,
depthStencilTexture.Info.BlockWidth,
depthStencilTexture.Info.BlockHeight,
depthStencilTexture.Info.BytesPerPixel,
depthStencilTexture.Info.Format,
depthStencilMode,
depthStencilTexture.Info.Target,
SwizzleComponent.Red,
SwizzleComponent.Green,
SwizzleComponent.Blue,
SwizzleComponent.Alpha), 0, 0);
}
private void BlitDepthStencilDraw(TextureView src, bool isDepth)
{
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest);
if (isDepth)
{
_pipeline.SetProgram(_programDepthBlit);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always));
}
else
{
_pipeline.SetProgram(_programStencilBlit);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
}
_pipeline.Draw(4, 1, 0, 0);
if (isDepth)
{
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always));
}
else
{
_pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
}
}
private static StencilTestDescriptor CreateStencilTestDescriptor(bool enabled)
{
return new StencilTestDescriptor(
enabled,
GAL.CompareOp.Always,
GAL.StencilOp.Replace,
GAL.StencilOp.Replace,
GAL.StencilOp.Replace,
0,
0xff,
0xff,
GAL.CompareOp.Always,
GAL.StencilOp.Replace,
GAL.StencilOp.Replace,
GAL.StencilOp.Replace,
0,
0xff,
0xff);
}
public void Clear(
VulkanRenderer gd,
Auto<DisposableImageView> dst,
@ -993,6 +1185,8 @@ namespace Ryujinx.Graphics.Vulkan
_programConvertIndirectData.Dispose();
_programColorCopyToNonMs.Dispose();
_programColorDrawToMs.Dispose();
_programDepthBlit.Dispose();
_programStencilBlit?.Dispose();
_samplerNearest.Dispose();
_samplerLinear.Dispose();
_pipeline.Dispose();