HelperShader.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. using Ryujinx.Graphics.GAL;
  2. using Ryujinx.Graphics.Shader;
  3. using Ryujinx.Graphics.Shader.Translation;
  4. using Ryujinx.Graphics.Vulkan.Shaders;
  5. using Silk.NET.Vulkan;
  6. using System;
  7. using System.Collections.Generic;
  8. using VkFormat = Silk.NET.Vulkan.Format;
  9. namespace Ryujinx.Graphics.Vulkan
  10. {
  11. class HelperShader : IDisposable
  12. {
  13. private readonly PipelineHelperShader _pipeline;
  14. private readonly ISampler _samplerLinear;
  15. private readonly ISampler _samplerNearest;
  16. private readonly IProgram _programColorBlit;
  17. private readonly IProgram _programColorBlitClearAlpha;
  18. private readonly IProgram _programColorClear;
  19. private readonly IProgram _programStrideChange;
  20. public HelperShader(VulkanRenderer gd, Device device)
  21. {
  22. _pipeline = new PipelineHelperShader(gd, device);
  23. _pipeline.Initialize();
  24. _samplerLinear = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
  25. _samplerNearest = gd.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
  26. var vertexBindings = new ShaderBindings(
  27. new[] { 1 },
  28. Array.Empty<int>(),
  29. Array.Empty<int>(),
  30. Array.Empty<int>());
  31. var fragmentBindings = new ShaderBindings(
  32. Array.Empty<int>(),
  33. Array.Empty<int>(),
  34. new[] { 0 },
  35. Array.Empty<int>());
  36. _programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
  37. {
  38. new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
  39. new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
  40. });
  41. _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
  42. {
  43. new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
  44. new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
  45. });
  46. var fragmentBindings2 = new ShaderBindings(
  47. Array.Empty<int>(),
  48. Array.Empty<int>(),
  49. Array.Empty<int>(),
  50. Array.Empty<int>());
  51. _programColorClear = gd.CreateProgramWithMinimalLayout(new[]
  52. {
  53. new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
  54. new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Spirv),
  55. });
  56. var strideChangeBindings = new ShaderBindings(
  57. new[] { 0 },
  58. new[] { 1, 2 },
  59. Array.Empty<int>(),
  60. Array.Empty<int>());
  61. _programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
  62. {
  63. new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
  64. });
  65. }
  66. public void Blit(
  67. VulkanRenderer gd,
  68. TextureView src,
  69. Auto<DisposableImageView> dst,
  70. int dstWidth,
  71. int dstHeight,
  72. VkFormat dstFormat,
  73. Extents2D srcRegion,
  74. Extents2D dstRegion,
  75. bool linearFilter,
  76. bool clearAlpha = false)
  77. {
  78. gd.FlushAllCommands();
  79. using var cbs = gd.CommandBufferPool.Rent();
  80. Blit(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha);
  81. }
  82. public void Blit(
  83. VulkanRenderer gd,
  84. CommandBufferScoped cbs,
  85. TextureView src,
  86. Auto<DisposableImageView> dst,
  87. int dstWidth,
  88. int dstHeight,
  89. VkFormat dstFormat,
  90. Extents2D srcRegion,
  91. Extents2D dstRegion,
  92. bool linearFilter,
  93. bool clearAlpha = false)
  94. {
  95. _pipeline.SetCommandBuffer(cbs);
  96. const int RegionBufferSize = 16;
  97. var sampler = linearFilter ? _samplerLinear : _samplerNearest;
  98. _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler);
  99. Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
  100. region[0] = (float)srcRegion.X1 / src.Width;
  101. region[1] = (float)srcRegion.X2 / src.Width;
  102. region[2] = (float)srcRegion.Y1 / src.Height;
  103. region[3] = (float)srcRegion.Y2 / src.Height;
  104. if (dstRegion.X1 > dstRegion.X2)
  105. {
  106. (region[0], region[1]) = (region[1], region[0]);
  107. }
  108. if (dstRegion.Y1 > dstRegion.Y2)
  109. {
  110. (region[2], region[3]) = (region[3], region[2]);
  111. }
  112. var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
  113. gd.BufferManager.SetData<float>(bufferHandle, 0, region);
  114. Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
  115. bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize);
  116. _pipeline.SetUniformBuffers(1, bufferRanges);
  117. Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
  118. var rect = new Rectangle<float>(
  119. MathF.Min(dstRegion.X1, dstRegion.X2),
  120. MathF.Min(dstRegion.Y1, dstRegion.Y2),
  121. MathF.Abs(dstRegion.X2 - dstRegion.X1),
  122. MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
  123. viewports[0] = new GAL.Viewport(
  124. rect,
  125. ViewportSwizzle.PositiveX,
  126. ViewportSwizzle.PositiveY,
  127. ViewportSwizzle.PositiveZ,
  128. ViewportSwizzle.PositiveW,
  129. 0f,
  130. 1f);
  131. Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
  132. scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
  133. _pipeline.SetProgram(clearAlpha ? _programColorBlitClearAlpha : _programColorBlit);
  134. _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
  135. _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
  136. _pipeline.SetScissors(scissors);
  137. if (clearAlpha)
  138. {
  139. _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
  140. }
  141. _pipeline.SetViewports(viewports, false);
  142. _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
  143. _pipeline.Draw(4, 1, 0, 0);
  144. _pipeline.Finish(gd, cbs);
  145. gd.BufferManager.Delete(bufferHandle);
  146. }
  147. public void Clear(
  148. VulkanRenderer gd,
  149. Auto<DisposableImageView> dst,
  150. ReadOnlySpan<float> clearColor,
  151. uint componentMask,
  152. int dstWidth,
  153. int dstHeight,
  154. VkFormat dstFormat,
  155. Rectangle<int> scissor)
  156. {
  157. const int ClearColorBufferSize = 16;
  158. gd.FlushAllCommands();
  159. using var cbs = gd.CommandBufferPool.Rent();
  160. _pipeline.SetCommandBuffer(cbs);
  161. var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize, false);
  162. gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
  163. Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
  164. bufferRanges[0] = new BufferRange(bufferHandle, 0, ClearColorBufferSize);
  165. _pipeline.SetUniformBuffers(1, bufferRanges);
  166. Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
  167. viewports[0] = new GAL.Viewport(
  168. new Rectangle<float>(0, 0, dstWidth, dstHeight),
  169. ViewportSwizzle.PositiveX,
  170. ViewportSwizzle.PositiveY,
  171. ViewportSwizzle.PositiveZ,
  172. ViewportSwizzle.PositiveW,
  173. 0f,
  174. 1f);
  175. Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
  176. scissors[0] = scissor;
  177. _pipeline.SetProgram(_programColorClear);
  178. _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
  179. _pipeline.SetRenderTargetColorMasks(new uint[] { componentMask });
  180. _pipeline.SetViewports(viewports, false);
  181. _pipeline.SetScissors(scissors);
  182. _pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
  183. _pipeline.Draw(4, 1, 0, 0);
  184. _pipeline.Finish();
  185. gd.BufferManager.Delete(bufferHandle);
  186. }
  187. public void DrawTexture(
  188. VulkanRenderer gd,
  189. PipelineBase pipeline,
  190. TextureView src,
  191. ISampler srcSampler,
  192. Extents2DF srcRegion,
  193. Extents2DF dstRegion)
  194. {
  195. const int RegionBufferSize = 16;
  196. pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler);
  197. Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
  198. region[0] = srcRegion.X1 / src.Width;
  199. region[1] = srcRegion.X2 / src.Width;
  200. region[2] = srcRegion.Y1 / src.Height;
  201. region[3] = srcRegion.Y2 / src.Height;
  202. if (dstRegion.X1 > dstRegion.X2)
  203. {
  204. (region[0], region[1]) = (region[1], region[0]);
  205. }
  206. if (dstRegion.Y1 > dstRegion.Y2)
  207. {
  208. (region[2], region[3]) = (region[3], region[2]);
  209. }
  210. var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize, false);
  211. gd.BufferManager.SetData<float>(bufferHandle, 0, region);
  212. Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
  213. bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize);
  214. pipeline.SetUniformBuffers(1, bufferRanges);
  215. Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
  216. var rect = new Rectangle<float>(
  217. MathF.Min(dstRegion.X1, dstRegion.X2),
  218. MathF.Min(dstRegion.Y1, dstRegion.Y2),
  219. MathF.Abs(dstRegion.X2 - dstRegion.X1),
  220. MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
  221. viewports[0] = new GAL.Viewport(
  222. rect,
  223. ViewportSwizzle.PositiveX,
  224. ViewportSwizzle.PositiveY,
  225. ViewportSwizzle.PositiveZ,
  226. ViewportSwizzle.PositiveW,
  227. 0f,
  228. 1f);
  229. Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
  230. pipeline.SetProgram(_programColorBlit);
  231. pipeline.SetViewports(viewports, false);
  232. pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
  233. pipeline.Draw(4, 1, 0, 0);
  234. gd.BufferManager.Delete(bufferHandle);
  235. }
  236. public unsafe void ConvertI8ToI16(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
  237. {
  238. ChangeStride(gd, cbs, src, dst, srcOffset, size, 1, 2);
  239. }
  240. public unsafe void ChangeStride(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size, int stride, int newStride)
  241. {
  242. bool supportsUint8 = gd.Capabilities.SupportsShaderInt8;
  243. int elems = size / stride;
  244. int newSize = elems * newStride;
  245. var srcBufferAuto = src.GetBuffer();
  246. var dstBufferAuto = dst.GetBuffer();
  247. var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value;
  248. var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value;
  249. var access = supportsUint8 ? AccessFlags.AccessShaderWriteBit : AccessFlags.AccessTransferWriteBit;
  250. var stage = supportsUint8 ? PipelineStageFlags.PipelineStageComputeShaderBit : PipelineStageFlags.PipelineStageTransferBit;
  251. BufferHolder.InsertBufferBarrier(
  252. gd,
  253. cbs.CommandBuffer,
  254. dstBuffer,
  255. BufferHolder.DefaultAccessFlags,
  256. access,
  257. PipelineStageFlags.PipelineStageAllCommandsBit,
  258. stage,
  259. 0,
  260. newSize);
  261. if (supportsUint8)
  262. {
  263. const int ParamsBufferSize = 16;
  264. Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
  265. shaderParams[0] = stride;
  266. shaderParams[1] = newStride;
  267. shaderParams[2] = size;
  268. shaderParams[3] = srcOffset;
  269. var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
  270. gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
  271. _pipeline.SetCommandBuffer(cbs);
  272. Span<BufferRange> cbRanges = stackalloc BufferRange[1];
  273. cbRanges[0] = new BufferRange(bufferHandle, 0, ParamsBufferSize);
  274. _pipeline.SetUniformBuffers(0, cbRanges);
  275. Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
  276. sbRanges[0] = srcBufferAuto;
  277. sbRanges[1] = dstBufferAuto;
  278. _pipeline.SetStorageBuffers(1, sbRanges);
  279. _pipeline.SetProgram(_programStrideChange);
  280. _pipeline.DispatchCompute(1, 1, 1);
  281. gd.BufferManager.Delete(bufferHandle);
  282. _pipeline.Finish(gd, cbs);
  283. }
  284. else
  285. {
  286. gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
  287. var bufferCopy = new BufferCopy[elems];
  288. for (ulong i = 0; i < (ulong)elems; i++)
  289. {
  290. bufferCopy[i] = new BufferCopy((ulong)srcOffset + i * (ulong)stride, i * (ulong)newStride, (ulong)stride);
  291. }
  292. fixed (BufferCopy* pBufferCopy = bufferCopy)
  293. {
  294. gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)elems, pBufferCopy);
  295. }
  296. }
  297. BufferHolder.InsertBufferBarrier(
  298. gd,
  299. cbs.CommandBuffer,
  300. dstBuffer,
  301. access,
  302. BufferHolder.DefaultAccessFlags,
  303. stage,
  304. PipelineStageFlags.PipelineStageAllCommandsBit,
  305. 0,
  306. newSize);
  307. }
  308. public unsafe void ConvertIndexBuffer(VulkanRenderer gd,
  309. CommandBufferScoped cbs,
  310. BufferHolder src,
  311. BufferHolder dst,
  312. IndexBufferPattern pattern,
  313. int indexSize,
  314. int srcOffset,
  315. int indexCount)
  316. {
  317. int convertedCount = pattern.GetConvertedCount(indexCount);
  318. int outputIndexSize = 4;
  319. // TODO: Do this with a compute shader?
  320. var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
  321. var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
  322. gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
  323. var bufferCopy = new List<BufferCopy>();
  324. int outputOffset = 0;
  325. // Try to merge copies of adjacent indices to reduce copy count.
  326. int sequenceStart = 0;
  327. int sequenceLength = 0;
  328. foreach (var index in pattern.GetIndexMapping(indexCount))
  329. {
  330. if (sequenceLength > 0)
  331. {
  332. if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize)
  333. {
  334. sequenceLength++;
  335. continue;
  336. }
  337. // Commit the copy so far.
  338. bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
  339. outputOffset += outputIndexSize * sequenceLength;
  340. }
  341. sequenceStart = index;
  342. sequenceLength = 1;
  343. }
  344. if (sequenceLength > 0)
  345. {
  346. // Commit final pending copy.
  347. bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
  348. }
  349. var bufferCopyArray = bufferCopy.ToArray();
  350. BufferHolder.InsertBufferBarrier(
  351. gd,
  352. cbs.CommandBuffer,
  353. dstBuffer,
  354. BufferHolder.DefaultAccessFlags,
  355. AccessFlags.AccessTransferWriteBit,
  356. PipelineStageFlags.PipelineStageAllCommandsBit,
  357. PipelineStageFlags.PipelineStageTransferBit,
  358. 0,
  359. convertedCount * outputIndexSize);
  360. fixed (BufferCopy* pBufferCopy = bufferCopyArray)
  361. {
  362. gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy);
  363. }
  364. BufferHolder.InsertBufferBarrier(
  365. gd,
  366. cbs.CommandBuffer,
  367. dstBuffer,
  368. AccessFlags.AccessTransferWriteBit,
  369. BufferHolder.DefaultAccessFlags,
  370. PipelineStageFlags.PipelineStageTransferBit,
  371. PipelineStageFlags.PipelineStageAllCommandsBit,
  372. 0,
  373. convertedCount * outputIndexSize);
  374. }
  375. protected virtual void Dispose(bool disposing)
  376. {
  377. if (disposing)
  378. {
  379. _programColorBlitClearAlpha.Dispose();
  380. _programColorBlit.Dispose();
  381. _samplerNearest.Dispose();
  382. _samplerLinear.Dispose();
  383. _pipeline.Dispose();
  384. }
  385. }
  386. public void Dispose()
  387. {
  388. Dispose(true);
  389. }
  390. }
  391. }