diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Vulkan/HelperShader.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Vulkan/HelperShader.cs | 1683 |
1 files changed, 1683 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs new file mode 100644 index 00000000..c57edaf7 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -0,0 +1,1683 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using Ryujinx.Graphics.Vulkan.Shaders; +using Silk.NET.Vulkan; +using System; +using System.Collections.Generic; +using System.Numerics; +using VkFormat = Silk.NET.Vulkan.Format; + +namespace Ryujinx.Graphics.Vulkan +{ + enum ComponentType + { + Float, + SignedInteger, + UnsignedInteger + } + + class HelperShader : IDisposable + { + private const int UniformBufferAlignment = 256; + + private readonly PipelineHelperShader _pipeline; + 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; + private readonly IProgram _programColorClearUI; + private readonly IProgram _programStrideChange; + private readonly IProgram _programConvertIndexBuffer; + private readonly IProgram _programConvertIndirectData; + private readonly IProgram _programColorCopyShortening; + private readonly IProgram _programColorCopyToNonMs; + private readonly IProgram _programColorCopyWidening; + private readonly IProgram _programColorDrawToMs; + private readonly IProgram _programDepthBlit; + private readonly IProgram _programDepthBlitMs; + private readonly IProgram _programDepthDrawToMs; + private readonly IProgram _programDepthDrawToNonMs; + private readonly IProgram _programStencilBlit; + private readonly IProgram _programStencilBlitMs; + private readonly IProgram _programStencilDrawToMs; + private readonly IProgram _programStencilDrawToNonMs; + + public HelperShader(VulkanRenderer gd, Device device) + { + _pipeline = new PipelineHelperShader(gd, device); + _pipeline.Initialize(); + + _samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + _samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); + + var blitVertexBindings = new ShaderBindings( + new[] { 1 }, + Array.Empty<int>(), + Array.Empty<int>(), + Array.Empty<int>()); + + var blitFragmentBindings = new ShaderBindings( + Array.Empty<int>(), + Array.Empty<int>(), + new[] { 0 }, + Array.Empty<int>()); + + _programColorBlit = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + 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), + new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + var colorClearFragmentBindings = new ShaderBindings( + Array.Empty<int>(), + Array.Empty<int>(), + Array.Empty<int>(), + Array.Empty<int>()); + + _programColorClearF = gd.CreateProgramWithMinimalLayout(new[] + { + 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, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programColorClearUI = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + var strideChangeBindings = new ShaderBindings( + new[] { 0 }, + new[] { 1, 2 }, + Array.Empty<int>(), + Array.Empty<int>()); + + _programStrideChange = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + + var colorCopyBindings = new ShaderBindings( + new[] { 0 }, + Array.Empty<int>(), + new[] { 0 }, + new[] { 0 }); + + _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorCopyShorteningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + + _programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorCopyToNonMsComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + + _programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorCopyWideningComputeShaderSource, colorCopyBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + + var colorDrawToMsVertexBindings = new ShaderBindings( + Array.Empty<int>(), + Array.Empty<int>(), + Array.Empty<int>(), + Array.Empty<int>()); + + var colorDrawToMsFragmentBindings = new ShaderBindings( + new[] { 0 }, + Array.Empty<int>(), + new[] { 0 }, + Array.Empty<int>()); + + _programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.ColorDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + var convertIndexBufferBindings = new ShaderBindings( + new[] { 0 }, + new[] { 1, 2 }, + Array.Empty<int>(), + Array.Empty<int>()); + + _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv), + }); + + var convertIndirectDataBindings = new ShaderBindings( + new[] { 0 }, + new[] { 1, 2, 3 }, + Array.Empty<int>(), + Array.Empty<int>()); + + _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[] + { + 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), + }); + + _programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.DepthDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, 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), + }); + + _programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, blitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilBlitMsFragmentShaderSource, blitFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilDrawToMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + + _programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(ShaderBinaries.ColorDrawToMsVertexShaderSource, colorDrawToMsVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), + new ShaderSource(ShaderBinaries.StencilDrawToNonMsFragmentShaderSource, colorDrawToMsFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), + }); + } + } + + public void Blit( + VulkanRenderer gd, + TextureView src, + TextureView dst, + Extents2D srcRegion, + Extents2D dstRegion, + int layers, + int levels, + bool isDepthOrStencil, + bool linearFilter, + bool clearAlpha = false) + { + gd.FlushAllCommands(); + + using var cbs = gd.CommandBufferPool.Rent(); + + var dstFormat = dst.VkFormat; + var dstSamples = dst.Info.Samples; + + for (int l = 0; l < levels; l++) + { + 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(); + } + } + } + } + + 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++) + { + 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(); + } + } + } + } + + public void BlitColor( + VulkanRenderer gd, + CommandBufferScoped cbs, + TextureView src, + Auto<DisposableImageView> dst, + int dstWidth, + int dstHeight, + int dstSamples, + VkFormat dstFormat, + bool dstIsDepthOrStencil, + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter, + bool clearAlpha = false) + { + _pipeline.SetCommandBuffer(cbs); + + const int RegionBufferSize = 16; + + var sampler = linearFilter ? _samplerLinear : _samplerNearest; + + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); + + 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); + + 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); + + 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); + + if (clearAlpha) + { + _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); + } + + _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); + } + + private void BlitDepthStencil( + VulkanRenderer gd, + CommandBufferScoped cbs, + TextureView src, + Auto<DisposableImageView> dst, + int dstWidth, + int dstHeight, + int dstSamples, + 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); + + 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, (uint)dstSamples, 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(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); + } + else + { + _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _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, + ReadOnlySpan<float> clearColor, + uint componentMask, + int dstWidth, + int dstHeight, + VkFormat dstFormat, + ComponentType type, + Rectangle<int> scissor) + { + const int ClearColorBufferSize = 16; + + gd.FlushAllCommands(); + + using var cbs = gd.CommandBufferPool.Rent(); + + _pipeline.SetCommandBuffer(cbs); + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize); + + gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) }); + + 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; + + IProgram program; + + if (type == ComponentType.SignedInteger) + { + program = _programColorClearSI; + } + else if (type == ComponentType.UnsignedInteger) + { + program = _programColorClearUI; + } + else + { + program = _programColorClearF; + } + + _pipeline.SetProgram(program); + _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); + _pipeline.SetRenderTargetColorMasks(new uint[] { componentMask }); + _pipeline.SetViewports(viewports, false); + _pipeline.SetScissors(scissors); + _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + _pipeline.Draw(4, 1, 0, 0); + _pipeline.Finish(); + + gd.BufferManager.Delete(bufferHandle); + } + + public void DrawTexture( + VulkanRenderer gd, + PipelineBase pipeline, + TextureView src, + ISampler srcSampler, + Extents2DF srcRegion, + Extents2DF dstRegion) + { + const int RegionBufferSize = 16; + + pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler); + + Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)]; + + region[0] = srcRegion.X1 / src.Width; + region[1] = srcRegion.X2 / src.Width; + region[2] = srcRegion.Y1 / src.Height; + region[3] = 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); + + 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]; + + pipeline.SetProgram(_programColorBlit); + pipeline.SetViewports(viewports, false); + pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + pipeline.Draw(4, 1, 0, 0); + + gd.BufferManager.Delete(bufferHandle); + } + + public unsafe void ConvertI8ToI16(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size) + { + ChangeStride(gd, cbs, src, dst, srcOffset, size, 1, 2); + } + + public unsafe void ChangeStride(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size, int stride, int newStride) + { + bool supportsUint8 = gd.Capabilities.SupportsShaderInt8; + + int elems = size / stride; + int newSize = elems * newStride; + + var srcBufferAuto = src.GetBuffer(); + var dstBufferAuto = dst.GetBuffer(); + + var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value; + var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value; + + var access = supportsUint8 ? AccessFlags.ShaderWriteBit : AccessFlags.TransferWriteBit; + var stage = supportsUint8 ? PipelineStageFlags.ComputeShaderBit : PipelineStageFlags.TransferBit; + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + BufferHolder.DefaultAccessFlags, + access, + PipelineStageFlags.AllCommandsBit, + stage, + 0, + newSize); + + if (supportsUint8) + { + const int ParamsBufferSize = 16; + + Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; + + shaderParams[0] = stride; + shaderParams[1] = newStride; + shaderParams[2] = size; + shaderParams[3] = srcOffset; + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + + gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); + + _pipeline.SetCommandBuffer(cbs); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + + Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2]; + + sbRanges[0] = srcBufferAuto; + sbRanges[1] = dstBufferAuto; + + _pipeline.SetStorageBuffers(1, sbRanges); + + _pipeline.SetProgram(_programStrideChange); + _pipeline.DispatchCompute(1, 1, 1); + + gd.BufferManager.Delete(bufferHandle); + + _pipeline.Finish(gd, cbs); + } + else + { + gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0); + + var bufferCopy = new BufferCopy[elems]; + + for (ulong i = 0; i < (ulong)elems; i++) + { + bufferCopy[i] = new BufferCopy((ulong)srcOffset + i * (ulong)stride, i * (ulong)newStride, (ulong)stride); + } + + fixed (BufferCopy* pBufferCopy = bufferCopy) + { + gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)elems, pBufferCopy); + } + } + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + access, + BufferHolder.DefaultAccessFlags, + stage, + PipelineStageFlags.AllCommandsBit, + 0, + newSize); + } + + public unsafe void ConvertIndexBuffer(VulkanRenderer gd, + CommandBufferScoped cbs, + BufferHolder src, + BufferHolder dst, + IndexBufferPattern pattern, + int indexSize, + int srcOffset, + int indexCount) + { + // TODO: Support conversion with primitive restart enabled. + // TODO: Convert with a compute shader? + + int convertedCount = pattern.GetConvertedCount(indexCount); + int outputIndexSize = 4; + + var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value; + var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value; + + gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0); + + var bufferCopy = new List<BufferCopy>(); + int outputOffset = 0; + + // Try to merge copies of adjacent indices to reduce copy count. + int sequenceStart = 0; + int sequenceLength = 0; + + foreach (var index in pattern.GetIndexMapping(indexCount)) + { + if (sequenceLength > 0) + { + if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize) + { + sequenceLength++; + continue; + } + + // Commit the copy so far. + bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength))); + outputOffset += outputIndexSize * sequenceLength; + } + + sequenceStart = index; + sequenceLength = 1; + } + + if (sequenceLength > 0) + { + // Commit final pending copy. + bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength))); + } + + var bufferCopyArray = bufferCopy.ToArray(); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + BufferHolder.DefaultAccessFlags, + AccessFlags.TransferWriteBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, + 0, + convertedCount * outputIndexSize); + + fixed (BufferCopy* pBufferCopy = bufferCopyArray) + { + gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy); + } + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + AccessFlags.TransferWriteBit, + BufferHolder.DefaultAccessFlags, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, + 0, + convertedCount * outputIndexSize); + } + + public void CopyIncompatibleFormats( + VulkanRenderer gd, + CommandBufferScoped cbs, + TextureView src, + TextureView dst, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel, + int depth, + int levels) + { + const int ParamsBufferSize = 4; + + Span<int> shaderParams = stackalloc int[sizeof(int)]; + + int srcBpp = src.Info.BytesPerPixel; + int dstBpp = dst.Info.BytesPerPixel; + + int ratio = srcBpp < dstBpp ? dstBpp / srcBpp : srcBpp / dstBpp; + + shaderParams[0] = BitOperations.Log2((uint)ratio); + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + + gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + src.GetImage().Get(cbs).Value, + TextureStorage.DefaultAccessMask, + AccessFlags.ShaderReadBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.ComputeShaderBit, + ImageAspectFlags.ColorBit, + src.FirstLayer + srcLayer, + src.FirstLevel + srcLevel, + depth, + levels); + + _pipeline.SetCommandBuffer(cbs); + + _pipeline.SetProgram(srcBpp < dstBpp ? _programColorCopyWidening : _programColorCopyShortening); + + // Calculate ideal component size, given our constraints: + // - Component size must not exceed bytes per pixel of source and destination image formats. + // - Maximum component size is 4 (R32). + int componentSize = Math.Min(Math.Min(srcBpp, dstBpp), 4); + + var srcFormat = GetFormat(componentSize, srcBpp / componentSize); + var dstFormat = GetFormat(componentSize, dstBpp / componentSize); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + + for (int l = 0; l < levels; l++) + { + for (int z = 0; z < depth; z++) + { + var srcView = Create2DLayerView(src, srcLayer + z, srcLevel + l, srcFormat); + var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l); + + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null); + _pipeline.SetImage(0, dstView, dstFormat); + + int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; + int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32; + + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } + } + } + + gd.BufferManager.Delete(bufferHandle); + + _pipeline.Finish(gd, cbs); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + dst.GetImage().Get(cbs).Value, + AccessFlags.ShaderWriteBit, + TextureStorage.DefaultAccessMask, + PipelineStageFlags.ComputeShaderBit, + PipelineStageFlags.AllCommandsBit, + ImageAspectFlags.ColorBit, + dst.FirstLayer + dstLayer, + dst.FirstLevel + dstLevel, + depth, + levels); + } + + public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth) + { + const int ParamsBufferSize = 16; + + Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; + + int samples = src.Info.Samples; + bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil(); + var aspectFlags = src.Info.Format.ConvertAspectFlags(); + + // 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(gd.Capabilities.SupportedSampleCounts, (uint)samples)); + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + + gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + src.GetImage().Get(cbs).Value, + TextureStorage.DefaultAccessMask, + AccessFlags.ShaderReadBit, + PipelineStageFlags.AllCommandsBit, + isDepthOrStencil ? PipelineStageFlags.FragmentShaderBit : PipelineStageFlags.ComputeShaderBit, + aspectFlags, + src.FirstLayer + srcLayer, + src.FirstLevel, + depth, + 1); + + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + + if (isDepthOrStencil) + { + // We can't use compute for this case because compute can't modify depth textures. + + Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + + var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height); + + 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, dst.Width, dst.Height); + + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + + for (int z = 0; z < depth; z++) + { + var srcView = Create2DLayerView(src, srcLayer + z, 0); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); + + _pipeline.SetRenderTarget( + ((TextureView)dstView).GetImageViewForAttachment(), + (uint)dst.Width, + (uint)dst.Height, + true, + dst.VkFormat); + + CopyMSDraw(srcView, aspectFlags, fromMS: true); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } + } + } + else + { + var format = GetFormat(src.Info.BytesPerPixel); + + int dispatchX = (dst.Info.Width + 31) / 32; + int dispatchY = (dst.Info.Height + 31) / 32; + + _pipeline.SetProgram(_programColorCopyToNonMs); + + for (int z = 0; z < depth; z++) + { + 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, dstView, format); + + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } + } + } + + gd.BufferManager.Delete(bufferHandle); + + _pipeline.Finish(gd, cbs); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + dst.GetImage().Get(cbs).Value, + isDepthOrStencil ? AccessFlags.DepthStencilAttachmentWriteBit : AccessFlags.ShaderWriteBit, + TextureStorage.DefaultAccessMask, + isDepthOrStencil ? PipelineStageFlags.LateFragmentTestsBit : PipelineStageFlags.ComputeShaderBit, + PipelineStageFlags.AllCommandsBit, + aspectFlags, + dst.FirstLayer + dstLayer, + dst.FirstLevel, + depth, + 1); + } + + public void CopyNonMSToMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth) + { + const int ParamsBufferSize = 16; + + Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; + + int samples = dst.Info.Samples; + bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil(); + var aspectFlags = src.Info.Format.ConvertAspectFlags(); + + // 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(gd.Capabilities.SupportedSampleCounts, (uint)samples)); + + var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize); + + gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + src.GetImage().Get(cbs).Value, + TextureStorage.DefaultAccessMask, + AccessFlags.ShaderReadBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.FragmentShaderBit, + aspectFlags, + src.FirstLayer + srcLayer, + src.FirstLevel, + depth, + 1); + + _pipeline.SetCommandBuffer(cbs); + + Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1]; + + var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height); + + 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, dst.Width, dst.Height); + + _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) }); + + if (isDepthOrStencil) + { + for (int z = 0; z < depth; z++) + { + var srcView = Create2DLayerView(src, srcLayer + z, 0); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); + + _pipeline.SetRenderTarget( + ((TextureView)dstView).GetImageViewForAttachment(), + (uint)dst.Width, + (uint)dst.Height, + (uint)samples, + true, + dst.VkFormat); + + CopyMSDraw(srcView, aspectFlags, fromMS: false); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } + } + } + else + { + _pipeline.SetProgram(_programColorDrawToMs); + + var format = GetFormat(src.Info.BytesPerPixel); + var vkFormat = FormatTable.GetFormat(format); + + for (int z = 0; z < depth; z++) + { + var srcView = Create2DLayerView(src, srcLayer + z, 0, format); + var dstView = Create2DLayerView(dst, dstLayer + z, 0); + + _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); + + if (srcView != src) + { + srcView.Release(); + } + + if (dstView != dst) + { + dstView.Release(); + } + } + } + + gd.BufferManager.Delete(bufferHandle); + + _pipeline.Finish(gd, cbs); + + TextureView.InsertImageBarrier( + gd.Api, + cbs.CommandBuffer, + dst.GetImage().Get(cbs).Value, + isDepthOrStencil ? AccessFlags.DepthStencilAttachmentWriteBit : AccessFlags.ColorAttachmentWriteBit, + TextureStorage.DefaultAccessMask, + isDepthOrStencil ? PipelineStageFlags.LateFragmentTestsBit : PipelineStageFlags.ColorAttachmentOutputBit, + PipelineStageFlags.AllCommandsBit, + aspectFlags, + dst.FirstLayer + dstLayer, + dst.FirstLevel, + depth, + 1); + } + + private void CopyMSDraw(TextureView src, ImageAspectFlags aspectFlags, bool fromMS) + { + if (aspectFlags.HasFlag(ImageAspectFlags.DepthBit)) + { + var depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth); + + CopyMSAspectDraw(depthTexture, fromMS, isDepth: true); + + if (depthTexture != src) + { + depthTexture.Release(); + } + } + + if (aspectFlags.HasFlag(ImageAspectFlags.StencilBit) && _programStencilDrawToMs != null) + { + var stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil); + + CopyMSAspectDraw(stencilTexture, fromMS, isDepth: false); + + if (stencilTexture != src) + { + stencilTexture.Release(); + } + } + } + + private void CopyMSAspectDraw(TextureView src, bool fromMS, bool isDepth) + { + _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); + + if (isDepth) + { + _pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs); + _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, GAL.CompareOp.Always)); + } + else + { + _pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs); + _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 (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 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.Texture2DMultisampleArray => Target.Texture2DMultisample, + _ => Target.Texture2D + }; + + 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.CreateViewImpl(info, layer, level); + } + + 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}.") + }; + } + + private static GAL.Format GetFormat(int componentSize, int componentsCount) + { + if (componentSize == 1) + { + return componentsCount switch + { + 1 => GAL.Format.R8Uint, + 2 => GAL.Format.R8G8Uint, + 4 => GAL.Format.R8G8B8A8Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else if (componentSize == 2) + { + return componentsCount switch + { + 1 => GAL.Format.R16Uint, + 2 => GAL.Format.R16G16Uint, + 4 => GAL.Format.R16G16B16A16Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else if (componentSize == 4) + { + return componentsCount switch + { + 1 => GAL.Format.R32Uint, + 2 => GAL.Format.R32G32Uint, + 4 => GAL.Format.R32G32B32A32Uint, + _ => throw new ArgumentException($"Invalid components count {componentsCount}.") + }; + } + else + { + throw new ArgumentException($"Invalid component size {componentSize}."); + } + } + + public void ConvertIndexBufferIndirect( + VulkanRenderer gd, + CommandBufferScoped cbs, + BufferHolder srcIndirectBuffer, + BufferHolder dstIndirectBuffer, + BufferRange drawCountBuffer, + BufferHolder srcIndexBuffer, + BufferHolder dstIndexBuffer, + IndexBufferPattern pattern, + int indexSize, + int srcIndexBufferOffset, + int srcIndexBufferSize, + int srcIndirectBufferOffset, + bool hasDrawCount, + int maxDrawCount, + int indirectDataStride) + { + // TODO: Support conversion with primitive restart enabled. + + BufferRange drawCountBufferAligned = new BufferRange( + drawCountBuffer.Handle, + drawCountBuffer.Offset & ~(UniformBufferAlignment - 1), + UniformBufferAlignment); + + int indirectDataSize = maxDrawCount * indirectDataStride; + + int indexCount = srcIndexBufferSize / indexSize; + int primitivesCount = pattern.GetPrimitiveCount(indexCount); + int convertedCount = pattern.GetConvertedCount(indexCount); + int outputIndexSize = 4; + + var srcBuffer = srcIndexBuffer.GetBuffer().Get(cbs, srcIndexBufferOffset, indexCount * indexSize).Value; + var dstBuffer = dstIndexBuffer.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value; + + const int ParamsBufferSize = 24 * sizeof(int); + const int ParamsIndirectDispatchOffset = 16 * sizeof(int); + const int ParamsIndirectDispatchSize = 3 * sizeof(int); + + Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; + + shaderParams[8] = pattern.PrimitiveVertices; + shaderParams[9] = pattern.PrimitiveVerticesOut; + shaderParams[10] = indexSize; + shaderParams[11] = outputIndexSize; + shaderParams[12] = pattern.BaseIndex; + shaderParams[13] = pattern.IndexStride; + shaderParams[14] = srcIndexBufferOffset; + shaderParams[15] = primitivesCount; + shaderParams[16] = 1; + shaderParams[17] = 1; + shaderParams[18] = 1; + shaderParams[19] = hasDrawCount ? 1 : 0; + shaderParams[20] = maxDrawCount; + shaderParams[21] = (drawCountBuffer.Offset & (UniformBufferAlignment - 1)) / 4; + shaderParams[22] = indirectDataStride / 4; + shaderParams[23] = srcIndirectBufferOffset / 4; + + pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length)); + + var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, out var patternBuffer); + var patternBufferAuto = patternBuffer.GetBuffer(); + + gd.BufferManager.SetData<int>(patternBufferHandle, 0, shaderParams); + + _pipeline.SetCommandBuffer(cbs); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + srcIndirectBuffer.GetBuffer().Get(cbs, srcIndirectBufferOffset, indirectDataSize).Value, + BufferHolder.DefaultAccessFlags, + AccessFlags.ShaderReadBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.ComputeShaderBit, + srcIndirectBufferOffset, + indirectDataSize); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, drawCountBufferAligned) }); + _pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() }); + + _pipeline.SetProgram(_programConvertIndirectData); + _pipeline.DispatchCompute(1, 1, 1); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value, + AccessFlags.ShaderWriteBit, + AccessFlags.IndirectCommandReadBit, + PipelineStageFlags.ComputeShaderBit, + PipelineStageFlags.DrawIndirectBit, + ParamsIndirectDispatchOffset, + ParamsIndirectDispatchSize); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + BufferHolder.DefaultAccessFlags, + AccessFlags.TransferWriteBit, + PipelineStageFlags.AllCommandsBit, + PipelineStageFlags.TransferBit, + 0, + convertedCount * outputIndexSize); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(patternBufferHandle, 0, ParamsBufferSize)) }); + _pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() }); + + _pipeline.SetProgram(_programConvertIndexBuffer); + _pipeline.DispatchComputeIndirect(patternBufferAuto, ParamsIndirectDispatchOffset); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstBuffer, + AccessFlags.TransferWriteBit, + BufferHolder.DefaultAccessFlags, + PipelineStageFlags.TransferBit, + PipelineStageFlags.AllCommandsBit, + 0, + convertedCount * outputIndexSize); + + gd.BufferManager.Delete(patternBufferHandle); + + _pipeline.Finish(gd, cbs); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _programColorBlitClearAlpha.Dispose(); + _programColorBlit.Dispose(); + _programColorBlitMs.Dispose(); + _programColorClearF.Dispose(); + _programColorClearSI.Dispose(); + _programColorClearUI.Dispose(); + _programStrideChange.Dispose(); + _programConvertIndexBuffer.Dispose(); + _programConvertIndirectData.Dispose(); + _programColorCopyShortening.Dispose(); + _programColorCopyToNonMs.Dispose(); + _programColorCopyWidening.Dispose(); + _programColorDrawToMs.Dispose(); + _programDepthBlit.Dispose(); + _programDepthBlitMs.Dispose(); + _programDepthDrawToMs.Dispose(); + _programDepthDrawToNonMs.Dispose(); + _programStencilBlit?.Dispose(); + _programStencilBlitMs?.Dispose(); + _programStencilDrawToMs?.Dispose(); + _programStencilDrawToNonMs?.Dispose(); + _samplerNearest.Dispose(); + _samplerLinear.Dispose(); + _pipeline.Dispose(); + } + } + + public void Dispose() + { + Dispose(true); + } + } +} |