aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Vulkan/HelperShader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Vulkan/HelperShader.cs')
-rw-r--r--Ryujinx.Graphics.Vulkan/HelperShader.cs234
1 files changed, 214 insertions, 20 deletions
diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs
index 0201de0a..c842b922 100644
--- a/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programColorBlitClearAlpha;
private readonly IProgram _programColorClear;
private readonly IProgram _programStrideChange;
+ private readonly IProgram _programColorCopyBetweenMsNonMs;
public HelperShader(VulkanRenderer gd, Device device)
{
@@ -73,6 +74,20 @@ namespace Ryujinx.Graphics.Vulkan
{
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
});
+
+ var colorCopyMSBindings = new ShaderBindings(
+ new[] { 0 },
+ Array.Empty<int>(),
+ new[] { 0 },
+ new[] { 0 });
+
+ _programColorCopyBetweenMsNonMs = gd.CreateProgramWithMinimalLayout(new[]
+ {
+ new ShaderSource(ShaderBinaries.ColorCopyBetweenMsNonMs, colorCopyMSBindings, ShaderStage.Compute, TargetLanguage.Spirv),
+ }, new[]
+ {
+ new SpecDescription((0, SpecConstType.Int32))
+ });
}
public void Blit(
@@ -136,11 +151,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
- Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
-
- bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize);
-
- _pipeline.SetUniformBuffers(1, bufferRanges);
+ _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) });
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
@@ -203,11 +214,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
- Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
-
- bufferRanges[0] = new BufferRange(bufferHandle, 0, ClearColorBufferSize);
-
- _pipeline.SetUniformBuffers(1, bufferRanges);
+ _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, ClearColorBufferSize) });
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
@@ -269,11 +276,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
- Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
-
- bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize);
-
- pipeline.SetUniformBuffers(1, bufferRanges);
+ pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) });
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
@@ -351,11 +354,7 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetCommandBuffer(cbs);
- Span<BufferRange> cbRanges = stackalloc BufferRange[1];
-
- cbRanges[0] = new BufferRange(bufferHandle, 0, ParamsBufferSize);
-
- _pipeline.SetUniformBuffers(0, cbRanges);
+ _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) });
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
@@ -480,12 +479,207 @@ namespace Ryujinx.Graphics.Vulkan
convertedCount * outputIndexSize);
}
+ public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
+ {
+ CopyMS(gd, cbs, src, dst, srcLayer, dstLayer, depth, src.Info.Samples, dst.Info.Width, dst.Info.Height);
+ }
+
+ public void CopyNonMSToMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
+ {
+ CopyMS(gd, cbs, src, dst, srcLayer, dstLayer, depth, dst.Info.Samples, src.Info.Width, src.Info.Height);
+ }
+
+ private void CopyMS(
+ VulkanRenderer gd,
+ CommandBufferScoped cbs,
+ TextureView src,
+ TextureView dst,
+ int srcLayer,
+ int dstLayer,
+ int depth,
+ int samples,
+ int nonMSWidth,
+ int nonMSHeight)
+ {
+ const int ParamsBufferSize = 16;
+
+ Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
+
+ // X and Y are the expected texture samples.
+ // Z and W are the actual texture samples used.
+ // They may differ if the GPU does not support the samples count requested and we had to use a lower amount.
+ (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
+ (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags((uint)samples));
+
+ var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
+
+ gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
+
+ TextureView.InsertImageBarrier(
+ gd.Api,
+ cbs.CommandBuffer,
+ src.GetImage().Get(cbs).Value,
+ TextureStorage.DefaultAccessMask,
+ AccessFlags.AccessShaderReadBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ PipelineStageFlags.PipelineStageComputeShaderBit,
+ ImageAspectFlags.ImageAspectColorBit,
+ src.FirstLayer + srcLayer,
+ src.FirstLevel,
+ depth,
+ 1);
+
+ _pipeline.SetCommandBuffer(cbs);
+
+ _pipeline.SetProgram(_programColorCopyBetweenMsNonMs);
+
+ var format = GetFormat(src.Info.BytesPerPixel);
+
+ int dispatchX = (nonMSWidth + 31) / 32;
+ int dispatchY = (nonMSHeight + 31) / 32;
+
+ // Specialize shader.
+ bool srcIsMs = src.Info.Target.IsMultisample();
+ int conversionType = srcIsMs ? src.Info.BytesPerPixel : -src.Info.BytesPerPixel;
+ _pipeline.Specialize(conversionType);
+
+ _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) });
+
+ if (src.Info.Target == Target.Texture2DMultisampleArray ||
+ dst.Info.Target == Target.Texture2DMultisampleArray)
+ {
+ 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);
+
+ _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
+ _pipeline.SetImage(0, dst, format);
+
+ _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
+
+ srcView.Release();
+ }
+
+ gd.BufferManager.Delete(bufferHandle);
+
+ _pipeline.Finish(gd, cbs);
+
+ TextureView.InsertImageBarrier(
+ gd.Api,
+ cbs.CommandBuffer,
+ dst.GetImage().Get(cbs).Value,
+ AccessFlags.AccessShaderWriteBit,
+ TextureStorage.DefaultAccessMask,
+ PipelineStageFlags.PipelineStageComputeShaderBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ ImageAspectFlags.ImageAspectColorBit,
+ dst.FirstLayer + dstLayer,
+ dst.FirstLevel,
+ depth,
+ 1);
+ }
+
+ private static (int, int) GetSampleCountXYLog2(int samples)
+ {
+ int samplesInXLog2 = 0;
+ int samplesInYLog2 = 0;
+
+ switch (samples)
+ {
+ case 2: // 2x1
+ samplesInXLog2 = 1;
+ break;
+ case 4: // 2x2
+ samplesInXLog2 = 1;
+ samplesInYLog2 = 1;
+ break;
+ case 8: // 4x2
+ samplesInXLog2 = 2;
+ samplesInYLog2 = 1;
+ break;
+ case 16: // 4x4
+ samplesInXLog2 = 2;
+ samplesInYLog2 = 2;
+ break;
+ case 32: // 8x4
+ samplesInXLog2 = 3;
+ samplesInYLog2 = 2;
+ break;
+ case 64: // 8x8
+ samplesInXLog2 = 3;
+ samplesInYLog2 = 3;
+ break;
+ }
+
+ return (samplesInXLog2, samplesInYLog2);
+ }
+
+ private static ITexture Create2DLayerView(TextureView from, int layer, GAL.Format? format = null)
+ {
+ var target = from.Info.Target switch
+ {
+ Target.Texture1DArray => Target.Texture1D,
+ Target.Texture2DArray => Target.Texture2D,
+ Target.Texture2DMultisampleArray => Target.Texture2DMultisample,
+ _ => from.Info.Target
+ };
+
+ var info = new TextureCreateInfo(
+ from.Info.Width,
+ from.Info.Height,
+ from.Info.Depth,
+ 1,
+ from.Info.Samples,
+ from.Info.BlockWidth,
+ from.Info.BlockHeight,
+ from.Info.BytesPerPixel,
+ format ?? from.Info.Format,
+ from.Info.DepthStencilMode,
+ target,
+ from.Info.SwizzleR,
+ from.Info.SwizzleG,
+ from.Info.SwizzleB,
+ from.Info.SwizzleA);
+
+ return from.CreateView(info, layer, 0);
+ }
+
+ private static GAL.Format GetFormat(int bytesPerPixel)
+ {
+ return bytesPerPixel switch
+ {
+ 1 => GAL.Format.R8Uint,
+ 2 => GAL.Format.R16Uint,
+ 4 => GAL.Format.R32Uint,
+ 8 => GAL.Format.R32G32Uint,
+ 16 => GAL.Format.R32G32B32A32Uint,
+ _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.")
+ };
+ }
+
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_programColorBlitClearAlpha.Dispose();
_programColorBlit.Dispose();
+ _programColorClear.Dispose();
+ _programStrideChange.Dispose();
+ _programColorCopyBetweenMsNonMs.Dispose();
_samplerNearest.Dispose();
_samplerLinear.Dispose();
_pipeline.Dispose();