SmaaPostProcessingEffect.cs
1 using Ryujinx.Common; 2 using Ryujinx.Graphics.GAL; 3 using Ryujinx.Graphics.Shader; 4 using Ryujinx.Graphics.Shader.Translation; 5 using Silk.NET.Vulkan; 6 using System; 7 using Format = Ryujinx.Graphics.GAL.Format; 8 using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; 9 10 namespace Ryujinx.Graphics.Vulkan.Effects 11 { 12 internal class SmaaPostProcessingEffect : IPostProcessingEffect 13 { 14 public const int AreaWidth = 160; 15 public const int AreaHeight = 560; 16 public const int SearchWidth = 64; 17 public const int SearchHeight = 16; 18 19 private readonly VulkanRenderer _renderer; 20 private ISampler _samplerLinear; 21 private SmaaConstants _specConstants; 22 private ShaderCollection _edgeProgram; 23 private ShaderCollection _blendProgram; 24 private ShaderCollection _neighbourProgram; 25 26 private PipelineHelperShader _pipeline; 27 28 private TextureView _outputTexture; 29 private TextureView _edgeOutputTexture; 30 private TextureView _blendOutputTexture; 31 private TextureView _areaTexture; 32 private TextureView _searchTexture; 33 private Device _device; 34 private bool _recreatePipelines; 35 private int _quality; 36 37 public SmaaPostProcessingEffect(VulkanRenderer renderer, Device device, int quality) 38 { 39 _device = device; 40 _renderer = renderer; 41 _quality = quality; 42 43 Initialize(); 44 } 45 46 public int Quality 47 { 48 get => _quality; 49 set 50 { 51 _quality = value; 52 53 _recreatePipelines = true; 54 } 55 } 56 57 public void Dispose() 58 { 59 DeletePipelines(); 60 _samplerLinear?.Dispose(); 61 _outputTexture?.Dispose(); 62 _edgeOutputTexture?.Dispose(); 63 _blendOutputTexture?.Dispose(); 64 _areaTexture?.Dispose(); 65 _searchTexture?.Dispose(); 66 } 67 68 private void RecreateShaders(int width, int height) 69 { 70 _recreatePipelines = false; 71 72 DeletePipelines(); 73 _pipeline = new PipelineHelperShader(_renderer, _device); 74 75 _pipeline.Initialize(); 76 77 var edgeShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv"); 78 var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv"); 79 var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv"); 80 81 var edgeResourceLayout = new ResourceLayoutBuilder() 82 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 83 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 84 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); 85 86 var blendResourceLayout = new ResourceLayoutBuilder() 87 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 88 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 89 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) 90 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4) 91 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); 92 93 var neighbourResourceLayout = new ResourceLayoutBuilder() 94 .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2) 95 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1) 96 .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3) 97 .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build(); 98 99 _samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); 100 101 _specConstants = new SmaaConstants 102 { 103 Width = width, 104 Height = height, 105 QualityLow = Quality == 0 ? 1 : 0, 106 QualityMedium = Quality == 1 ? 1 : 0, 107 QualityHigh = Quality == 2 ? 1 : 0, 108 QualityUltra = Quality == 3 ? 1 : 0, 109 }; 110 111 var specInfo = new SpecDescription( 112 (0, SpecConstType.Int32), 113 (1, SpecConstType.Int32), 114 (2, SpecConstType.Int32), 115 (3, SpecConstType.Int32), 116 (4, SpecConstType.Float32), 117 (5, SpecConstType.Float32)); 118 119 _edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[] 120 { 121 new ShaderSource(edgeShader, ShaderStage.Compute, TargetLanguage.Spirv), 122 }, edgeResourceLayout, new[] { specInfo }); 123 124 _blendProgram = _renderer.CreateProgramWithMinimalLayout(new[] 125 { 126 new ShaderSource(blendShader, ShaderStage.Compute, TargetLanguage.Spirv), 127 }, blendResourceLayout, new[] { specInfo }); 128 129 _neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[] 130 { 131 new ShaderSource(neighbourShader, ShaderStage.Compute, TargetLanguage.Spirv), 132 }, neighbourResourceLayout, new[] { specInfo }); 133 } 134 135 public void DeletePipelines() 136 { 137 _pipeline?.Dispose(); 138 _edgeProgram?.Dispose(); 139 _blendProgram?.Dispose(); 140 _neighbourProgram?.Dispose(); 141 } 142 143 private void Initialize() 144 { 145 var areaInfo = new TextureCreateInfo(AreaWidth, 146 AreaHeight, 147 1, 148 1, 149 1, 150 1, 151 1, 152 1, 153 Format.R8G8Unorm, 154 DepthStencilMode.Depth, 155 Target.Texture2D, 156 SwizzleComponent.Red, 157 SwizzleComponent.Green, 158 SwizzleComponent.Blue, 159 SwizzleComponent.Alpha); 160 161 var searchInfo = new TextureCreateInfo(SearchWidth, 162 SearchHeight, 163 1, 164 1, 165 1, 166 1, 167 1, 168 1, 169 Format.R8Unorm, 170 DepthStencilMode.Depth, 171 Target.Texture2D, 172 SwizzleComponent.Red, 173 SwizzleComponent.Green, 174 SwizzleComponent.Blue, 175 SwizzleComponent.Alpha); 176 177 var areaTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin"); 178 var searchTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin"); 179 180 _areaTexture = _renderer.CreateTexture(areaInfo) as TextureView; 181 _searchTexture = _renderer.CreateTexture(searchInfo) as TextureView; 182 183 _areaTexture.SetData(areaTexture); 184 _searchTexture.SetData(searchTexture); 185 } 186 187 public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height) 188 { 189 if (_recreatePipelines || _outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height) 190 { 191 RecreateShaders(view.Width, view.Height); 192 _outputTexture?.Dispose(); 193 _edgeOutputTexture?.Dispose(); 194 _blendOutputTexture?.Dispose(); 195 196 _outputTexture = _renderer.CreateTexture(view.Info) as TextureView; 197 _edgeOutputTexture = _renderer.CreateTexture(view.Info) as TextureView; 198 _blendOutputTexture = _renderer.CreateTexture(view.Info) as TextureView; 199 } 200 201 _pipeline.SetCommandBuffer(cbs); 202 203 Clear(_edgeOutputTexture); 204 Clear(_blendOutputTexture); 205 206 _renderer.Pipeline.TextureBarrier(); 207 208 var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); 209 var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize); 210 211 // Edge pass 212 _pipeline.SetProgram(_edgeProgram); 213 _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); 214 _pipeline.Specialize(_specConstants); 215 216 ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height }; 217 int rangeSize = resolutionBuffer.Length * sizeof(float); 218 using var buffer = _renderer.BufferManager.ReserveOrCreate(_renderer, cbs, rangeSize); 219 220 buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer); 221 _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); 222 _pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format))); 223 _pipeline.DispatchCompute(dispatchX, dispatchY, 1); 224 _pipeline.ComputeBarrier(); 225 226 // Blend pass 227 _pipeline.SetProgram(_blendProgram); 228 _pipeline.Specialize(_specConstants); 229 _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear); 230 _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear); 231 _pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear); 232 _pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format))); 233 _pipeline.DispatchCompute(dispatchX, dispatchY, 1); 234 _pipeline.ComputeBarrier(); 235 236 // Neighbour pass 237 _pipeline.SetProgram(_neighbourProgram); 238 _pipeline.Specialize(_specConstants); 239 _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear); 240 _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear); 241 _pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture.GetView(FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format))); 242 _pipeline.DispatchCompute(dispatchX, dispatchY, 1); 243 _pipeline.ComputeBarrier(); 244 245 _pipeline.Finish(); 246 247 return _outputTexture; 248 } 249 250 private void Clear(TextureView texture) 251 { 252 Span<uint> colorMasks = stackalloc uint[1]; 253 254 colorMasks[0] = 0xf; 255 256 Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1]; 257 258 scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height); 259 260 _pipeline.SetRenderTarget(texture, (uint)texture.Width, (uint)texture.Height); 261 _pipeline.SetRenderTargetColorMasks(colorMasks); 262 _pipeline.SetScissors(scissors); 263 _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); 264 } 265 } 266 }