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:
parent
cb250162cb
commit
f8beeeb7d3
7 changed files with 436 additions and 74 deletions
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue