ShaderCache.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Gpu.Engine.Threed;
  4. using Ryujinx.Graphics.Gpu.Memory;
  5. using Ryujinx.Graphics.Gpu.Shader.Cache;
  6. using Ryujinx.Graphics.Gpu.Shader.DiskCache;
  7. using Ryujinx.Graphics.Shader;
  8. using Ryujinx.Graphics.Shader.Translation;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Threading;
  12. namespace Ryujinx.Graphics.Gpu.Shader
  13. {
  14. /// <summary>
  15. /// Memory cache of shader code.
  16. /// </summary>
  17. class ShaderCache : IDisposable
  18. {
  19. /// <summary>
  20. /// Default flags used on the shader translation process.
  21. /// </summary>
  22. public const TranslationFlags DefaultFlags = TranslationFlags.DebugMode;
  23. private struct TranslatedShader
  24. {
  25. public readonly CachedShaderStage Shader;
  26. public readonly ShaderProgram Program;
  27. public TranslatedShader(CachedShaderStage shader, ShaderProgram program)
  28. {
  29. Shader = shader;
  30. Program = program;
  31. }
  32. }
  33. private struct TranslatedShaderVertexPair
  34. {
  35. public readonly CachedShaderStage VertexA;
  36. public readonly CachedShaderStage VertexB;
  37. public readonly ShaderProgram Program;
  38. public TranslatedShaderVertexPair(CachedShaderStage vertexA, CachedShaderStage vertexB, ShaderProgram program)
  39. {
  40. VertexA = vertexA;
  41. VertexB = vertexB;
  42. Program = program;
  43. }
  44. }
  45. private readonly GpuContext _context;
  46. private readonly ShaderDumper _dumper;
  47. private readonly Dictionary<ulong, CachedShaderProgram> _cpPrograms;
  48. private readonly Dictionary<ShaderAddresses, CachedShaderProgram> _gpPrograms;
  49. private struct ProgramToSave
  50. {
  51. public readonly CachedShaderProgram CachedProgram;
  52. public readonly IProgram HostProgram;
  53. public ProgramToSave(CachedShaderProgram cachedProgram, IProgram hostProgram)
  54. {
  55. CachedProgram = cachedProgram;
  56. HostProgram = hostProgram;
  57. }
  58. }
  59. private Queue<ProgramToSave> _programsToSaveQueue;
  60. private readonly ComputeShaderCacheHashTable _computeShaderCache;
  61. private readonly ShaderCacheHashTable _graphicsShaderCache;
  62. private readonly DiskCacheHostStorage _diskCacheHostStorage;
  63. private readonly BackgroundDiskCacheWriter _cacheWriter;
  64. /// <summary>
  65. /// Event for signalling shader cache loading progress.
  66. /// </summary>
  67. public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged;
  68. /// <summary>
  69. /// Creates a new instance of the shader cache.
  70. /// </summary>
  71. /// <param name="context">GPU context that the shader cache belongs to</param>
  72. public ShaderCache(GpuContext context)
  73. {
  74. _context = context;
  75. _dumper = new ShaderDumper();
  76. _cpPrograms = new Dictionary<ulong, CachedShaderProgram>();
  77. _gpPrograms = new Dictionary<ShaderAddresses, CachedShaderProgram>();
  78. _programsToSaveQueue = new Queue<ProgramToSave>();
  79. string diskCacheTitleId = GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
  80. ? CacheHelper.GetBaseCacheDirectory(GraphicsConfig.TitleId)
  81. : null;
  82. _computeShaderCache = new ComputeShaderCacheHashTable();
  83. _graphicsShaderCache = new ShaderCacheHashTable();
  84. _diskCacheHostStorage = new DiskCacheHostStorage(diskCacheTitleId);
  85. if (_diskCacheHostStorage.CacheEnabled)
  86. {
  87. _cacheWriter = new BackgroundDiskCacheWriter(context, _diskCacheHostStorage);
  88. }
  89. }
  90. /// <summary>
  91. /// Processes the queue of shaders that must save their binaries to the disk cache.
  92. /// </summary>
  93. public void ProcessShaderCacheQueue()
  94. {
  95. // Check to see if the binaries for previously compiled shaders are ready, and save them out.
  96. while (_programsToSaveQueue.TryPeek(out ProgramToSave programToSave))
  97. {
  98. ProgramLinkStatus result = programToSave.HostProgram.CheckProgramLink(false);
  99. if (result != ProgramLinkStatus.Incomplete)
  100. {
  101. if (result == ProgramLinkStatus.Success)
  102. {
  103. _cacheWriter.AddShader(programToSave.CachedProgram, programToSave.HostProgram.GetBinary());
  104. }
  105. _programsToSaveQueue.Dequeue();
  106. }
  107. else
  108. {
  109. break;
  110. }
  111. }
  112. }
  113. /// <summary>
  114. /// Initialize the cache.
  115. /// </summary>
  116. /// <param name="cancellationToken">Cancellation token to cancel the shader cache initialization process</param>
  117. internal void Initialize(CancellationToken cancellationToken)
  118. {
  119. if (_diskCacheHostStorage.CacheEnabled)
  120. {
  121. if (!_diskCacheHostStorage.CacheExists())
  122. {
  123. // If we don't have a shader cache on the new format, try to perform migration from the old shader cache.
  124. Logger.Info?.Print(LogClass.Gpu, "No shader cache found, trying to migrate from legacy shader cache...");
  125. int migrationCount = Migration.MigrateFromLegacyCache(_context, _diskCacheHostStorage);
  126. Logger.Info?.Print(LogClass.Gpu, $"Migrated {migrationCount} shaders.");
  127. }
  128. ParallelDiskCacheLoader loader = new ParallelDiskCacheLoader(
  129. _context,
  130. _graphicsShaderCache,
  131. _computeShaderCache,
  132. _diskCacheHostStorage,
  133. cancellationToken,
  134. ShaderCacheStateUpdate);
  135. loader.LoadShaders();
  136. int errorCount = loader.ErrorCount;
  137. if (errorCount != 0)
  138. {
  139. Logger.Warning?.Print(LogClass.Gpu, $"Failed to load {errorCount} shaders from the disk cache.");
  140. }
  141. }
  142. }
  143. /// <summary>
  144. /// Shader cache state update handler.
  145. /// </summary>
  146. /// <param name="state">Current state of the shader cache load process</param>
  147. /// <param name="current">Number of the current shader being processed</param>
  148. /// <param name="total">Total number of shaders to process</param>
  149. private void ShaderCacheStateUpdate(ShaderCacheState state, int current, int total)
  150. {
  151. ShaderCacheStateChanged?.Invoke(state, current, total);
  152. }
  153. /// <summary>
  154. /// Gets a compute shader from the cache.
  155. /// </summary>
  156. /// <remarks>
  157. /// This automatically translates, compiles and adds the code to the cache if not present.
  158. /// </remarks>
  159. /// <param name="channel">GPU channel</param>
  160. /// <param name="poolState">Texture pool state</param>
  161. /// <param name="computeState">Compute engine state</param>
  162. /// <param name="gpuVa">GPU virtual address of the binary shader code</param>
  163. /// <returns>Compiled compute shader code</returns>
  164. public CachedShaderProgram GetComputeShader(
  165. GpuChannel channel,
  166. GpuChannelPoolState poolState,
  167. GpuChannelComputeState computeState,
  168. ulong gpuVa)
  169. {
  170. if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, cpShader, gpuVa))
  171. {
  172. return cpShader;
  173. }
  174. if (_computeShaderCache.TryFind(channel, poolState, gpuVa, out cpShader, out byte[] cachedGuestCode))
  175. {
  176. _cpPrograms[gpuVa] = cpShader;
  177. return cpShader;
  178. }
  179. ShaderSpecializationState specState = new ShaderSpecializationState(computeState);
  180. GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, computeState, default, specState);
  181. GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
  182. TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, gpuVa);
  183. TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
  184. IProgram hostProgram = _context.Renderer.CreateProgram(new ShaderSource[] { CreateShaderSource(translatedShader.Program) }, new ShaderInfo(-1));
  185. cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
  186. _computeShaderCache.Add(cpShader);
  187. EnqueueProgramToSave(new ProgramToSave(cpShader, hostProgram));
  188. _cpPrograms[gpuVa] = cpShader;
  189. return cpShader;
  190. }
  191. /// <summary>
  192. /// Gets a graphics shader program from the shader cache.
  193. /// This includes all the specified shader stages.
  194. /// </summary>
  195. /// <remarks>
  196. /// This automatically translates, compiles and adds the code to the cache if not present.
  197. /// </remarks>
  198. /// <param name="state">GPU state</param>
  199. /// <param name="channel">GPU channel</param>
  200. /// <param name="poolState">Texture pool state</param>
  201. /// <param name="graphicsState">3D engine state</param>
  202. /// <param name="addresses">Addresses of the shaders for each stage</param>
  203. /// <returns>Compiled graphics shader code</returns>
  204. public CachedShaderProgram GetGraphicsShader(
  205. ref ThreedClassState state,
  206. GpuChannel channel,
  207. GpuChannelPoolState poolState,
  208. GpuChannelGraphicsState graphicsState,
  209. ShaderAddresses addresses)
  210. {
  211. if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, graphicsState, gpShaders, addresses))
  212. {
  213. return gpShaders;
  214. }
  215. if (_graphicsShaderCache.TryFind(channel, poolState, graphicsState, addresses, out gpShaders, out var cachedGuestCode))
  216. {
  217. _gpPrograms[addresses] = gpShaders;
  218. return gpShaders;
  219. }
  220. TransformFeedbackDescriptor[] transformFeedbackDescriptors = GetTransformFeedbackDescriptors(ref state);
  221. ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, transformFeedbackDescriptors);
  222. GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, default, graphicsState, specState, transformFeedbackDescriptors);
  223. ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan();
  224. TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1];
  225. TranslatorContext nextStage = null;
  226. for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
  227. {
  228. ulong gpuVa = addressesSpan[stageIndex + 1];
  229. if (gpuVa != 0)
  230. {
  231. GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState, stageIndex);
  232. TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, DefaultFlags, gpuVa);
  233. if (nextStage != null)
  234. {
  235. currentStage.SetNextStage(nextStage);
  236. }
  237. if (stageIndex == 0 && addresses.VertexA != 0)
  238. {
  239. translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA);
  240. }
  241. translatorContexts[stageIndex + 1] = currentStage;
  242. nextStage = currentStage;
  243. }
  244. }
  245. CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
  246. List<ShaderSource> shaderSources = new List<ShaderSource>();
  247. for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
  248. {
  249. TranslatorContext currentStage = translatorContexts[stageIndex + 1];
  250. if (currentStage != null)
  251. {
  252. ShaderProgram program;
  253. if (stageIndex == 0 && translatorContexts[0] != null)
  254. {
  255. TranslatedShaderVertexPair translatedShader = TranslateShader(
  256. _dumper,
  257. channel,
  258. currentStage,
  259. translatorContexts[0],
  260. cachedGuestCode.VertexACode,
  261. cachedGuestCode.VertexBCode);
  262. shaders[0] = translatedShader.VertexA;
  263. shaders[1] = translatedShader.VertexB;
  264. program = translatedShader.Program;
  265. }
  266. else
  267. {
  268. byte[] code = cachedGuestCode.GetByIndex(stageIndex);
  269. TranslatedShader translatedShader = TranslateShader(_dumper, channel, currentStage, code);
  270. shaders[stageIndex + 1] = translatedShader.Shader;
  271. program = translatedShader.Program;
  272. }
  273. if (program != null)
  274. {
  275. shaderSources.Add(CreateShaderSource(program));
  276. }
  277. }
  278. }
  279. int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
  280. IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources.ToArray(), new ShaderInfo(fragmentOutputMap));
  281. gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
  282. _graphicsShaderCache.Add(gpShaders);
  283. EnqueueProgramToSave(new ProgramToSave(gpShaders, hostProgram));
  284. _gpPrograms[addresses] = gpShaders;
  285. return gpShaders;
  286. }
  287. /// <summary>
  288. /// Creates a shader source for use with the backend from a translated shader program.
  289. /// </summary>
  290. /// <param name="program">Translated shader program</param>
  291. /// <returns>Shader source</returns>
  292. public static ShaderSource CreateShaderSource(ShaderProgram program)
  293. {
  294. return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language);
  295. }
  296. /// <summary>
  297. /// Puts a program on the queue of programs to be saved on the disk cache.
  298. /// </summary>
  299. /// <remarks>
  300. /// This will not do anything if disk shader cache is disabled.
  301. /// </remarks>
  302. /// <param name="programToSave">Program to be saved on disk</param>
  303. private void EnqueueProgramToSave(ProgramToSave programToSave)
  304. {
  305. if (_diskCacheHostStorage.CacheEnabled)
  306. {
  307. _programsToSaveQueue.Enqueue(programToSave);
  308. }
  309. }
  310. /// <summary>
  311. /// Gets transform feedback state from the current GPU state.
  312. /// </summary>
  313. /// <param name="state">Current GPU state</param>
  314. /// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
  315. private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(ref ThreedClassState state)
  316. {
  317. bool tfEnable = state.TfEnable;
  318. if (!tfEnable)
  319. {
  320. return null;
  321. }
  322. TransformFeedbackDescriptor[] descs = new TransformFeedbackDescriptor[Constants.TotalTransformFeedbackBuffers];
  323. for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
  324. {
  325. var tf = state.TfState[i];
  326. descs[i] = new TransformFeedbackDescriptor(
  327. tf.BufferIndex,
  328. tf.Stride,
  329. tf.VaryingsCount,
  330. ref state.TfVaryingLocations[i]);
  331. }
  332. return descs;
  333. }
  334. /// <summary>
  335. /// Checks if compute shader code in memory is equal to the cached shader.
  336. /// </summary>
  337. /// <param name="channel">GPU channel using the shader</param>
  338. /// <param name="poolState">GPU channel state to verify shader compatibility</param>
  339. /// <param name="cpShader">Cached compute shader</param>
  340. /// <param name="gpuVa">GPU virtual address of the shader code in memory</param>
  341. /// <returns>True if the code is different, false otherwise</returns>
  342. private static bool IsShaderEqual(
  343. GpuChannel channel,
  344. GpuChannelPoolState poolState,
  345. CachedShaderProgram cpShader,
  346. ulong gpuVa)
  347. {
  348. if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa))
  349. {
  350. return cpShader.SpecializationState.MatchesCompute(channel, poolState);
  351. }
  352. return false;
  353. }
  354. /// <summary>
  355. /// Checks if graphics shader code from all stages in memory are equal to the cached shaders.
  356. /// </summary>
  357. /// <param name="channel">GPU channel using the shader</param>
  358. /// <param name="poolState">GPU channel state to verify shader compatibility</param>
  359. /// <param name="graphicsState">GPU channel graphics state to verify shader compatibility</param>
  360. /// <param name="gpShaders">Cached graphics shaders</param>
  361. /// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
  362. /// <returns>True if the code is different, false otherwise</returns>
  363. private static bool IsShaderEqual(
  364. GpuChannel channel,
  365. GpuChannelPoolState poolState,
  366. GpuChannelGraphicsState graphicsState,
  367. CachedShaderProgram gpShaders,
  368. ShaderAddresses addresses)
  369. {
  370. ReadOnlySpan<ulong> addressesSpan = addresses.AsSpan();
  371. for (int stageIndex = 0; stageIndex < gpShaders.Shaders.Length; stageIndex++)
  372. {
  373. CachedShaderStage shader = gpShaders.Shaders[stageIndex];
  374. ulong gpuVa = addressesSpan[stageIndex];
  375. if (!IsShaderEqual(channel.MemoryManager, shader, gpuVa))
  376. {
  377. return false;
  378. }
  379. }
  380. return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState);
  381. }
  382. /// <summary>
  383. /// Checks if the code of the specified cached shader is different from the code in memory.
  384. /// </summary>
  385. /// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
  386. /// <param name="shader">Cached shader to compare with</param>
  387. /// <param name="gpuVa">GPU virtual address of the binary shader code</param>
  388. /// <returns>True if the code is different, false otherwise</returns>
  389. private static bool IsShaderEqual(MemoryManager memoryManager, CachedShaderStage shader, ulong gpuVa)
  390. {
  391. if (shader == null)
  392. {
  393. return true;
  394. }
  395. ReadOnlySpan<byte> memoryCode = memoryManager.GetSpan(gpuVa, shader.Code.Length);
  396. return memoryCode.SequenceEqual(shader.Code);
  397. }
  398. /// <summary>
  399. /// Decode the binary Maxwell shader code to a translator context.
  400. /// </summary>
  401. /// <param name="gpuAccessor">GPU state accessor</param>
  402. /// <param name="gpuVa">GPU virtual address of the binary shader code</param>
  403. /// <returns>The generated translator context</returns>
  404. public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, ulong gpuVa)
  405. {
  406. var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags | TranslationFlags.Compute);
  407. return Translator.CreateContext(gpuVa, gpuAccessor, options);
  408. }
  409. /// <summary>
  410. /// Decode the binary Maxwell shader code to a translator context.
  411. /// </summary>
  412. /// <remarks>
  413. /// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
  414. /// </remarks>
  415. /// <param name="gpuAccessor">GPU state accessor</param>
  416. /// <param name="flags">Flags that controls shader translation</param>
  417. /// <param name="gpuVa">GPU virtual address of the shader code</param>
  418. /// <returns>The generated translator context</returns>
  419. public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TranslationFlags flags, ulong gpuVa)
  420. {
  421. var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags);
  422. return Translator.CreateContext(gpuVa, gpuAccessor, options);
  423. }
  424. /// <summary>
  425. /// Translates a previously generated translator context to something that the host API accepts.
  426. /// </summary>
  427. /// <param name="dumper">Optional shader code dumper</param>
  428. /// <param name="channel">GPU channel using the shader</param>
  429. /// <param name="currentStage">Translator context of the stage to be translated</param>
  430. /// <param name="vertexA">Optional translator context of the shader that should be combined</param>
  431. /// <param name="codeA">Optional Maxwell binary code of the Vertex A shader, if present</param>
  432. /// <param name="codeB">Optional Maxwell binary code of the Vertex B or current stage shader, if present on cache</param>
  433. /// <returns>Compiled graphics shader code</returns>
  434. private static TranslatedShaderVertexPair TranslateShader(
  435. ShaderDumper dumper,
  436. GpuChannel channel,
  437. TranslatorContext currentStage,
  438. TranslatorContext vertexA,
  439. byte[] codeA,
  440. byte[] codeB)
  441. {
  442. ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
  443. var memoryManager = channel.MemoryManager;
  444. codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
  445. codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
  446. byte[] cb1DataA = memoryManager.Physical.GetSpan(cb1DataAddress, vertexA.Cb1DataSize).ToArray();
  447. byte[] cb1DataB = memoryManager.Physical.GetSpan(cb1DataAddress, currentStage.Cb1DataSize).ToArray();
  448. ShaderDumpPaths pathsA = default;
  449. ShaderDumpPaths pathsB = default;
  450. if (dumper != null)
  451. {
  452. pathsA = dumper.Dump(codeA, compute: false);
  453. pathsB = dumper.Dump(codeB, compute: false);
  454. }
  455. ShaderProgram program = currentStage.Translate(vertexA);
  456. pathsB.Prepend(program);
  457. pathsA.Prepend(program);
  458. CachedShaderStage vertexAStage = new CachedShaderStage(null, codeA, cb1DataA);
  459. CachedShaderStage vertexBStage = new CachedShaderStage(program.Info, codeB, cb1DataB);
  460. return new TranslatedShaderVertexPair(vertexAStage, vertexBStage, program);
  461. }
  462. /// <summary>
  463. /// Translates a previously generated translator context to something that the host API accepts.
  464. /// </summary>
  465. /// <param name="dumper">Optional shader code dumper</param>
  466. /// <param name="channel">GPU channel using the shader</param>
  467. /// <param name="context">Translator context of the stage to be translated</param>
  468. /// <param name="code">Optional Maxwell binary code of the current stage shader, if present on cache</param>
  469. /// <returns>Compiled graphics shader code</returns>
  470. private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code)
  471. {
  472. var memoryManager = channel.MemoryManager;
  473. ulong cb1DataAddress = context.Stage == ShaderStage.Compute
  474. ? channel.BufferManager.GetComputeUniformBufferAddress(1)
  475. : channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
  476. byte[] cb1Data = memoryManager.Physical.GetSpan(cb1DataAddress, context.Cb1DataSize).ToArray();
  477. code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
  478. ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
  479. ShaderProgram program = context.Translate();
  480. paths.Prepend(program);
  481. return new TranslatedShader(new CachedShaderStage(program.Info, code, cb1Data), program);
  482. }
  483. /// <summary>
  484. /// Gets the index of a stage from a <see cref="ShaderStage"/>.
  485. /// </summary>
  486. /// <param name="stage">Stage to get the index from</param>
  487. /// <returns>Stage index</returns>
  488. private static int StageToStageIndex(ShaderStage stage)
  489. {
  490. return stage switch
  491. {
  492. ShaderStage.TessellationControl => 1,
  493. ShaderStage.TessellationEvaluation => 2,
  494. ShaderStage.Geometry => 3,
  495. ShaderStage.Fragment => 4,
  496. _ => 0
  497. };
  498. }
  499. /// <summary>
  500. /// Disposes the shader cache, deleting all the cached shaders.
  501. /// It's an error to use the shader cache after disposal.
  502. /// </summary>
  503. public void Dispose()
  504. {
  505. foreach (CachedShaderProgram program in _graphicsShaderCache.GetPrograms())
  506. {
  507. program.Dispose();
  508. }
  509. foreach (CachedShaderProgram program in _computeShaderCache.GetPrograms())
  510. {
  511. program.Dispose();
  512. }
  513. _cacheWriter?.Dispose();
  514. }
  515. }
  516. }