aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
diff options
context:
space:
mode:
authorEmmanuel Hansen <emmausssss@gmail.com>2023-02-27 21:11:55 +0000
committerGitHub <noreply@github.com>2023-02-27 18:11:55 -0300
commit80b497213981512e9ba1a629bcd5e2c519d2e566 (patch)
tree0b0e8f074be1486402457ba17c941ceca1e4acff /Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
parent5d85468302dd21a93ac141abfb7b8749b938dc9a (diff)
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
Diffstat (limited to 'Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs')
-rw-r--r--Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs208
1 files changed, 208 insertions, 0 deletions
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<int>(),
+ new[] { 1 },
+ new[] { 0 });
+
+ var sharpeningBindings = new ShaderBindings(
+ new[] { 2, 3, 4 },
+ Array.Empty<int>(),
+ 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<DisposableImageView> 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<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
+ Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
+
+ viewports[0] = new GAL.Viewport(
+ new Rectangle<float>(0, 0, view.Width, view.Height),
+ ViewportSwizzle.PositiveX,
+ ViewportSwizzle.PositiveY,
+ ViewportSwizzle.PositiveZ,
+ ViewportSwizzle.PositiveW,
+ 0f,
+ 1f);
+
+ scissors[0] = new Rectangle<int>(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<float> 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<float> 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<float>(0, 0, width, height),
+ ViewportSwizzle.PositiveX,
+ ViewportSwizzle.PositiveY,
+ ViewportSwizzle.PositiveZ,
+ ViewportSwizzle.PositiveW,
+ 0f,
+ 1f);
+
+ scissors[0] = new Rectangle<int>(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