/ src / Ryujinx.Graphics.OpenGL / Effects / SmaaPostProcessingEffect.cs
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  }