From 80b497213981512e9ba1a629bcd5e2c519d2e566 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 27 Feb 2023 21:11:55 +0000 Subject: Add Support for Post Processing Effects (#3616) * Add Post Processing Effects * fix events and shader issues * fix gtk upscale slider value * fix bgra games * don't swap swizzle if already swapped * restore opengl texture state after effects run * addressed review * use single pipeline for smaa and fsr * call finish on all pipelines * addressed review * attempt fix file case * attempt fixing file case * fix filter level tick frequency * adjust filter slider margins * replace fxaa shaders with original shader * addressed review --- .../Effects/FsrScalingFilter.cs | 208 +++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs (limited to 'Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs') diff --git a/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs new file mode 100644 index 00000000..a1207059 --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs @@ -0,0 +1,208 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using Silk.NET.Vulkan; +using System; +using Extent2D = Ryujinx.Graphics.GAL.Extents2D; + +namespace Ryujinx.Graphics.Vulkan.Effects +{ + internal partial class FsrScalingFilter : IScalingFilter + { + private readonly VulkanRenderer _renderer; + private PipelineHelperShader _pipeline; + private ISampler _sampler; + private ShaderCollection _scalingProgram; + private ShaderCollection _sharpeningProgram; + private float _sharpeningLevel = 1; + private Device _device; + private TextureView _intermediaryTexture; + + public float Level + { + get => _sharpeningLevel; + set + { + _sharpeningLevel = MathF.Max(0.01f, value); + } + } + + public FsrScalingFilter(VulkanRenderer renderer, Device device) + { + _device = device; + _renderer = renderer; + + Initialize(); + } + + public void Dispose() + { + _pipeline.Dispose(); + _scalingProgram.Dispose(); + _sharpeningProgram.Dispose(); + _sampler.Dispose(); + _intermediaryTexture?.Dispose(); + } + + public void Initialize() + { + _pipeline = new PipelineHelperShader(_renderer, _device); + + _pipeline.Initialize(); + + var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv"); + var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv"); + + var computeBindings = new ShaderBindings( + new[] { 2 }, + Array.Empty(), + new[] { 1 }, + new[] { 0 }); + + var sharpeningBindings = new ShaderBindings( + new[] { 2, 3, 4 }, + Array.Empty(), + new[] { 1 }, + new[] { 0 }); + + _sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); + + _scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(scalingShader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv) + }); + + _sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[] + { + new ShaderSource(sharpeningShader, sharpeningBindings, ShaderStage.Compute, TargetLanguage.Spirv) + }); + } + + public void Run( + TextureView view, + CommandBufferScoped cbs, + Auto destinationTexture, + Silk.NET.Vulkan.Format format, + int width, + int height, + Extent2D source, + Extent2D destination) + { + if (_intermediaryTexture == null + || _intermediaryTexture.Info.Width != width + || _intermediaryTexture.Info.Height != height + || !_intermediaryTexture.Info.Equals(view.Info)) + { + var originalInfo = view.Info; + + var swapRB = originalInfo.Format.IsBgr() && originalInfo.SwizzleR == SwizzleComponent.Red; + + var info = new TextureCreateInfo( + width, + height, + originalInfo.Depth, + originalInfo.Levels, + originalInfo.Samples, + originalInfo.BlockWidth, + originalInfo.BlockHeight, + originalInfo.BytesPerPixel, + originalInfo.Format, + originalInfo.DepthStencilMode, + originalInfo.Target, + swapRB ? originalInfo.SwizzleB : originalInfo.SwizzleR, + originalInfo.SwizzleG, + swapRB ? originalInfo.SwizzleR : originalInfo.SwizzleB, + originalInfo.SwizzleA); + _intermediaryTexture?.Dispose(); + _intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView; + } + + Span viewports = stackalloc GAL.Viewport[1]; + Span> scissors = stackalloc Rectangle[1]; + + viewports[0] = new GAL.Viewport( + new Rectangle(0, 0, view.Width, view.Height), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + scissors[0] = new Rectangle(0, 0, view.Width, view.Height); + + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetProgram(_scalingProgram); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _sampler); + + float srcWidth = Math.Abs(source.X2 - source.X1); + float srcHeight = Math.Abs(source.Y2 - source.Y1); + float scaleX = srcWidth / view.Width; + float scaleY = srcHeight / view.Height; + + ReadOnlySpan dimensionsBuffer = stackalloc float[] + { + source.X1, + source.X2, + source.Y1, + source.Y2, + destination.X1, + destination.X2, + destination.Y1, + destination.Y2, + scaleX, + scaleY + }; + + int rangeSize = dimensionsBuffer.Length * sizeof(float); + var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false); + _renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer); + + ReadOnlySpan sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)}; + var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float), false); + _renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer); + + int threadGroupWorkRegionDim = 16; + int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetImage(0, _intermediaryTexture, GAL.Format.R8G8B8A8Unorm); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.ComputeBarrier(); + + viewports[0] = new GAL.Viewport( + new Rectangle(0, 0, width, height), + ViewportSwizzle.PositiveX, + ViewportSwizzle.PositiveY, + ViewportSwizzle.PositiveZ, + ViewportSwizzle.PositiveW, + 0f, + 1f); + + scissors[0] = new Rectangle(0, 0, width, height); + + // Sharpening pass + _pipeline.SetCommandBuffer(cbs); + _pipeline.SetProgram(_sharpeningProgram); + _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) }); + var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float)); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) }); + _pipeline.SetScissors(scissors); + _pipeline.SetViewports(viewports, false); + _pipeline.SetImage(0, destinationTexture); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); + _pipeline.ComputeBarrier(); + + _pipeline.Finish(); + + _renderer.BufferManager.Delete(bufferHandle); + _renderer.BufferManager.Delete(sharpeningBufferHandle); + } + } +} \ No newline at end of file -- cgit v1.2.3