SmaaPostProcessingEffect.cs 11 KB

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