SmaaPostProcessingEffect.cs
1 using OpenTK.Graphics.OpenGL; 2 using Ryujinx.Common; 3 using Ryujinx.Graphics.GAL; 4 using Ryujinx.Graphics.OpenGL.Image; 5 using System; 6 7 namespace Ryujinx.Graphics.OpenGL.Effects.Smaa 8 { 9 internal partial class SmaaPostProcessingEffect : IPostProcessingEffect 10 { 11 public const int AreaWidth = 160; 12 public const int AreaHeight = 560; 13 public const int SearchWidth = 64; 14 public const int SearchHeight = 16; 15 16 private readonly OpenGLRenderer _renderer; 17 private TextureStorage _outputTexture; 18 private TextureStorage _searchTexture; 19 private TextureStorage _areaTexture; 20 private int[] _edgeShaderPrograms; 21 private int[] _blendShaderPrograms; 22 private int[] _neighbourShaderPrograms; 23 private TextureStorage _edgeOutputTexture; 24 private TextureStorage _blendOutputTexture; 25 private readonly string[] _qualities; 26 private int _inputUniform; 27 private int _outputUniform; 28 private int _samplerAreaUniform; 29 private int _samplerSearchUniform; 30 private int _samplerBlendUniform; 31 private int _resolutionUniform; 32 private int _quality = 1; 33 34 public int Quality 35 { 36 get => _quality; 37 set 38 { 39 _quality = Math.Clamp(value, 0, _qualities.Length - 1); 40 } 41 } 42 public SmaaPostProcessingEffect(OpenGLRenderer renderer, int quality) 43 { 44 _renderer = renderer; 45 46 _edgeShaderPrograms = Array.Empty<int>(); 47 _blendShaderPrograms = Array.Empty<int>(); 48 _neighbourShaderPrograms = Array.Empty<int>(); 49 50 _qualities = new string[] { "SMAA_PRESET_LOW", "SMAA_PRESET_MEDIUM", "SMAA_PRESET_HIGH", "SMAA_PRESET_ULTRA" }; 51 52 Quality = quality; 53 54 Initialize(); 55 } 56 57 public void Dispose() 58 { 59 _searchTexture?.Dispose(); 60 _areaTexture?.Dispose(); 61 _outputTexture?.Dispose(); 62 _edgeOutputTexture?.Dispose(); 63 _blendOutputTexture?.Dispose(); 64 65 DeleteShaders(); 66 } 67 68 private void DeleteShaders() 69 { 70 for (int i = 0; i < _edgeShaderPrograms.Length; i++) 71 { 72 GL.DeleteProgram(_edgeShaderPrograms[i]); 73 GL.DeleteProgram(_blendShaderPrograms[i]); 74 GL.DeleteProgram(_neighbourShaderPrograms[i]); 75 } 76 } 77 78 private unsafe void RecreateShaders(int width, int height) 79 { 80 string baseShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl"); 81 var pixelSizeDefine = $"#define SMAA_RT_METRICS float4(1.0 / {width}.0, 1.0 / {height}.0, {width}, {height}) \n"; 82 83 _edgeShaderPrograms = new int[_qualities.Length]; 84 _blendShaderPrograms = new int[_qualities.Length]; 85 _neighbourShaderPrograms = new int[_qualities.Length]; 86 87 for (int i = 0; i < +_edgeShaderPrograms.Length; i++) 88 { 89 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}"; 90 91 var edgeShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl"); 92 var blendShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl"); 93 var neighbourShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl"); 94 95 var shaders = new string[] { presets, edgeShaderData }; 96 var edgeProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader); 97 98 shaders[1] = blendShaderData; 99 var blendProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader); 100 101 shaders[1] = neighbourShaderData; 102 var neighbourProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader); 103 104 _edgeShaderPrograms[i] = edgeProgram; 105 _blendShaderPrograms[i] = blendProgram; 106 _neighbourShaderPrograms[i] = neighbourProgram; 107 } 108 109 _inputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "inputTexture"); 110 _outputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "imgOutput"); 111 _samplerAreaUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerArea"); 112 _samplerSearchUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerSearch"); 113 _samplerBlendUniform = GL.GetUniformLocation(_neighbourShaderPrograms[0], "samplerBlend"); 114 _resolutionUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "invResolution"); 115 } 116 117 private void Initialize() 118 { 119 var areaInfo = new TextureCreateInfo(AreaWidth, 120 AreaHeight, 121 1, 122 1, 123 1, 124 1, 125 1, 126 1, 127 Format.R8G8Unorm, 128 DepthStencilMode.Depth, 129 Target.Texture2D, 130 SwizzleComponent.Red, 131 SwizzleComponent.Green, 132 SwizzleComponent.Blue, 133 SwizzleComponent.Alpha); 134 135 var searchInfo = new TextureCreateInfo(SearchWidth, 136 SearchHeight, 137 1, 138 1, 139 1, 140 1, 141 1, 142 1, 143 Format.R8Unorm, 144 DepthStencilMode.Depth, 145 Target.Texture2D, 146 SwizzleComponent.Red, 147 SwizzleComponent.Green, 148 SwizzleComponent.Blue, 149 SwizzleComponent.Alpha); 150 151 _areaTexture = new TextureStorage(_renderer, areaInfo); 152 _searchTexture = new TextureStorage(_renderer, searchInfo); 153 154 var areaTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin"); 155 var searchTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin"); 156 157 var areaView = _areaTexture.CreateDefaultView(); 158 var searchView = _searchTexture.CreateDefaultView(); 159 160 areaView.SetData(areaTexture); 161 searchView.SetData(searchTexture); 162 } 163 164 public TextureView Run(TextureView view, int width, int height) 165 { 166 if (_outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height) 167 { 168 _outputTexture?.Dispose(); 169 _outputTexture = new TextureStorage(_renderer, view.Info); 170 _outputTexture.CreateDefaultView(); 171 _edgeOutputTexture = new TextureStorage(_renderer, view.Info); 172 _edgeOutputTexture.CreateDefaultView(); 173 _blendOutputTexture = new TextureStorage(_renderer, view.Info); 174 _blendOutputTexture.CreateDefaultView(); 175 176 DeleteShaders(); 177 178 RecreateShaders(view.Width, view.Height); 179 } 180 181 var textureView = _outputTexture.CreateView(view.Info, 0, 0) as TextureView; 182 var edgeOutput = _edgeOutputTexture.DefaultView as TextureView; 183 var blendOutput = _blendOutputTexture.DefaultView as TextureView; 184 var areaTexture = _areaTexture.DefaultView as TextureView; 185 var searchTexture = _searchTexture.DefaultView as TextureView; 186 187 var previousFramebuffer = GL.GetInteger(GetPName.FramebufferBinding); 188 int previousUnit = GL.GetInteger(GetPName.ActiveTexture); 189 GL.ActiveTexture(TextureUnit.Texture0); 190 int previousTextureBinding0 = GL.GetInteger(GetPName.TextureBinding2D); 191 GL.ActiveTexture(TextureUnit.Texture1); 192 int previousTextureBinding1 = GL.GetInteger(GetPName.TextureBinding2D); 193 GL.ActiveTexture(TextureUnit.Texture2); 194 int previousTextureBinding2 = GL.GetInteger(GetPName.TextureBinding2D); 195 196 var framebuffer = new Framebuffer(); 197 framebuffer.Bind(); 198 framebuffer.AttachColor(0, edgeOutput); 199 GL.Clear(ClearBufferMask.ColorBufferBit); 200 GL.ClearColor(0, 0, 0, 0); 201 framebuffer.AttachColor(0, blendOutput); 202 GL.Clear(ClearBufferMask.ColorBufferBit); 203 GL.ClearColor(0, 0, 0, 0); 204 205 GL.BindFramebuffer(FramebufferTarget.Framebuffer, previousFramebuffer); 206 207 framebuffer.Dispose(); 208 209 var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); 210 var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); 211 212 int previousProgram = GL.GetInteger(GetPName.CurrentProgram); 213 GL.BindImageTexture(0, edgeOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); 214 GL.UseProgram(_edgeShaderPrograms[Quality]); 215 view.Bind(0); 216 GL.Uniform1(_inputUniform, 0); 217 GL.Uniform1(_outputUniform, 0); 218 GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height); 219 GL.DispatchCompute(dispatchX, dispatchY, 1); 220 GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); 221 222 GL.BindImageTexture(0, blendOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); 223 GL.UseProgram(_blendShaderPrograms[Quality]); 224 edgeOutput.Bind(0); 225 areaTexture.Bind(1); 226 searchTexture.Bind(2); 227 GL.Uniform1(_inputUniform, 0); 228 GL.Uniform1(_outputUniform, 0); 229 GL.Uniform1(_samplerAreaUniform, 1); 230 GL.Uniform1(_samplerSearchUniform, 2); 231 GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height); 232 GL.DispatchCompute(dispatchX, dispatchY, 1); 233 GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); 234 235 GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8); 236 GL.UseProgram(_neighbourShaderPrograms[Quality]); 237 view.Bind(0); 238 blendOutput.Bind(1); 239 GL.Uniform1(_inputUniform, 0); 240 GL.Uniform1(_outputUniform, 0); 241 GL.Uniform1(_samplerBlendUniform, 1); 242 GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height); 243 GL.DispatchCompute(dispatchX, dispatchY, 1); 244 GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit); 245 246 (_renderer.Pipeline as Pipeline).RestoreImages1And2(); 247 248 GL.UseProgram(previousProgram); 249 250 GL.ActiveTexture(TextureUnit.Texture0); 251 GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding0); 252 GL.ActiveTexture(TextureUnit.Texture1); 253 GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding1); 254 GL.ActiveTexture(TextureUnit.Texture2); 255 GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding2); 256 257 GL.ActiveTexture((TextureUnit)previousUnit); 258 259 return textureView; 260 } 261 } 262 }