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 VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan { class HelperShader : IDisposable { private readonly PipelineHelperShader _pipeline; private readonly ISampler _samplerLinear; private readonly ISampler _samplerNearest; private readonly IProgram _programColorBlit; private readonly IProgram _programColorBlitClearAlpha; private readonly IProgram _programColorClear; 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 vertexBindings = new ShaderBindings( new[] { 1 }, Array.Empty(), Array.Empty(), Array.Empty()); var fragmentBindings = new ShaderBindings( Array.Empty(), Array.Empty(), new[] { 0 }, Array.Empty()); _programColorBlit = gd.CreateProgramWithMinimalLayout(new[] { new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl), }); _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[] { new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl), }); var fragmentBindings2 = new ShaderBindings( Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); _programColorClear = gd.CreateProgramWithMinimalLayout(new[] { new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Glsl), }); } public void Blit( VulkanRenderer gd, TextureView src, Auto dst, int dstWidth, int dstHeight, VkFormat dstFormat, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter, bool clearAlpha = false) { gd.FlushAllCommands(); using var cbs = gd.CommandBufferPool.Rent(); Blit(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha); } public void Blit( VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, Auto dst, int dstWidth, int dstHeight, VkFormat dstFormat, 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 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); Span bufferRanges = stackalloc BufferRange[1]; bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize); _pipeline.SetUniformBuffers(1, bufferRanges); 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.SetProgram(clearAlpha ? _programColorBlitClearAlpha : _programColorBlit); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }); _pipeline.SetScissors(scissors); if (clearAlpha) { _pipeline.ClearRenderTargetColor(0, 0, new ColorF(0f, 0f, 0f, 1f)); } _pipeline.SetViewports(viewports, false); _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip); _pipeline.Draw(4, 1, 0, 0); _pipeline.Finish(); gd.BufferManager.Delete(bufferHandle); } public void Clear( VulkanRenderer gd, Auto dst, ReadOnlySpan clearColor, uint componentMask, int dstWidth, int dstHeight, VkFormat dstFormat, Rectangle scissor) { const int ClearColorBufferSize = 16; gd.FlushAllCommands(); using var cbs = gd.CommandBufferPool.Rent(); _pipeline.SetCommandBuffer(cbs); var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize, false); gd.BufferManager.SetData(bufferHandle, 0, clearColor); Span bufferRanges = stackalloc BufferRange[1]; bufferRanges[0] = new BufferRange(bufferHandle, 0, ClearColorBufferSize); _pipeline.SetUniformBuffers(1, bufferRanges); Span viewports = stackalloc GAL.Viewport[1]; viewports[0] = new GAL.Viewport( new Rectangle(0, 0, dstWidth, dstHeight), ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, ViewportSwizzle.PositiveZ, ViewportSwizzle.PositiveW, 0f, 1f); Span> scissors = stackalloc Rectangle[1]; scissors[0] = scissor; _pipeline.SetProgram(_programColorClear); _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 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, false); gd.BufferManager.SetData(bufferHandle, 0, region); Span bufferRanges = stackalloc BufferRange[1]; bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize); pipeline.SetUniformBuffers(1, bufferRanges); 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]; 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) { // TODO: Do this with a compute shader? var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, size).Value; var dstBuffer = dst.GetBuffer().Get(cbs, 0, size * 2).Value; gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0); var bufferCopy = new BufferCopy[size]; for (ulong i = 0; i < (ulong)size; i++) { bufferCopy[i] = new BufferCopy((ulong)srcOffset + i, i * 2, 1); } BufferHolder.InsertBufferBarrier( gd, cbs.CommandBuffer, dstBuffer, BufferHolder.DefaultAccessFlags, AccessFlags.AccessTransferWriteBit, PipelineStageFlags.PipelineStageAllCommandsBit, PipelineStageFlags.PipelineStageTransferBit, 0, size * 2); fixed (BufferCopy* pBufferCopy = bufferCopy) { gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)size, pBufferCopy); } BufferHolder.InsertBufferBarrier( gd, cbs.CommandBuffer, dstBuffer, AccessFlags.AccessTransferWriteBit, BufferHolder.DefaultAccessFlags, PipelineStageFlags.PipelineStageTransferBit, PipelineStageFlags.PipelineStageAllCommandsBit, 0, size * 2); } protected virtual void Dispose(bool disposing) { if (disposing) { _programColorBlitClearAlpha.Dispose(); _programColorBlit.Dispose(); _samplerNearest.Dispose(); _samplerLinear.Dispose(); _pipeline.Dispose(); } } public void Dispose() { Dispose(true); } } }