Support safe blit on non-2D textures (#4374)

* Support safe blit on non-2D textures (except multisample)

* Change safe blit with different levels and layers to match CmdBlitImage path

* Remove now unused variables

* Multisample safe blit support
This commit is contained in:
gdkchan 2023-02-07 13:55:59 -03:00 committed by GitHub
parent cb250162cb
commit f8beeeb7d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 436 additions and 74 deletions

View file

@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly ISampler _samplerLinear;
private readonly ISampler _samplerNearest;
private readonly IProgram _programColorBlit;
private readonly IProgram _programColorBlitMs;
private readonly IProgram _programColorBlitClearAlpha;
private readonly IProgram _programColorClearF;
private readonly IProgram _programColorClearSI;
@ -34,7 +35,9 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programColorCopyToNonMs;
private readonly IProgram _programColorDrawToMs;
private readonly IProgram _programDepthBlit;
private readonly IProgram _programDepthBlitMs;
private readonly IProgram _programStencilBlit;
private readonly IProgram _programStencilBlitMs;
public HelperShader(VulkanRenderer gd, Device device)
{
@ -62,6 +65,12 @@ namespace Ryujinx.Graphics.Vulkan
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
@ -160,6 +169,12 @@ namespace Ryujinx.Graphics.Vulkan
new ShaderSource(ShaderBinaries.DepthBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
if (gd.Capabilities.SupportsShaderStencilExport)
{
_programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
@ -167,18 +182,23 @@ namespace Ryujinx.Graphics.Vulkan
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.StencilBlitFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
}
}
public void Blit(
VulkanRenderer gd,
TextureView src,
Auto<DisposableImageView> dst,
int dstWidth,
int dstHeight,
VkFormat dstFormat,
TextureView dst,
Extents2D srcRegion,
Extents2D dstRegion,
int layers,
int levels,
bool isDepthOrStencil,
bool linearFilter,
bool clearAlpha = false)
@ -187,13 +207,137 @@ namespace Ryujinx.Graphics.Vulkan
using var cbs = gd.CommandBufferPool.Rent();
if (isDepthOrStencil)
var dstFormat = dst.VkFormat;
var dstSamples = dst.Info.Samples;
for (int l = 0; l < levels; l++)
{
BlitDepthStencil(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion);
int srcWidth = Math.Max(1, src.Width >> l);
int srcHeight = Math.Max(1, src.Height >> l);
int dstWidth = Math.Max(1, dst.Width >> l);
int dstHeight = Math.Max(1, dst.Height >> l);
var mipSrcRegion = new Extents2D(
srcRegion.X1 >> l,
srcRegion.Y1 >> l,
srcRegion.X2 >> l,
srcRegion.Y2 >> l);
var mipDstRegion = new Extents2D(
dstRegion.X1 >> l,
dstRegion.Y1 >> l,
dstRegion.X2 >> l,
dstRegion.Y2 >> l);
for (int z = 0; z < layers; z++)
{
var srcView = Create2DLayerView(src, z, l);
var dstView = Create2DLayerView(dst, z, l);
if (isDepthOrStencil)
{
BlitDepthStencil(
gd,
cbs,
srcView,
dst.GetImageViewForAttachment(),
dstWidth,
dstHeight,
dstSamples,
dstFormat,
mipSrcRegion,
mipDstRegion);
}
else
{
BlitColor(
gd,
cbs,
srcView,
dst.GetImageViewForAttachment(),
dstWidth,
dstHeight,
dstSamples,
dstFormat,
false,
mipSrcRegion,
mipDstRegion,
linearFilter,
clearAlpha);
}
if (srcView != src)
{
srcView.Release();
}
if (dstView != dst)
{
dstView.Release();
}
}
}
else
}
public void CopyColor(
VulkanRenderer gd,
CommandBufferScoped cbs,
TextureView src,
TextureView dst,
int srcLayer,
int dstLayer,
int srcLevel,
int dstLevel,
int depth,
int levels)
{
for (int l = 0; l < levels; l++)
{
BlitColor(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha);
int mipSrcLevel = srcLevel + l;
int mipDstLevel = dstLevel + l;
int srcWidth = Math.Max(1, src.Width >> mipSrcLevel);
int srcHeight = Math.Max(1, src.Height >> mipSrcLevel);
int dstWidth = Math.Max(1, dst.Width >> mipDstLevel);
int dstHeight = Math.Max(1, dst.Height >> mipDstLevel);
var extents = new Extents2D(
0,
0,
Math.Min(srcWidth, dstWidth),
Math.Min(srcHeight, dstHeight));
for (int z = 0; z < depth; z++)
{
var srcView = Create2DLayerView(src, srcLayer + z, mipSrcLevel);
var dstView = Create2DLayerView(dst, dstLayer + z, mipDstLevel);
BlitColor(
gd,
cbs,
srcView,
dstView.GetImageViewForAttachment(),
dstView.Width,
dstView.Height,
dstView.Info.Samples,
dstView.VkFormat,
dstView.Info.Format.IsDepthOrStencil(),
extents,
extents,
false);
if (srcView != src)
{
srcView.Release();
}
if (dstView != dst)
{
dstView.Release();
}
}
}
}
@ -204,7 +348,9 @@ namespace Ryujinx.Graphics.Vulkan
Auto<DisposableImageView> dst,
int dstWidth,
int dstHeight,
int dstSamples,
VkFormat dstFormat,
bool dstIsDepthOrStencil,
Extents2D srcRegion,
Extents2D dstRegion,
bool linearFilter,
@ -262,8 +408,25 @@ namespace Ryujinx.Graphics.Vulkan
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
_pipeline.SetProgram(clearAlpha ? _programColorBlitClearAlpha : _programColorBlit);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
if (dstIsDepthOrStencil)
{
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always));
}
else if (src.Info.Target.IsMultisample())
{
_pipeline.SetProgram(_programColorBlitMs);
}
else if (clearAlpha)
{
_pipeline.SetProgram(_programColorBlitClearAlpha);
}
else
{
_pipeline.SetProgram(_programColorBlit);
}
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, dstIsDepthOrStencil, dstFormat);
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
_pipeline.SetScissors(scissors);
@ -275,6 +438,12 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0);
if (dstIsDepthOrStencil)
{
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, GAL.CompareOp.Always));
}
_pipeline.Finish(gd, cbs);
gd.BufferManager.Delete(bufferHandle);
@ -287,6 +456,7 @@ namespace Ryujinx.Graphics.Vulkan
Auto<DisposableImageView> dst,
int dstWidth,
int dstHeight,
int dstSamples,
VkFormat dstFormat,
Extents2D srcRegion,
Extents2D dstRegion)
@ -339,7 +509,7 @@ namespace Ryujinx.Graphics.Vulkan
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat);
_pipeline.SetScissors(scissors);
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
@ -406,12 +576,12 @@ namespace Ryujinx.Graphics.Vulkan
if (isDepth)
{
_pipeline.SetProgram(_programDepthBlit);
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always));
}
else
{
_pipeline.SetProgram(_programStencilBlit);
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
}
@ -795,33 +965,25 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
if (src.Info.Target == Target.Texture2DMultisampleArray ||
dst.Info.Target == Target.Texture2DMultisampleArray)
for (int z = 0; z < depth; z++)
{
for (int z = 0; z < depth; z++)
{
var srcView = Create2DLayerView(src, srcLayer + z, format);
var dstView = Create2DLayerView(dst, dstLayer + z);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
srcView.Release();
dstView.Release();
}
}
else
{
var srcView = Create2DLayerView(src, srcLayer, format);
var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dst, format);
_pipeline.SetImage(0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
srcView.Release();
if (srcView != src)
{
srcView.Release();
}
if (dstView != dst)
{
dstView.Release();
}
}
gd.BufferManager.Delete(bufferHandle);
@ -906,36 +1068,14 @@ namespace Ryujinx.Graphics.Vulkan
var format = GetFormat(src.Info.BytesPerPixel);
var vkFormat = FormatTable.GetFormat(format);
if (src.Info.Target == Target.Texture2DMultisampleArray ||
dst.Info.Target == Target.Texture2DMultisampleArray)
for (int z = 0; z < depth; z++)
{
for (int z = 0; z < depth; z++)
{
var srcView = Create2DLayerView(src, srcLayer + z, format);
var dstView = Create2DLayerView(dst, dstLayer + z);
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
_pipeline.SetRenderTarget(
((TextureView)dstView).GetView(format).GetImageViewForAttachment(),
(uint)dst.Width,
(uint)dst.Height,
(uint)samples,
false,
vkFormat);
_pipeline.Draw(4, 1, 0, 0);
srcView.Release();
dstView.Release();
}
}
else
{
var srcView = Create2DLayerView(src, srcLayer, format);
var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
_pipeline.SetRenderTarget(
dst.GetView(format).GetImageViewForAttachment(),
((TextureView)dstView).GetView(format).GetImageViewForAttachment(),
(uint)dst.Width,
(uint)dst.Height,
(uint)samples,
@ -944,7 +1084,15 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.Draw(4, 1, 0, 0);
srcView.Release();
if (srcView != src)
{
srcView.Release();
}
if (dstView != dst)
{
dstView.Release();
}
}
gd.BufferManager.Delete(bufferHandle);
@ -1001,14 +1149,18 @@ namespace Ryujinx.Graphics.Vulkan
return (samplesInXLog2, samplesInYLog2);
}
private static ITexture Create2DLayerView(TextureView from, int layer, GAL.Format? format = null)
private static TextureView Create2DLayerView(TextureView from, int layer, int level, GAL.Format? format = null)
{
if (from.Info.Target == Target.Texture2D && level == 0 && (format == null || format.Value == from.Info.Format))
{
return from;
}
var target = from.Info.Target switch
{
Target.Texture1DArray => Target.Texture1D,
Target.Texture2DArray => Target.Texture2D,
Target.Texture2DMultisampleArray => Target.Texture2DMultisample,
_ => from.Info.Target
_ => Target.Texture2D
};
var info = new TextureCreateInfo(
@ -1028,7 +1180,7 @@ namespace Ryujinx.Graphics.Vulkan
from.Info.SwizzleB,
from.Info.SwizzleA);
return from.CreateView(info, layer, 0);
return from.CreateViewImpl(info, layer, level);
}
private static GAL.Format GetFormat(int bytesPerPixel)
@ -1177,6 +1329,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_programColorBlitClearAlpha.Dispose();
_programColorBlit.Dispose();
_programColorBlitMs.Dispose();
_programColorClearF.Dispose();
_programColorClearSI.Dispose();
_programColorClearUI.Dispose();
@ -1186,7 +1339,9 @@ namespace Ryujinx.Graphics.Vulkan
_programColorCopyToNonMs.Dispose();
_programColorDrawToMs.Dispose();
_programDepthBlit.Dispose();
_programDepthBlitMs.Dispose();
_programStencilBlit?.Dispose();
_programStencilBlitMs?.Dispose();
_samplerNearest.Dispose();
_samplerLinear.Dispose();
_pipeline.Dispose();