SmaaPostProcessingEffect.cs 12 KB

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