ShaderCache.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. using Ryujinx.Common.Configuration;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Gpu.Engine.Threed;
  5. using Ryujinx.Graphics.Gpu.Engine.Types;
  6. using Ryujinx.Graphics.Gpu.Image;
  7. using Ryujinx.Graphics.Gpu.Memory;
  8. using Ryujinx.Graphics.Gpu.Shader.DiskCache;
  9. using Ryujinx.Graphics.Shader;
  10. using Ryujinx.Graphics.Shader.Translation;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.IO;
  14. using System.Linq;
  15. using System.Threading;
  16. namespace Ryujinx.Graphics.Gpu.Shader
  17. {
  18. /// <summary>
  19. /// Memory cache of shader code.
  20. /// </summary>
  21. class ShaderCache : IDisposable
  22. {
  23. /// <summary>
  24. /// Default flags used on the shader translation process.
  25. /// </summary>
  26. public const TranslationFlags DefaultFlags = TranslationFlags.DebugMode;
  27. private readonly struct TranslatedShader
  28. {
  29. public readonly CachedShaderStage Shader;
  30. public readonly ShaderProgram Program;
  31. public TranslatedShader(CachedShaderStage shader, ShaderProgram program)
  32. {
  33. Shader = shader;
  34. Program = program;
  35. }
  36. }
  37. private readonly struct TranslatedShaderVertexPair
  38. {
  39. public readonly CachedShaderStage VertexA;
  40. public readonly CachedShaderStage VertexB;
  41. public readonly ShaderProgram Program;
  42. public TranslatedShaderVertexPair(CachedShaderStage vertexA, CachedShaderStage vertexB, ShaderProgram program)
  43. {
  44. VertexA = vertexA;
  45. VertexB = vertexB;
  46. Program = program;
  47. }
  48. }
  49. private readonly GpuContext _context;
  50. private readonly ShaderDumper _dumper;
  51. private readonly Dictionary<ulong, CachedShaderProgram> _cpPrograms;
  52. private readonly Dictionary<ShaderAddresses, CachedShaderProgram> _gpPrograms;
  53. private readonly struct ProgramToSave
  54. {
  55. public readonly CachedShaderProgram CachedProgram;
  56. public readonly IProgram HostProgram;
  57. public readonly byte[] BinaryCode;
  58. public ProgramToSave(CachedShaderProgram cachedProgram, IProgram hostProgram, byte[] binaryCode)
  59. {
  60. CachedProgram = cachedProgram;
  61. HostProgram = hostProgram;
  62. BinaryCode = binaryCode;
  63. }
  64. }
  65. private Queue<ProgramToSave> _programsToSaveQueue;
  66. private readonly ComputeShaderCacheHashTable _computeShaderCache;
  67. private readonly ShaderCacheHashTable _graphicsShaderCache;
  68. private readonly DiskCacheHostStorage _diskCacheHostStorage;
  69. private readonly BackgroundDiskCacheWriter _cacheWriter;
  70. /// <summary>
  71. /// Event for signalling shader cache loading progress.
  72. /// </summary>
  73. public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged;
  74. /// <summary>
  75. /// Creates a new instance of the shader cache.
  76. /// </summary>
  77. /// <param name="context">GPU context that the shader cache belongs to</param>
  78. public ShaderCache(GpuContext context)
  79. {
  80. _context = context;
  81. _dumper = new ShaderDumper();
  82. _cpPrograms = new Dictionary<ulong, CachedShaderProgram>();
  83. _gpPrograms = new Dictionary<ShaderAddresses, CachedShaderProgram>();
  84. _programsToSaveQueue = new Queue<ProgramToSave>();
  85. string diskCacheTitleId = GetDiskCachePath();
  86. _computeShaderCache = new ComputeShaderCacheHashTable();
  87. _graphicsShaderCache = new ShaderCacheHashTable();
  88. _diskCacheHostStorage = new DiskCacheHostStorage(diskCacheTitleId);
  89. if (_diskCacheHostStorage.CacheEnabled)
  90. {
  91. _cacheWriter = new BackgroundDiskCacheWriter(context, _diskCacheHostStorage);
  92. }
  93. }
  94. /// <summary>
  95. /// Gets the path where the disk cache for the current application is stored.
  96. /// </summary>
  97. private static string GetDiskCachePath()
  98. {
  99. return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
  100. ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
  101. : null;
  102. }
  103. /// <summary>
  104. /// Processes the queue of shaders that must save their binaries to the disk cache.
  105. /// </summary>
  106. public void ProcessShaderCacheQueue()
  107. {
  108. // Check to see if the binaries for previously compiled shaders are ready, and save them out.
  109. while (_programsToSaveQueue.TryPeek(out ProgramToSave programToSave))
  110. {
  111. ProgramLinkStatus result = programToSave.HostProgram.CheckProgramLink(false);
  112. if (result != ProgramLinkStatus.Incomplete)
  113. {
  114. if (result == ProgramLinkStatus.Success)
  115. {
  116. _cacheWriter.AddShader(programToSave.CachedProgram, programToSave.BinaryCode ?? programToSave.HostProgram.GetBinary());
  117. }
  118. _programsToSaveQueue.Dequeue();
  119. }
  120. else
  121. {
  122. break;
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// Initialize the cache.
  128. /// </summary>
  129. /// <param name="cancellationToken">Cancellation token to cancel the shader cache initialization process</param>
  130. internal void Initialize(CancellationToken cancellationToken)
  131. {
  132. if (_diskCacheHostStorage.CacheEnabled)
  133. {
  134. ParallelDiskCacheLoader loader = new ParallelDiskCacheLoader(
  135. _context,
  136. _graphicsShaderCache,
  137. _computeShaderCache,
  138. _diskCacheHostStorage,
  139. cancellationToken,
  140. ShaderCacheStateUpdate);
  141. loader.LoadShaders();
  142. int errorCount = loader.ErrorCount;
  143. if (errorCount != 0)
  144. {
  145. Logger.Warning?.Print(LogClass.Gpu, $"Failed to load {errorCount} shaders from the disk cache.");
  146. }
  147. }
  148. }
  149. /// <summary>
  150. /// Shader cache state update handler.
  151. /// </summary>
  152. /// <param name="state">Current state of the shader cache load process</param>
  153. /// <param name="current">Number of the current shader being processed</param>
  154. /// <param name="total">Total number of shaders to process</param>
  155. private void ShaderCacheStateUpdate(ShaderCacheState state, int current, int total)
  156. {
  157. ShaderCacheStateChanged?.Invoke(state, current, total);
  158. }
  159. /// <summary>
  160. /// Gets a compute shader from the cache.
  161. /// </summary>
  162. /// <remarks>
  163. /// This automatically translates, compiles and adds the code to the cache if not present.
  164. /// </remarks>
  165. /// <param name="channel">GPU channel</param>
  166. /// <param name="poolState">Texture pool state</param>
  167. /// <param name="computeState">Compute engine state</param>
  168. /// <param name="gpuVa">GPU virtual address of the binary shader code</param>
  169. /// <returns>Compiled compute shader code</returns>
  170. public CachedShaderProgram GetComputeShader(
  171. GpuChannel channel,
  172. GpuChannelPoolState poolState,
  173. GpuChannelComputeState computeState,
  174. ulong gpuVa)
  175. {
  176. if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
  177. {
  178. return cpShader;
  179. }
  180. if (_computeShaderCache.TryFind(channel, poolState, computeState, gpuVa, out cpShader, out byte[] cachedGuestCode))
  181. {
  182. _cpPrograms[gpuVa] = cpShader;
  183. return cpShader;
  184. }
  185. ShaderSpecializationState specState = new ShaderSpecializationState(ref computeState);
  186. GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, computeState, default, specState);
  187. GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
  188. TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
  189. TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
  190. ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
  191. IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(-1));
  192. cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
  193. _computeShaderCache.Add(cpShader);
  194. EnqueueProgramToSave(cpShader, hostProgram, shaderSourcesArray);
  195. _cpPrograms[gpuVa] = cpShader;
  196. return cpShader;
  197. }
  198. /// <summary>
  199. /// Updates the shader pipeline state based on the current GPU state.
  200. /// </summary>
  201. /// <param name="state">Current GPU 3D engine state</param>
  202. /// <param name="pipeline">Shader pipeline state to be updated</param>
  203. /// <param name="graphicsState">Current graphics state</param>
  204. /// <param name="channel">Current GPU channel</param>
  205. private void UpdatePipelineInfo(
  206. ref ThreedClassState state,
  207. ref ProgramPipelineState pipeline,
  208. GpuChannelGraphicsState graphicsState,
  209. GpuChannel channel)
  210. {
  211. channel.TextureManager.UpdateRenderTargets();
  212. var rtControl = state.RtControl;
  213. var msaaMode = state.RtMsaaMode;
  214. pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
  215. int count = rtControl.UnpackCount();
  216. for (int index = 0; index < Constants.TotalRenderTargets; index++)
  217. {
  218. int rtIndex = rtControl.UnpackPermutationIndex(index);
  219. var colorState = state.RtColorState[rtIndex];
  220. if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
  221. {
  222. pipeline.AttachmentEnable[index] = false;
  223. pipeline.AttachmentFormats[index] = Format.R8G8B8A8Unorm;
  224. }
  225. else
  226. {
  227. pipeline.AttachmentEnable[index] = true;
  228. pipeline.AttachmentFormats[index] = colorState.Format.Convert().Format;
  229. }
  230. }
  231. pipeline.DepthStencilEnable = state.RtDepthStencilEnable;
  232. pipeline.DepthStencilFormat = pipeline.DepthStencilEnable ? state.RtDepthStencilState.Format.Convert().Format : Format.D24UnormS8Uint;
  233. pipeline.VertexBufferCount = Constants.TotalVertexBuffers;
  234. pipeline.Topology = graphicsState.Topology;
  235. }
  236. /// <summary>
  237. /// Gets a graphics shader program from the shader cache.
  238. /// This includes all the specified shader stages.
  239. /// </summary>
  240. /// <remarks>
  241. /// This automatically translates, compiles and adds the code to the cache if not present.
  242. /// </remarks>
  243. /// <param name="state">GPU state</param>
  244. /// <param name="pipeline">Pipeline state</param>
  245. /// <param name="channel">GPU channel</param>
  246. /// <param name="poolState">Texture pool state</param>
  247. /// <param name="graphicsState">3D engine state</param>
  248. /// <param name="addresses">Addresses of the shaders for each stage</param>
  249. /// <returns>Compiled graphics shader code</returns>
  250. public CachedShaderProgram GetGraphicsShader(
  251. ref ThreedClassState state,
  252. ref ProgramPipelineState pipeline,
  253. GpuChannel channel,
  254. ref GpuChannelPoolState poolState,
  255. ref GpuChannelGraphicsState graphicsState,
  256. ShaderAddresses addresses)
  257. {
  258. if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
  259. {
  260. return gpShaders;
  261. }
  262. if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode))
  263. {
  264. _gpPrograms[addresses] = gpShaders;
  265. return gpShaders;
  266. }
  267. TransformFeedbackDescriptor[] transformFeedbackDescriptors = GetTransformFeedbackDescriptors(ref state);
  268. UpdatePipelineInfo(ref state, ref pipeline, graphicsState, channel);
  269. ShaderSpecializationState specState = new ShaderSpecializationState(ref graphicsState, ref pipeline, transformFeedbackDescriptors);
  270. GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, default, graphicsState, specState, transformFeedbackDescriptors);
  271. ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan();
  272. TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1];
  273. TranslatorContext nextStage = null;
  274. TargetApi api = _context.Capabilities.Api;
  275. for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
  276. {
  277. ulong gpuVa = addressesSpan[stageIndex + 1];
  278. if (gpuVa != 0)
  279. {
  280. GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState, stageIndex);
  281. TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);
  282. if (nextStage != null)
  283. {
  284. currentStage.SetNextStage(nextStage);
  285. }
  286. if (stageIndex == 0 && addresses.VertexA != 0)
  287. {
  288. translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA);
  289. }
  290. translatorContexts[stageIndex + 1] = currentStage;
  291. nextStage = currentStage;
  292. }
  293. }
  294. if (!_context.Capabilities.SupportsGeometryShader)
  295. {
  296. TryRemoveGeometryStage(translatorContexts);
  297. }
  298. CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
  299. List<ShaderSource> shaderSources = new List<ShaderSource>();
  300. TranslatorContext previousStage = null;
  301. for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
  302. {
  303. TranslatorContext currentStage = translatorContexts[stageIndex + 1];
  304. if (currentStage != null)
  305. {
  306. ShaderProgram program;
  307. if (stageIndex == 0 && translatorContexts[0] != null)
  308. {
  309. TranslatedShaderVertexPair translatedShader = TranslateShader(
  310. _dumper,
  311. channel,
  312. currentStage,
  313. translatorContexts[0],
  314. cachedGuestCode.VertexACode,
  315. cachedGuestCode.VertexBCode);
  316. shaders[0] = translatedShader.VertexA;
  317. shaders[1] = translatedShader.VertexB;
  318. program = translatedShader.Program;
  319. }
  320. else
  321. {
  322. byte[] code = cachedGuestCode.GetByIndex(stageIndex);
  323. TranslatedShader translatedShader = TranslateShader(_dumper, channel, currentStage, code);
  324. shaders[stageIndex + 1] = translatedShader.Shader;
  325. program = translatedShader.Program;
  326. }
  327. if (program != null)
  328. {
  329. shaderSources.Add(CreateShaderSource(program));
  330. }
  331. previousStage = currentStage;
  332. }
  333. else if (
  334. previousStage != null &&
  335. previousStage.LayerOutputWritten &&
  336. stageIndex == 3 &&
  337. !_context.Capabilities.SupportsLayerVertexTessellation)
  338. {
  339. shaderSources.Add(CreateShaderSource(previousStage.GenerateGeometryPassthrough()));
  340. }
  341. }
  342. ShaderSource[] shaderSourcesArray = shaderSources.ToArray();
  343. int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
  344. IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, new ShaderInfo(fragmentOutputMap, pipeline));
  345. gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
  346. _graphicsShaderCache.Add(gpShaders);
  347. EnqueueProgramToSave(gpShaders, hostProgram, shaderSourcesArray);
  348. _gpPrograms[addresses] = gpShaders;
  349. return gpShaders;
  350. }
  351. /// <summary>
  352. /// Tries to eliminate the geometry stage from the array of translator contexts.
  353. /// </summary>
  354. /// <param name="translatorContexts">Array of translator contexts</param>
  355. public static void TryRemoveGeometryStage(TranslatorContext[] translatorContexts)
  356. {
  357. if (translatorContexts[4] != null)
  358. {
  359. // We have a geometry shader, but geometry shaders are not supported.
  360. // Try to eliminate the geometry shader.
  361. ShaderProgramInfo info = translatorContexts[4].Translate().Info;
  362. if (info.Identification == ShaderIdentification.GeometryLayerPassthrough)
  363. {
  364. // We managed to identify that this geometry shader is only used to set the output Layer value,
  365. // we can set the Layer on the previous stage instead (usually the vertex stage) and eliminate it.
  366. for (int i = 3; i >= 1; i--)
  367. {
  368. if (translatorContexts[i] != null)
  369. {
  370. translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute);
  371. translatorContexts[i].SetLastInVertexPipeline(translatorContexts[5] != null);
  372. break;
  373. }
  374. }
  375. translatorContexts[4] = null;
  376. }
  377. }
  378. }
  379. /// <summary>
  380. /// Creates a shader source for use with the backend from a translated shader program.
  381. /// </summary>
  382. /// <param name="program">Translated shader program</param>
  383. /// <returns>Shader source</returns>
  384. public static ShaderSource CreateShaderSource(ShaderProgram program)
  385. {
  386. return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language);
  387. }
  388. /// <summary>
  389. /// Puts a program on the queue of programs to be saved on the disk cache.
  390. /// </summary>
  391. /// <remarks>
  392. /// This will not do anything if disk shader cache is disabled.
  393. /// </remarks>
  394. /// <param name="program">Cached shader program</param>
  395. /// <param name="hostProgram">Host program</param>
  396. /// <param name="sources">Source for each shader stage</param>
  397. private void EnqueueProgramToSave(CachedShaderProgram program, IProgram hostProgram, ShaderSource[] sources)
  398. {
  399. if (_diskCacheHostStorage.CacheEnabled)
  400. {
  401. byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(sources) : null;
  402. ProgramToSave programToSave = new ProgramToSave(program, hostProgram, binaryCode);
  403. _programsToSaveQueue.Enqueue(programToSave);
  404. }
  405. }
  406. /// <summary>
  407. /// Gets transform feedback state from the current GPU state.
  408. /// </summary>
  409. /// <param name="state">Current GPU state</param>
  410. /// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
  411. private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(ref ThreedClassState state)
  412. {
  413. bool tfEnable = state.TfEnable;
  414. if (!tfEnable)
  415. {
  416. return null;
  417. }
  418. TransformFeedbackDescriptor[] descs = new TransformFeedbackDescriptor[Constants.TotalTransformFeedbackBuffers];
  419. for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
  420. {
  421. var tf = state.TfState[i];
  422. descs[i] = new TransformFeedbackDescriptor(
  423. tf.BufferIndex,
  424. tf.Stride,
  425. tf.VaryingsCount,
  426. ref state.TfVaryingLocations[i]);
  427. }
  428. return descs;
  429. }
  430. /// <summary>
  431. /// Checks if compute shader code in memory is equal to the cached shader.
  432. /// </summary>
  433. /// <param name="channel">GPU channel using the shader</param>
  434. /// <param name="poolState">GPU channel state to verify shader compatibility</param>
  435. /// <param name="computeState">GPU channel compute state to verify shader compatibility</param>
  436. /// <param name="cpShader">Cached compute shader</param>
  437. /// <param name="gpuVa">GPU virtual address of the shader code in memory</param>
  438. /// <returns>True if the code is different, false otherwise</returns>
  439. private static bool IsShaderEqual(
  440. GpuChannel channel,
  441. GpuChannelPoolState poolState,
  442. GpuChannelComputeState computeState,
  443. CachedShaderProgram cpShader,
  444. ulong gpuVa)
  445. {
  446. if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
  447. {
  448. return cpShader.SpecializationState.MatchesCompute(channel, ref poolState, computeState, true);
  449. }
  450. return false;
  451. }
  452. /// <summary>
  453. /// Checks if graphics shader code from all stages in memory are equal to the cached shaders.
  454. /// </summary>
  455. /// <param name="channel">GPU channel using the shader</param>
  456. /// <param name="poolState">GPU channel state to verify shader compatibility</param>
  457. /// <param name="graphicsState">GPU channel graphics state to verify shader compatibility</param>
  458. /// <param name="gpShaders">Cached graphics shaders</param>
  459. /// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
  460. /// <returns>True if the code is different, false otherwise</returns>
  461. private static bool IsShaderEqual(
  462. GpuChannel channel,
  463. ref GpuChannelPoolState poolState,
  464. ref GpuChannelGraphicsState graphicsState,
  465. CachedShaderProgram gpShaders,
  466. ShaderAddresses addresses)
  467. {
  468. ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan();
  469. for (int stageIndex = 0; stageIndex < gpShaders.Shaders.Length; stageIndex++)
  470. {
  471. CachedShaderStage shader = gpShaders.Shaders[stageIndex];
  472. ulong gpuVa = addressesSpan[stageIndex];
  473. if (!IsShaderEqual(channel.MemoryManager, shader, gpuVa))
  474. {
  475. return false;
  476. }
  477. }
  478. bool usesDrawParameters = gpShaders.Shaders[1]?.Info.UsesDrawParameters ?? false;
  479. return gpShaders.SpecializationState.MatchesGraphics(channel, ref poolState, ref graphicsState, usesDrawParameters, true);
  480. }
  481. /// <summary>
  482. /// Checks if the code of the specified cached shader is different from the code in memory.
  483. /// </summary>
  484. /// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
  485. /// <param name="shader">Cached shader to compare with</param>
  486. /// <param name="gpuVa">GPU virtual address of the binary shader code</param>
  487. /// <returns>True if the code is different, false otherwise</returns>
  488. private static bool IsShaderEqual(MemoryManager memoryManager, CachedShaderStage shader, ulong gpuVa)
  489. {
  490. if (shader == null)
  491. {
  492. return true;
  493. }
  494. ReadOnlySpan<byte> memoryCode = memoryManager.GetSpan(gpuVa, shader.Code.Length);
  495. return memoryCode.SequenceEqual(shader.Code);
  496. }
  497. /// <summary>
  498. /// Decode the binary Maxwell shader code to a translator context.
  499. /// </summary>
  500. /// <param name="gpuAccessor">GPU state accessor</param>
  501. /// <param name="api">Graphics API that will be used with the shader</param>
  502. /// <param name="gpuVa">GPU virtual address of the binary shader code</param>
  503. /// <returns>The generated translator context</returns>
  504. public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
  505. {
  506. var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
  507. return Translator.CreateContext(gpuVa, gpuAccessor, options);
  508. }
  509. /// <summary>
  510. /// Decode the binary Maxwell shader code to a translator context.
  511. /// </summary>
  512. /// <remarks>
  513. /// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
  514. /// </remarks>
  515. /// <param name="gpuAccessor">GPU state accessor</param>
  516. /// <param name="api">Graphics API that will be used with the shader</param>
  517. /// <param name="flags">Flags that controls shader translation</param>
  518. /// <param name="gpuVa">GPU virtual address of the shader code</param>
  519. /// <returns>The generated translator context</returns>
  520. public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
  521. {
  522. var options = CreateTranslationOptions(api, flags);
  523. return Translator.CreateContext(gpuVa, gpuAccessor, options);
  524. }
  525. /// <summary>
  526. /// Translates a previously generated translator context to something that the host API accepts.
  527. /// </summary>
  528. /// <param name="dumper">Optional shader code dumper</param>
  529. /// <param name="channel">GPU channel using the shader</param>
  530. /// <param name="currentStage">Translator context of the stage to be translated</param>
  531. /// <param name="vertexA">Optional translator context of the shader that should be combined</param>
  532. /// <param name="codeA">Optional Maxwell binary code of the Vertex A shader, if present</param>
  533. /// <param name="codeB">Optional Maxwell binary code of the Vertex B or current stage shader, if present on cache</param>
  534. /// <returns>Compiled graphics shader code</returns>
  535. private static TranslatedShaderVertexPair TranslateShader(
  536. ShaderDumper dumper,
  537. GpuChannel channel,
  538. TranslatorContext currentStage,
  539. TranslatorContext vertexA,
  540. byte[] codeA,
  541. byte[] codeB)
  542. {
  543. ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
  544. var memoryManager = channel.MemoryManager;
  545. codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
  546. codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
  547. byte[] cb1DataA = memoryManager.Physical.GetSpan(cb1DataAddress, vertexA.Cb1DataSize).ToArray();
  548. byte[] cb1DataB = memoryManager.Physical.GetSpan(cb1DataAddress, currentStage.Cb1DataSize).ToArray();
  549. ShaderDumpPaths pathsA = default;
  550. ShaderDumpPaths pathsB = default;
  551. if (dumper != null)
  552. {
  553. pathsA = dumper.Dump(codeA, compute: false);
  554. pathsB = dumper.Dump(codeB, compute: false);
  555. }
  556. ShaderProgram program = currentStage.Translate(vertexA);
  557. pathsB.Prepend(program);
  558. pathsA.Prepend(program);
  559. CachedShaderStage vertexAStage = new CachedShaderStage(null, codeA, cb1DataA);
  560. CachedShaderStage vertexBStage = new CachedShaderStage(program.Info, codeB, cb1DataB);
  561. return new TranslatedShaderVertexPair(vertexAStage, vertexBStage, program);
  562. }
  563. /// <summary>
  564. /// Translates a previously generated translator context to something that the host API accepts.
  565. /// </summary>
  566. /// <param name="dumper">Optional shader code dumper</param>
  567. /// <param name="channel">GPU channel using the shader</param>
  568. /// <param name="context">Translator context of the stage to be translated</param>
  569. /// <param name="code">Optional Maxwell binary code of the current stage shader, if present on cache</param>
  570. /// <returns>Compiled graphics shader code</returns>
  571. private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code)
  572. {
  573. var memoryManager = channel.MemoryManager;
  574. ulong cb1DataAddress = context.Stage == ShaderStage.Compute
  575. ? channel.BufferManager.GetComputeUniformBufferAddress(1)
  576. : channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
  577. byte[] cb1Data = memoryManager.Physical.GetSpan(cb1DataAddress, context.Cb1DataSize).ToArray();
  578. code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
  579. ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
  580. ShaderProgram program = context.Translate();
  581. paths.Prepend(program);
  582. return new TranslatedShader(new CachedShaderStage(program.Info, code, cb1Data), program);
  583. }
  584. /// <summary>
  585. /// Gets the index of a stage from a <see cref="ShaderStage"/>.
  586. /// </summary>
  587. /// <param name="stage">Stage to get the index from</param>
  588. /// <returns>Stage index</returns>
  589. private static int StageToStageIndex(ShaderStage stage)
  590. {
  591. return stage switch
  592. {
  593. ShaderStage.TessellationControl => 1,
  594. ShaderStage.TessellationEvaluation => 2,
  595. ShaderStage.Geometry => 3,
  596. ShaderStage.Fragment => 4,
  597. _ => 0
  598. };
  599. }
  600. /// <summary>
  601. /// Gets information about the bindings used by a shader program.
  602. /// </summary>
  603. /// <param name="info">Shader program information to get the information from</param>
  604. /// <returns>Shader bindings</returns>
  605. public static ShaderBindings GetBindings(ShaderProgramInfo info)
  606. {
  607. var uniformBufferBindings = info.CBuffers.Select(x => x.Binding).ToArray();
  608. var storageBufferBindings = info.SBuffers.Select(x => x.Binding).ToArray();
  609. var textureBindings = info.Textures.Select(x => x.Binding).ToArray();
  610. var imageBindings = info.Images.Select(x => x.Binding).ToArray();
  611. return new ShaderBindings(
  612. uniformBufferBindings,
  613. storageBufferBindings,
  614. textureBindings,
  615. imageBindings);
  616. }
  617. /// <summary>
  618. /// Creates shader translation options with the requested graphics API and flags.
  619. /// The shader language is choosen based on the current configuration and graphics API.
  620. /// </summary>
  621. /// <param name="api">Target graphics API</param>
  622. /// <param name="flags">Translation flags</param>
  623. /// <returns>Translation options</returns>
  624. private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags)
  625. {
  626. TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan
  627. ? TargetLanguage.Spirv
  628. : TargetLanguage.Glsl;
  629. return new TranslationOptions(lang, api, flags);
  630. }
  631. /// <summary>
  632. /// Disposes the shader cache, deleting all the cached shaders.
  633. /// It's an error to use the shader cache after disposal.
  634. /// </summary>
  635. public void Dispose()
  636. {
  637. foreach (CachedShaderProgram program in _graphicsShaderCache.GetPrograms())
  638. {
  639. program.Dispose();
  640. }
  641. foreach (CachedShaderProgram program in _computeShaderCache.GetPrograms())
  642. {
  643. program.Dispose();
  644. }
  645. _cacheWriter?.Dispose();
  646. }
  647. }
  648. }