diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index ff8d423b83..0a1cdcce36 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Vulkan { _device = device; _attachments = new[] { view }; - _validColorAttachments = 1u; + _validColorAttachments = isDepthStencil ? 0u : 1u; Width = width; Height = height; @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan AttachmentSamples = new[] { samples }; AttachmentFormats = new[] { format }; - AttachmentIndices = new[] { 0 }; + AttachmentIndices = isDepthStencil ? Array.Empty() : new[] { 0 }; AttachmentsCount = 1; diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index 376f396083..82fcaea102 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsSubgroupSizeControl; public readonly bool SupportsShaderInt8; + public readonly bool SupportsShaderStencilExport; public readonly bool SupportsConditionalRendering; public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsMultiView; @@ -48,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan bool supportsGeometryShaderPassthrough, bool supportsSubgroupSizeControl, bool supportsShaderInt8, + bool supportsShaderStencilExport, bool supportsConditionalRendering, bool supportsExtendedDynamicState, bool supportsMultiView, @@ -71,6 +73,7 @@ namespace Ryujinx.Graphics.Vulkan SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsSubgroupSizeControl = supportsSubgroupSizeControl; SupportsShaderInt8 = supportsShaderInt8; + SupportsShaderStencilExport = supportsShaderStencilExport; SupportsConditionalRendering = supportsConditionalRendering; SupportsExtendedDynamicState = supportsExtendedDynamicState; SupportsMultiView = supportsMultiView; diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 223bcc71ee..658516e6dc 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -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(), Array.Empty(), Array.Empty()); - var colorBlitFragmentBindings = new ShaderBindings( + var blitFragmentBindings = new ShaderBindings( Array.Empty(), Array.Empty(), 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 dst, + int dstWidth, + int dstHeight, + VkFormat dstFormat, + Extents2D srcRegion, + Extents2D dstRegion) + { + _pipeline.SetCommandBuffer(cbs); + + const int RegionBufferSize = 16; + + Span 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(bufferHandle, 0, region); + + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); + + Span viewports = stackalloc GAL.Viewport[1]; + + var rect = new Rectangle( + 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> scissors = stackalloc Rectangle[1]; + + scissors[0] = new Rectangle(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 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(); diff --git a/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag new file mode 100644 index 0000000000..55b7be137e --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/DepthBlitFragmentShaderSource.frag @@ -0,0 +1,10 @@ +#version 450 core + +layout (binding = 0, set = 2) uniform sampler2D texDepth; + +layout (location = 0) in vec2 tex_coord; + +void main() +{ + gl_FragDepth = texture(texDepth, tex_coord).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs index 667e5a8b4c..5a2acf2218 100644 --- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs +++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs @@ -1459,5 +1459,95 @@ namespace Ryujinx.Graphics.Vulkan.Shaders 0x3B, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; + + public static readonly byte[] DepthBlitFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x44, + 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x74, 0x65, 0x78, 0x44, 0x65, 0x70, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; + + public static readonly byte[] StencilBlitFragmentShaderSource = new byte[] + { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x95, 0x13, 0x00, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x45, 0x58, 0x54, 0x5F, + 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, + 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA3, 0x13, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, + 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x5F, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x53, + 0x74, 0x65, 0x6E, 0x63, 0x69, 0x6C, 0x52, 0x65, 0x66, 0x41, 0x52, 0x42, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x53, 0x74, 0x65, 0x6E, 0x63, + 0x69, 0x6C, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F, + 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + }; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag b/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag new file mode 100644 index 0000000000..1919269be7 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Shaders/StencilBlitFragmentShaderSource.frag @@ -0,0 +1,12 @@ +#version 450 core + +#extension GL_ARB_shader_stencil_export : require + +layout (binding = 0, set = 2) uniform isampler2D texStencil; + +layout (location = 0) in vec2 tex_coord; + +void main() +{ + gl_FragStencilRefARB = texture(texStencil, tex_coord).r; +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index 67f2072196..3ee196756c 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -362,21 +362,16 @@ namespace Ryujinx.Graphics.Vulkan levels, linearFilter); - return; - } - else if (srcFormat == GAL.Format.D32FloatS8Uint && srcFormat == dstFormat && SupportsBlitFromD32FS8ToD32FAndS8()) - { - BlitDepthStencilWithBuffer(_gd, cbs, src, dst, srcRegion, dstRegion); - return; } } + bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); + if (VulkanConfiguration.UseSlowSafeBlitOnAmd && (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk) && src.Info.Target == Target.Texture2D && - dst.Info.Target == Target.Texture2D && - !dst.Info.Format.IsDepthOrStencil()) + dst.Info.Target == Target.Texture2D) { _gd.HelperShader.Blit( _gd, @@ -387,6 +382,7 @@ namespace Ryujinx.Graphics.Vulkan dst.VkFormat, srcRegion, dstRegion, + isDepthOrStencil, linearFilter); return; @@ -395,7 +391,7 @@ namespace Ryujinx.Graphics.Vulkan Auto srcImage; Auto dstImage; - if (dst.Info.Format.IsDepthOrStencil()) + if (isDepthOrStencil) { srcImage = src.Storage.CreateAliasedColorForDepthStorageUnsafe(srcFormat).GetImage(); dstImage = dst.Storage.CreateAliasedColorForDepthStorageUnsafe(dstFormat).GetImage(); @@ -426,189 +422,6 @@ namespace Ryujinx.Graphics.Vulkan ImageAspectFlags.ColorBit); } - private static void BlitDepthStencilWithBuffer( - VulkanRenderer gd, - CommandBufferScoped cbs, - TextureView src, - TextureView dst, - Extents2D srcRegion, - Extents2D dstRegion) - { - int drBaseX = Math.Min(dstRegion.X1, dstRegion.X2); - int drBaseY = Math.Min(dstRegion.Y1, dstRegion.Y2); - int drWidth = Math.Abs(dstRegion.X2 - dstRegion.X1); - int drHeight = Math.Abs(dstRegion.Y2 - dstRegion.Y1); - - var drOriginZero = new Extents2D( - dstRegion.X1 - drBaseX, - dstRegion.Y1 - drBaseY, - dstRegion.X2 - drBaseX, - dstRegion.Y2 - drBaseY); - - var d32SrcStorageInfo = TextureStorage.NewCreateInfoWith(ref src._info, GAL.Format.D32Float, 4); - var d32DstStorageInfo = TextureStorage.NewCreateInfoWith(ref dst._info, GAL.Format.D32Float, 4, drWidth, drHeight); - var s8SrcStorageInfo = TextureStorage.NewCreateInfoWith(ref src._info, GAL.Format.S8Uint, 1); - var s8DstStorageInfo = TextureStorage.NewCreateInfoWith(ref dst._info, GAL.Format.S8Uint, 1, drWidth, drHeight); - - using var d32SrcStorage = gd.CreateTextureStorage(d32SrcStorageInfo, src.Storage.ScaleFactor); - using var d32DstStorage = gd.CreateTextureStorage(d32DstStorageInfo, dst.Storage.ScaleFactor); - using var s8SrcStorage = gd.CreateTextureStorage(s8SrcStorageInfo, src.Storage.ScaleFactor); - using var s8DstStorage = gd.CreateTextureStorage(s8DstStorageInfo, dst.Storage.ScaleFactor); - - void SlowBlit(TextureStorage srcTemp, TextureStorage dstTemp, ImageAspectFlags aspectFlags) - { - int levels = Math.Min(src.Info.Levels, dst.Info.Levels); - - int srcSize = 0; - int dstSize = 0; - - for (int l = 0; l < levels; l++) - { - srcSize += srcTemp.Info.GetMipSize2D(l); - dstSize += dstTemp.Info.GetMipSize2D(l); - } - - using var srcTempBuffer = gd.BufferManager.Create(gd, srcSize, deviceLocal: true); - using var dstTempBuffer = gd.BufferManager.Create(gd, dstSize, deviceLocal: true); - - src.Storage.CopyFromOrToBuffer( - cbs.CommandBuffer, - srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, - src.GetImage().Get(cbs).Value, - srcSize, - to: true, - 0, - 0, - src.FirstLayer, - src.FirstLevel, - 1, - levels, - true, - aspectFlags, - false); - - BufferHolder.InsertBufferBarrier( - gd, - cbs.CommandBuffer, - srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, - AccessFlags.TransferWriteBit, - AccessFlags.TransferReadBit, - PipelineStageFlags.TransferBit, - PipelineStageFlags.TransferBit, - 0, - srcSize); - - srcTemp.CopyFromOrToBuffer( - cbs.CommandBuffer, - srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, - srcTemp.GetImage().Get(cbs).Value, - srcSize, - to: false, - 0, - 0, - 0, - 0, - 1, - levels, - true, - aspectFlags, - false); - - InsertImageBarrier( - gd.Api, - cbs.CommandBuffer, - srcTemp.GetImage().Get(cbs).Value, - AccessFlags.TransferWriteBit, - AccessFlags.TransferReadBit, - PipelineStageFlags.TransferBit, - PipelineStageFlags.TransferBit, - aspectFlags, - 0, - 0, - 1, - levels); - - TextureCopy.Blit( - gd.Api, - cbs.CommandBuffer, - srcTemp.GetImage().Get(cbs).Value, - dstTemp.GetImage().Get(cbs).Value, - srcTemp.Info, - dstTemp.Info, - srcRegion, - drOriginZero, - 0, - 0, - 0, - 0, - 1, - levels, - false, - aspectFlags, - aspectFlags); - - InsertImageBarrier( - gd.Api, - cbs.CommandBuffer, - dstTemp.GetImage().Get(cbs).Value, - AccessFlags.TransferWriteBit, - AccessFlags.TransferReadBit, - PipelineStageFlags.TransferBit, - PipelineStageFlags.TransferBit, - aspectFlags, - 0, - 0, - 1, - levels); - - dstTemp.CopyFromOrToBuffer( - cbs.CommandBuffer, - dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, - dstTemp.GetImage().Get(cbs).Value, - dstSize, - to: true, - 0, - 0, - 0, - 0, - 1, - levels, - true, - aspectFlags, - false); - - BufferHolder.InsertBufferBarrier( - gd, - cbs.CommandBuffer, - dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, - AccessFlags.TransferWriteBit, - AccessFlags.TransferReadBit, - PipelineStageFlags.TransferBit, - PipelineStageFlags.TransferBit, - 0, - dstSize); - - dst.Storage.CopyFromOrToBuffer( - cbs.CommandBuffer, - dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, - dst.GetImage().Get(cbs).Value, - dstSize, - to: false, - drBaseX, - drBaseY, - dst.FirstLayer, - dst.FirstLevel, - 1, - levels, - true, - aspectFlags, - false); - } - - SlowBlit(d32SrcStorage, d32DstStorage, ImageAspectFlags.DepthBit); - SlowBlit(s8SrcStorage, s8DstStorage, ImageAspectFlags.StencilBit); - } - public static unsafe void InsertImageBarrier( Vk api, CommandBuffer commandBuffer, @@ -649,13 +462,6 @@ namespace Ryujinx.Graphics.Vulkan memoryBarrier); } - private bool SupportsBlitFromD32FS8ToD32FAndS8() - { - var formatFeatureFlags = FormatFeatureFlags.BlitSrcBit | FormatFeatureFlags.BlitDstBit; - return _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.D32Float) && - _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.S8Uint); - } - public TextureView GetView(GAL.Format format) { if (format == Info.Format) diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index f38fc8ab83..ab5a0acfb7 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.Vulkan "VK_EXT_fragment_shader_interlock", "VK_EXT_index_type_uint8", "VK_EXT_robustness2", + "VK_EXT_shader_stencil_export", "VK_KHR_shader_float16_int8", "VK_EXT_shader_subgroup_ballot", "VK_EXT_subgroup_size_control", diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 085a7e930b..92dec7a1a5 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -263,6 +263,7 @@ namespace Ryujinx.Graphics.Vulkan supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), supportedExtensions.Contains("VK_EXT_subgroup_size_control"), featuresShaderInt8.ShaderInt8, + supportedExtensions.Contains("VK_EXT_shader_stencil_export"), supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName), features2.Features.MultiViewport, diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 9f731e0e6e..dc4dc6be94 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -335,7 +335,7 @@ namespace Ryujinx.Graphics.Vulkan int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; - _gd.HelperShader.Blit( + _gd.HelperShader.BlitColor( _gd, cbs, view,