aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs')
-rw-r--r--Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs261
1 files changed, 261 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs b/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs
new file mode 100644
index 00000000..1ad300c8
--- /dev/null
+++ b/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs
@@ -0,0 +1,261 @@
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Common;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.OpenGL.Image;
+using System;
+
+namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
+{
+ internal partial class SmaaPostProcessingEffect : IPostProcessingEffect
+ {
+ public const int AreaWidth = 160;
+ public const int AreaHeight = 560;
+ public const int SearchWidth = 64;
+ public const int SearchHeight = 16;
+
+ private readonly OpenGLRenderer _renderer;
+ private TextureStorage _outputTexture;
+ private TextureStorage _searchTexture;
+ private TextureStorage _areaTexture;
+ private int[] _edgeShaderPrograms;
+ private int[] _blendShaderPrograms;
+ private int[] _neighbourShaderPrograms;
+ private TextureStorage _edgeOutputTexture;
+ private TextureStorage _blendOutputTexture;
+ private string[] _qualities;
+ private int _inputUniform;
+ private int _outputUniform;
+ private int _samplerAreaUniform;
+ private int _samplerSearchUniform;
+ private int _samplerBlendUniform;
+ private int _resolutionUniform;
+ private int _quality = 1;
+
+ public int Quality
+ {
+ get => _quality; set
+ {
+ _quality = Math.Clamp(value, 0, _qualities.Length - 1);
+ }
+ }
+ public SmaaPostProcessingEffect(OpenGLRenderer renderer, int quality)
+ {
+ _renderer = renderer;
+
+ _edgeShaderPrograms = Array.Empty<int>();
+ _blendShaderPrograms = Array.Empty<int>();
+ _neighbourShaderPrograms = Array.Empty<int>();
+
+ _qualities = new string[] { "SMAA_PRESET_LOW", "SMAA_PRESET_MEDIUM", "SMAA_PRESET_HIGH", "SMAA_PRESET_ULTRA" };
+
+ Quality = quality;
+
+ Initialize();
+ }
+
+ public void Dispose()
+ {
+ _searchTexture?.Dispose();
+ _areaTexture?.Dispose();
+ _outputTexture?.Dispose();
+ _edgeOutputTexture?.Dispose();
+ _blendOutputTexture?.Dispose();
+
+ DeleteShaders();
+ }
+
+ private void DeleteShaders()
+ {
+ for (int i = 0; i < _edgeShaderPrograms.Length; i++)
+ {
+ GL.DeleteProgram(_edgeShaderPrograms[i]);
+ GL.DeleteProgram(_blendShaderPrograms[i]);
+ GL.DeleteProgram(_neighbourShaderPrograms[i]);
+ }
+ }
+
+ private unsafe void RecreateShaders(int width, int height)
+ {
+ string baseShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl");
+ var pixelSizeDefine = $"#define SMAA_RT_METRICS float4(1.0 / {width}.0, 1.0 / {height}.0, {width}, {height}) \n";
+
+ _edgeShaderPrograms = new int[_qualities.Length];
+ _blendShaderPrograms = new int[_qualities.Length];
+ _neighbourShaderPrograms = new int[_qualities.Length];
+
+ for (int i = 0; i < +_edgeShaderPrograms.Length; i++)
+ {
+ var presets = $"#version 430 core \n#define {_qualities[i]} 1 \n{pixelSizeDefine}#define SMAA_GLSL_4 1 \nlayout (local_size_x = 16, local_size_y = 16) in;\n{baseShader}";
+
+ var edgeShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl");
+ var blendShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl");
+ var neighbourShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl");
+
+ var shaders = new string[] { presets, edgeShaderData };
+ var edgeProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
+
+ shaders[1] = blendShaderData;
+ var blendProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
+
+ shaders[1] = neighbourShaderData;
+ var neighbourProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
+
+ _edgeShaderPrograms[i] = edgeProgram;
+ _blendShaderPrograms[i] = blendProgram;
+ _neighbourShaderPrograms[i] = neighbourProgram;
+ }
+
+ _inputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "inputTexture");
+ _outputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "imgOutput");
+ _samplerAreaUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerArea");
+ _samplerSearchUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerSearch");
+ _samplerBlendUniform = GL.GetUniformLocation(_neighbourShaderPrograms[0], "samplerBlend");
+ _resolutionUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "invResolution");
+ }
+
+ private void Initialize()
+ {
+ var areaInfo = new TextureCreateInfo(AreaWidth,
+ AreaHeight,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ Format.R8G8Unorm,
+ DepthStencilMode.Depth,
+ Target.Texture2D,
+ SwizzleComponent.Red,
+ SwizzleComponent.Green,
+ SwizzleComponent.Blue,
+ SwizzleComponent.Alpha);
+
+ var searchInfo = new TextureCreateInfo(SearchWidth,
+ SearchHeight,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ Format.R8Unorm,
+ DepthStencilMode.Depth,
+ Target.Texture2D,
+ SwizzleComponent.Red,
+ SwizzleComponent.Green,
+ SwizzleComponent.Blue,
+ SwizzleComponent.Alpha);
+
+ _areaTexture = new TextureStorage(_renderer, areaInfo, 1);
+ _searchTexture = new TextureStorage(_renderer, searchInfo, 1);
+
+ var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin");
+ var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin");
+
+ var areaView = _areaTexture.CreateDefaultView();
+ var searchView = _searchTexture.CreateDefaultView();
+
+ areaView.SetData(areaTexture);
+ searchView.SetData(searchTexture);
+ }
+
+ public TextureView Run(TextureView view, int width, int height)
+ {
+ if (_outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height)
+ {
+ _outputTexture?.Dispose();
+ _outputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
+ _outputTexture.CreateDefaultView();
+ _edgeOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
+ _edgeOutputTexture.CreateDefaultView();
+ _blendOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
+ _blendOutputTexture.CreateDefaultView();
+
+ DeleteShaders();
+
+ RecreateShaders(view.Width, view.Height);
+ }
+
+ var textureView = _outputTexture.CreateView(view.Info, 0, 0) as TextureView;
+ var edgeOutput = _edgeOutputTexture.DefaultView as TextureView;
+ var blendOutput = _blendOutputTexture.DefaultView as TextureView;
+ var areaTexture = _areaTexture.DefaultView as TextureView;
+ var searchTexture = _searchTexture.DefaultView as TextureView;
+
+ var previousFramebuffer = GL.GetInteger(GetPName.FramebufferBinding);
+ int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
+ GL.ActiveTexture(TextureUnit.Texture0);
+ int previousTextureBinding0 = GL.GetInteger(GetPName.TextureBinding2D);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ int previousTextureBinding1 = GL.GetInteger(GetPName.TextureBinding2D);
+ GL.ActiveTexture(TextureUnit.Texture2);
+ int previousTextureBinding2 = GL.GetInteger(GetPName.TextureBinding2D);
+
+ var framebuffer = new Framebuffer();
+ framebuffer.Bind();
+ framebuffer.AttachColor(0, edgeOutput);
+ GL.Clear(ClearBufferMask.ColorBufferBit);
+ GL.ClearColor(0, 0, 0, 0);
+ framebuffer.AttachColor(0, blendOutput);
+ GL.Clear(ClearBufferMask.ColorBufferBit);
+ GL.ClearColor(0, 0, 0, 0);
+
+ GL.BindFramebuffer(FramebufferTarget.Framebuffer, previousFramebuffer);
+
+ framebuffer.Dispose();
+
+ var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
+ var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
+
+ int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
+ GL.BindImageTexture(0, edgeOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
+ GL.UseProgram(_edgeShaderPrograms[Quality]);
+ view.Bind(0);
+ GL.Uniform1(_inputUniform, 0);
+ GL.Uniform1(_outputUniform, 0);
+ GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
+ GL.DispatchCompute(dispatchX, dispatchY, 1);
+ GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
+
+ GL.BindImageTexture(0, blendOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
+ GL.UseProgram(_blendShaderPrograms[Quality]);
+ edgeOutput.Bind(0);
+ areaTexture.Bind(1);
+ searchTexture.Bind(2);
+ GL.Uniform1(_inputUniform, 0);
+ GL.Uniform1(_outputUniform, 0);
+ GL.Uniform1(_samplerAreaUniform, 1);
+ GL.Uniform1(_samplerSearchUniform, 2);
+ GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
+ GL.DispatchCompute(dispatchX, dispatchY, 1);
+ GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
+
+ GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
+ GL.UseProgram(_neighbourShaderPrograms[Quality]);
+ view.Bind(0);
+ blendOutput.Bind(1);
+ GL.Uniform1(_inputUniform, 0);
+ GL.Uniform1(_outputUniform, 0);
+ GL.Uniform1(_samplerBlendUniform, 1);
+ GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
+ GL.DispatchCompute(dispatchX, dispatchY, 1);
+ GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
+
+ (_renderer.Pipeline as Pipeline).RestoreImages1And2();
+
+ GL.UseProgram(previousProgram);
+
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding0);
+ GL.ActiveTexture(TextureUnit.Texture1);
+ GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding1);
+ GL.ActiveTexture(TextureUnit.Texture2);
+ GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding2);
+
+ GL.ActiveTexture((TextureUnit)previousUnit);
+
+ return textureView;
+ }
+ }
+}