DiskCacheHostStorage.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. using Ryujinx.Graphics.GAL;
  2. using Ryujinx.Graphics.Shader;
  3. using Ryujinx.Graphics.Shader.Translation;
  4. using System;
  5. using System.IO;
  6. using System.Numerics;
  7. using System.Runtime.CompilerServices;
  8. namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
  9. {
  10. /// <summary>
  11. /// On-disk shader cache storage for host code.
  12. /// </summary>
  13. class DiskCacheHostStorage
  14. {
  15. private const uint TocsMagic = (byte)'T' | ((byte)'O' << 8) | ((byte)'C' << 16) | ((byte)'S' << 24);
  16. private const uint TochMagic = (byte)'T' | ((byte)'O' << 8) | ((byte)'C' << 16) | ((byte)'H' << 24);
  17. private const uint ShdiMagic = (byte)'S' | ((byte)'H' << 8) | ((byte)'D' << 16) | ((byte)'I' << 24);
  18. private const uint BufdMagic = (byte)'B' | ((byte)'U' << 8) | ((byte)'F' << 16) | ((byte)'D' << 24);
  19. private const uint TexdMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'D' << 24);
  20. private const ushort FileFormatVersionMajor = 1;
  21. private const ushort FileFormatVersionMinor = 2;
  22. private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
  23. private const uint CodeGenVersion = 4106;
  24. private const string SharedTocFileName = "shared.toc";
  25. private const string SharedDataFileName = "shared.data";
  26. private readonly string _basePath;
  27. public bool CacheEnabled => !string.IsNullOrEmpty(_basePath);
  28. /// <summary>
  29. /// TOC (Table of contents) file header.
  30. /// </summary>
  31. private struct TocHeader
  32. {
  33. /// <summary>
  34. /// Magic value, for validation and identification.
  35. /// </summary>
  36. public uint Magic;
  37. /// <summary>
  38. /// File format version.
  39. /// </summary>
  40. public uint FormatVersion;
  41. /// <summary>
  42. /// Generated shader code version.
  43. /// </summary>
  44. public uint CodeGenVersion;
  45. /// <summary>
  46. /// Header padding.
  47. /// </summary>
  48. public uint Padding;
  49. /// <summary>
  50. /// Timestamp of when the file was first created.
  51. /// </summary>
  52. public ulong Timestamp;
  53. /// <summary>
  54. /// Reserved space, to be used in the future. Write as zero.
  55. /// </summary>
  56. public ulong Reserved;
  57. }
  58. /// <summary>
  59. /// Offset and size pair.
  60. /// </summary>
  61. private struct OffsetAndSize
  62. {
  63. /// <summary>
  64. /// Offset.
  65. /// </summary>
  66. public ulong Offset;
  67. /// <summary>
  68. /// Size of uncompressed data.
  69. /// </summary>
  70. public uint UncompressedSize;
  71. /// <summary>
  72. /// Size of compressed data.
  73. /// </summary>
  74. public uint CompressedSize;
  75. }
  76. /// <summary>
  77. /// Per-stage data entry.
  78. /// </summary>
  79. private struct DataEntryPerStage
  80. {
  81. /// <summary>
  82. /// Index of the guest code on the guest code cache TOC file.
  83. /// </summary>
  84. public int GuestCodeIndex;
  85. }
  86. /// <summary>
  87. /// Per-program data entry.
  88. /// </summary>
  89. private struct DataEntry
  90. {
  91. /// <summary>
  92. /// Bit mask where each bit set is a used shader stage. Should be zero for compute shaders.
  93. /// </summary>
  94. public uint StagesBitMask;
  95. }
  96. /// <summary>
  97. /// Per-stage shader information, returned by the translator.
  98. /// </summary>
  99. private struct DataShaderInfo
  100. {
  101. /// <summary>
  102. /// Total constant buffers used.
  103. /// </summary>
  104. public ushort CBuffersCount;
  105. /// <summary>
  106. /// Total storage buffers used.
  107. /// </summary>
  108. public ushort SBuffersCount;
  109. /// <summary>
  110. /// Total textures used.
  111. /// </summary>
  112. public ushort TexturesCount;
  113. /// <summary>
  114. /// Total images used.
  115. /// </summary>
  116. public ushort ImagesCount;
  117. /// <summary>
  118. /// Shader stage.
  119. /// </summary>
  120. public ShaderStage Stage;
  121. /// <summary>
  122. /// Indicates if the shader accesses the Instance ID built-in variable.
  123. /// </summary>
  124. public bool UsesInstanceId;
  125. /// <summary>
  126. /// Indicates if the shader modifies the Layer built-in variable.
  127. /// </summary>
  128. public bool UsesRtLayer;
  129. /// <summary>
  130. /// Bit mask with the clip distances written on the vertex stage.
  131. /// </summary>
  132. public byte ClipDistancesWritten;
  133. /// <summary>
  134. /// Bit mask of the render target components written by the fragment stage.
  135. /// </summary>
  136. public int FragmentOutputMap;
  137. /// <summary>
  138. /// Indicates if the vertex shader accesses draw parameters.
  139. /// </summary>
  140. public bool UsesDrawParameters;
  141. }
  142. private readonly DiskCacheGuestStorage _guestStorage;
  143. /// <summary>
  144. /// Creates a disk cache host storage.
  145. /// </summary>
  146. /// <param name="basePath">Base path of the shader cache</param>
  147. public DiskCacheHostStorage(string basePath)
  148. {
  149. _basePath = basePath;
  150. _guestStorage = new DiskCacheGuestStorage(basePath);
  151. if (CacheEnabled)
  152. {
  153. Directory.CreateDirectory(basePath);
  154. }
  155. }
  156. /// <summary>
  157. /// Gets the total of host programs on the cache.
  158. /// </summary>
  159. /// <returns>Host programs count</returns>
  160. public int GetProgramCount()
  161. {
  162. string tocFilePath = Path.Combine(_basePath, SharedTocFileName);
  163. if (!File.Exists(tocFilePath))
  164. {
  165. return 0;
  166. }
  167. return Math.Max((int)((new FileInfo(tocFilePath).Length - Unsafe.SizeOf<TocHeader>()) / sizeof(ulong)), 0);
  168. }
  169. /// <summary>
  170. /// Guest the name of the host program cache file, with extension.
  171. /// </summary>
  172. /// <param name="context">GPU context</param>
  173. /// <returns>Name of the file, without extension</returns>
  174. private static string GetHostFileName(GpuContext context)
  175. {
  176. string apiName = context.Capabilities.Api.ToString().ToLowerInvariant();
  177. string vendorName = RemoveInvalidCharacters(context.Capabilities.VendorName.ToLowerInvariant());
  178. return $"{apiName}_{vendorName}";
  179. }
  180. /// <summary>
  181. /// Removes invalid path characters and spaces from a file name.
  182. /// </summary>
  183. /// <param name="fileName">File name</param>
  184. /// <returns>Filtered file name</returns>
  185. private static string RemoveInvalidCharacters(string fileName)
  186. {
  187. int indexOfSpace = fileName.IndexOf(' ');
  188. if (indexOfSpace >= 0)
  189. {
  190. fileName = fileName.Substring(0, indexOfSpace);
  191. }
  192. return string.Concat(fileName.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries));
  193. }
  194. /// <summary>
  195. /// Gets the name of the TOC host file.
  196. /// </summary>
  197. /// <param name="context">GPU context</param>
  198. /// <returns>File name</returns>
  199. private static string GetHostTocFileName(GpuContext context)
  200. {
  201. return GetHostFileName(context) + ".toc";
  202. }
  203. /// <summary>
  204. /// Gets the name of the data host file.
  205. /// </summary>
  206. /// <param name="context">GPU context</param>
  207. /// <returns>File name</returns>
  208. private static string GetHostDataFileName(GpuContext context)
  209. {
  210. return GetHostFileName(context) + ".data";
  211. }
  212. /// <summary>
  213. /// Checks if a disk cache exists for the current application.
  214. /// </summary>
  215. /// <returns>True if a disk cache exists, false otherwise</returns>
  216. public bool CacheExists()
  217. {
  218. string tocFilePath = Path.Combine(_basePath, SharedTocFileName);
  219. string dataFilePath = Path.Combine(_basePath, SharedDataFileName);
  220. if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath) || !_guestStorage.TocFileExists() || !_guestStorage.DataFileExists())
  221. {
  222. return false;
  223. }
  224. return true;
  225. }
  226. /// <summary>
  227. /// Loads all shaders from the cache.
  228. /// </summary>
  229. /// <param name="context">GPU context</param>
  230. /// <param name="loader">Parallel disk cache loader</param>
  231. public void LoadShaders(GpuContext context, ParallelDiskCacheLoader loader)
  232. {
  233. if (!CacheExists())
  234. {
  235. return;
  236. }
  237. Stream hostTocFileStream = null;
  238. Stream hostDataFileStream = null;
  239. try
  240. {
  241. using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: false);
  242. using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: false);
  243. using var guestTocFileStream = _guestStorage.OpenTocFileStream();
  244. using var guestDataFileStream = _guestStorage.OpenDataFileStream();
  245. BinarySerializer tocReader = new BinarySerializer(tocFileStream);
  246. BinarySerializer dataReader = new BinarySerializer(dataFileStream);
  247. TocHeader header = new TocHeader();
  248. if (!tocReader.TryRead(ref header) || header.Magic != TocsMagic)
  249. {
  250. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  251. }
  252. if (header.FormatVersion != FileFormatVersionPacked)
  253. {
  254. throw new DiskCacheLoadException(DiskCacheLoadResult.IncompatibleVersion);
  255. }
  256. bool loadHostCache = header.CodeGenVersion == CodeGenVersion;
  257. int programIndex = 0;
  258. DataEntry entry = new DataEntry();
  259. while (tocFileStream.Position < tocFileStream.Length && loader.Active)
  260. {
  261. ulong dataOffset = 0;
  262. tocReader.Read(ref dataOffset);
  263. if ((ulong)dataOffset >= (ulong)dataFileStream.Length)
  264. {
  265. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  266. }
  267. dataFileStream.Seek((long)dataOffset, SeekOrigin.Begin);
  268. dataReader.BeginCompression();
  269. dataReader.Read(ref entry);
  270. uint stagesBitMask = entry.StagesBitMask;
  271. if ((stagesBitMask & ~0x3fu) != 0)
  272. {
  273. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  274. }
  275. bool isCompute = stagesBitMask == 0;
  276. if (isCompute)
  277. {
  278. stagesBitMask = 1;
  279. }
  280. GuestCodeAndCbData?[] guestShaders = new GuestCodeAndCbData?[isCompute ? 1 : Constants.ShaderStages + 1];
  281. DataEntryPerStage stageEntry = new DataEntryPerStage();
  282. while (stagesBitMask != 0)
  283. {
  284. int stageIndex = BitOperations.TrailingZeroCount(stagesBitMask);
  285. dataReader.Read(ref stageEntry);
  286. guestShaders[stageIndex] = _guestStorage.LoadShader(
  287. guestTocFileStream,
  288. guestDataFileStream,
  289. stageEntry.GuestCodeIndex);
  290. stagesBitMask &= ~(1u << stageIndex);
  291. }
  292. ShaderSpecializationState specState = ShaderSpecializationState.Read(ref dataReader);
  293. dataReader.EndCompression();
  294. if (loadHostCache)
  295. {
  296. (byte[] hostCode, CachedShaderStage[] shaders) = ReadHostCode(
  297. context,
  298. ref hostTocFileStream,
  299. ref hostDataFileStream,
  300. guestShaders,
  301. programIndex,
  302. header.Timestamp);
  303. if (hostCode != null)
  304. {
  305. bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
  306. int fragmentOutputMap = hasFragmentShader ? shaders[5].Info.FragmentOutputMap : -1;
  307. ShaderInfo shaderInfo = specState.PipelineState.HasValue
  308. ? new ShaderInfo(fragmentOutputMap, specState.PipelineState.Value, fromCache: true)
  309. : new ShaderInfo(fragmentOutputMap, fromCache: true);
  310. IProgram hostProgram;
  311. if (context.Capabilities.Api == TargetApi.Vulkan)
  312. {
  313. ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode);
  314. hostProgram = context.Renderer.CreateProgram(shaderSources, shaderInfo);
  315. }
  316. else
  317. {
  318. hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
  319. }
  320. CachedShaderProgram program = new CachedShaderProgram(hostProgram, specState, shaders);
  321. loader.QueueHostProgram(program, hostCode, programIndex, isCompute);
  322. }
  323. else
  324. {
  325. loadHostCache = false;
  326. }
  327. }
  328. if (!loadHostCache)
  329. {
  330. loader.QueueGuestProgram(guestShaders, specState, programIndex, isCompute);
  331. }
  332. loader.CheckCompilation();
  333. programIndex++;
  334. }
  335. }
  336. finally
  337. {
  338. _guestStorage.ClearMemoryCache();
  339. hostTocFileStream?.Dispose();
  340. hostDataFileStream?.Dispose();
  341. }
  342. }
  343. /// <summary>
  344. /// Reads the host code for a given shader, if existent.
  345. /// </summary>
  346. /// <param name="context">GPU context</param>
  347. /// <param name="tocFileStream">Host TOC file stream, intialized if needed</param>
  348. /// <param name="dataFileStream">Host data file stream, initialized if needed</param>
  349. /// <param name="guestShaders">Guest shader code for each active stage</param>
  350. /// <param name="programIndex">Index of the program on the cache</param>
  351. /// <param name="expectedTimestamp">Timestamp of the shared cache file. The host file must be newer than it</param>
  352. /// <returns>Host binary code, or null if not found</returns>
  353. private (byte[], CachedShaderStage[]) ReadHostCode(
  354. GpuContext context,
  355. ref Stream tocFileStream,
  356. ref Stream dataFileStream,
  357. GuestCodeAndCbData?[] guestShaders,
  358. int programIndex,
  359. ulong expectedTimestamp)
  360. {
  361. if (tocFileStream == null && dataFileStream == null)
  362. {
  363. string tocFilePath = Path.Combine(_basePath, GetHostTocFileName(context));
  364. string dataFilePath = Path.Combine(_basePath, GetHostDataFileName(context));
  365. if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath))
  366. {
  367. return (null, null);
  368. }
  369. tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: false);
  370. dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: false);
  371. BinarySerializer tempTocReader = new BinarySerializer(tocFileStream);
  372. TocHeader header = new TocHeader();
  373. tempTocReader.Read(ref header);
  374. if (header.Timestamp < expectedTimestamp)
  375. {
  376. return (null, null);
  377. }
  378. }
  379. int offset = Unsafe.SizeOf<TocHeader>() + programIndex * Unsafe.SizeOf<OffsetAndSize>();
  380. if (offset + Unsafe.SizeOf<OffsetAndSize>() > tocFileStream.Length)
  381. {
  382. return (null, null);
  383. }
  384. if ((ulong)offset >= (ulong)dataFileStream.Length)
  385. {
  386. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  387. }
  388. tocFileStream.Seek(offset, SeekOrigin.Begin);
  389. BinarySerializer tocReader = new BinarySerializer(tocFileStream);
  390. OffsetAndSize offsetAndSize = new OffsetAndSize();
  391. tocReader.Read(ref offsetAndSize);
  392. if (offsetAndSize.Offset >= (ulong)dataFileStream.Length)
  393. {
  394. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  395. }
  396. dataFileStream.Seek((long)offsetAndSize.Offset, SeekOrigin.Begin);
  397. byte[] hostCode = new byte[offsetAndSize.UncompressedSize];
  398. BinarySerializer.ReadCompressed(dataFileStream, hostCode);
  399. CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
  400. BinarySerializer dataReader = new BinarySerializer(dataFileStream);
  401. dataFileStream.Seek((long)(offsetAndSize.Offset + offsetAndSize.CompressedSize), SeekOrigin.Begin);
  402. dataReader.BeginCompression();
  403. for (int index = 0; index < guestShaders.Length; index++)
  404. {
  405. if (!guestShaders[index].HasValue)
  406. {
  407. continue;
  408. }
  409. GuestCodeAndCbData guestShader = guestShaders[index].Value;
  410. ShaderProgramInfo info = index != 0 || guestShaders.Length == 1 ? ReadShaderProgramInfo(ref dataReader) : null;
  411. shaders[index] = new CachedShaderStage(info, guestShader.Code, guestShader.Cb1Data);
  412. }
  413. dataReader.EndCompression();
  414. return (hostCode, shaders);
  415. }
  416. /// <summary>
  417. /// Gets output streams for the disk cache, for faster batch writing.
  418. /// </summary>
  419. /// <param name="context">The GPU context, used to determine the host disk cache</param>
  420. /// <returns>A collection of disk cache output streams</returns>
  421. public DiskCacheOutputStreams GetOutputStreams(GpuContext context)
  422. {
  423. var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  424. var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  425. var hostTocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  426. var hostDataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  427. return new DiskCacheOutputStreams(tocFileStream, dataFileStream, hostTocFileStream, hostDataFileStream);
  428. }
  429. /// <summary>
  430. /// Adds a shader to the cache.
  431. /// </summary>
  432. /// <param name="context">GPU context</param>
  433. /// <param name="program">Cached program</param>
  434. /// <param name="hostCode">Optional host binary code</param>
  435. /// <param name="streams">Output streams to use</param>
  436. public void AddShader(GpuContext context, CachedShaderProgram program, ReadOnlySpan<byte> hostCode, DiskCacheOutputStreams streams = null)
  437. {
  438. uint stagesBitMask = 0;
  439. for (int index = 0; index < program.Shaders.Length; index++)
  440. {
  441. var shader = program.Shaders[index];
  442. if (shader == null || (shader.Info != null && shader.Info.Stage == ShaderStage.Compute))
  443. {
  444. continue;
  445. }
  446. stagesBitMask |= 1u << index;
  447. }
  448. var tocFileStream = streams != null ? streams.TocFileStream : DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  449. var dataFileStream = streams != null ? streams.DataFileStream : DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  450. ulong timestamp = (ulong)DateTime.UtcNow.Subtract(DateTime.UnixEpoch).TotalSeconds;
  451. if (tocFileStream.Length == 0)
  452. {
  453. TocHeader header = new TocHeader();
  454. CreateToc(tocFileStream, ref header, TocsMagic, CodeGenVersion, timestamp);
  455. }
  456. tocFileStream.Seek(0, SeekOrigin.End);
  457. dataFileStream.Seek(0, SeekOrigin.End);
  458. BinarySerializer tocWriter = new BinarySerializer(tocFileStream);
  459. BinarySerializer dataWriter = new BinarySerializer(dataFileStream);
  460. ulong dataOffset = (ulong)dataFileStream.Position;
  461. tocWriter.Write(ref dataOffset);
  462. DataEntry entry = new DataEntry();
  463. entry.StagesBitMask = stagesBitMask;
  464. dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm());
  465. dataWriter.Write(ref entry);
  466. DataEntryPerStage stageEntry = new DataEntryPerStage();
  467. for (int index = 0; index < program.Shaders.Length; index++)
  468. {
  469. var shader = program.Shaders[index];
  470. if (shader == null)
  471. {
  472. continue;
  473. }
  474. stageEntry.GuestCodeIndex = _guestStorage.AddShader(shader.Code, shader.Cb1Data);
  475. dataWriter.Write(ref stageEntry);
  476. }
  477. program.SpecializationState.Write(ref dataWriter);
  478. dataWriter.EndCompression();
  479. if (streams == null)
  480. {
  481. tocFileStream.Dispose();
  482. dataFileStream.Dispose();
  483. }
  484. if (hostCode.IsEmpty)
  485. {
  486. return;
  487. }
  488. WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
  489. }
  490. /// <summary>
  491. /// Clears all content from the guest cache files.
  492. /// </summary>
  493. public void ClearGuestCache()
  494. {
  495. _guestStorage.ClearCache();
  496. }
  497. /// <summary>
  498. /// Clears all content from the shared cache files.
  499. /// </summary>
  500. /// <param name="context">GPU context</param>
  501. public void ClearSharedCache()
  502. {
  503. using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  504. using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  505. tocFileStream.SetLength(0);
  506. dataFileStream.SetLength(0);
  507. }
  508. /// <summary>
  509. /// Deletes all content from the host cache files.
  510. /// </summary>
  511. /// <param name="context">GPU context</param>
  512. public void ClearHostCache(GpuContext context)
  513. {
  514. using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  515. using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  516. tocFileStream.SetLength(0);
  517. dataFileStream.SetLength(0);
  518. }
  519. /// <summary>
  520. /// Writes the host binary code on the host cache.
  521. /// </summary>
  522. /// <param name="context">GPU context</param>
  523. /// <param name="hostCode">Host binary code</param>
  524. /// <param name="shaders">Shader stages to be added to the host cache</param>
  525. /// <param name="streams">Output streams to use</param>
  526. /// <param name="timestamp">File creation timestamp</param>
  527. private void WriteHostCode(
  528. GpuContext context,
  529. ReadOnlySpan<byte> hostCode,
  530. CachedShaderStage[] shaders,
  531. DiskCacheOutputStreams streams,
  532. ulong timestamp)
  533. {
  534. var tocFileStream = streams != null ? streams.HostTocFileStream : DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  535. var dataFileStream = streams != null ? streams.HostDataFileStream : DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  536. if (tocFileStream.Length == 0)
  537. {
  538. TocHeader header = new TocHeader();
  539. CreateToc(tocFileStream, ref header, TochMagic, 0, timestamp);
  540. }
  541. tocFileStream.Seek(0, SeekOrigin.End);
  542. dataFileStream.Seek(0, SeekOrigin.End);
  543. BinarySerializer tocWriter = new BinarySerializer(tocFileStream);
  544. BinarySerializer dataWriter = new BinarySerializer(dataFileStream);
  545. OffsetAndSize offsetAndSize = new OffsetAndSize();
  546. offsetAndSize.Offset = (ulong)dataFileStream.Position;
  547. offsetAndSize.UncompressedSize = (uint)hostCode.Length;
  548. long dataStartPosition = dataFileStream.Position;
  549. BinarySerializer.WriteCompressed(dataFileStream, hostCode, DiskCacheCommon.GetCompressionAlgorithm());
  550. offsetAndSize.CompressedSize = (uint)(dataFileStream.Position - dataStartPosition);
  551. tocWriter.Write(ref offsetAndSize);
  552. dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm());
  553. for (int index = 0; index < shaders.Length; index++)
  554. {
  555. if (shaders[index] != null)
  556. {
  557. WriteShaderProgramInfo(ref dataWriter, shaders[index].Info);
  558. }
  559. }
  560. dataWriter.EndCompression();
  561. if (streams == null)
  562. {
  563. tocFileStream.Dispose();
  564. dataFileStream.Dispose();
  565. }
  566. }
  567. /// <summary>
  568. /// Creates a TOC file for the host or shared cache.
  569. /// </summary>
  570. /// <param name="tocFileStream">TOC file stream</param>
  571. /// <param name="header">Set to the TOC file header</param>
  572. /// <param name="magic">Magic value to be written</param>
  573. /// <param name="codegenVersion">Shader codegen version, only valid for the host file</param>
  574. /// <param name="timestamp">File creation timestamp</param>
  575. private void CreateToc(Stream tocFileStream, ref TocHeader header, uint magic, uint codegenVersion, ulong timestamp)
  576. {
  577. BinarySerializer writer = new BinarySerializer(tocFileStream);
  578. header.Magic = magic;
  579. header.FormatVersion = FileFormatVersionPacked;
  580. header.CodeGenVersion = codegenVersion;
  581. header.Padding = 0;
  582. header.Reserved = 0;
  583. header.Timestamp = timestamp;
  584. if (tocFileStream.Length > 0)
  585. {
  586. tocFileStream.Seek(0, SeekOrigin.Begin);
  587. tocFileStream.SetLength(0);
  588. }
  589. writer.Write(ref header);
  590. }
  591. /// <summary>
  592. /// Reads the shader program info from the cache.
  593. /// </summary>
  594. /// <param name="dataReader">Cache data reader</param>
  595. /// <returns>Shader program info</returns>
  596. private static ShaderProgramInfo ReadShaderProgramInfo(ref BinarySerializer dataReader)
  597. {
  598. DataShaderInfo dataInfo = new DataShaderInfo();
  599. dataReader.ReadWithMagicAndSize(ref dataInfo, ShdiMagic);
  600. BufferDescriptor[] cBuffers = new BufferDescriptor[dataInfo.CBuffersCount];
  601. BufferDescriptor[] sBuffers = new BufferDescriptor[dataInfo.SBuffersCount];
  602. TextureDescriptor[] textures = new TextureDescriptor[dataInfo.TexturesCount];
  603. TextureDescriptor[] images = new TextureDescriptor[dataInfo.ImagesCount];
  604. for (int index = 0; index < dataInfo.CBuffersCount; index++)
  605. {
  606. dataReader.ReadWithMagicAndSize(ref cBuffers[index], BufdMagic);
  607. }
  608. for (int index = 0; index < dataInfo.SBuffersCount; index++)
  609. {
  610. dataReader.ReadWithMagicAndSize(ref sBuffers[index], BufdMagic);
  611. }
  612. for (int index = 0; index < dataInfo.TexturesCount; index++)
  613. {
  614. dataReader.ReadWithMagicAndSize(ref textures[index], TexdMagic);
  615. }
  616. for (int index = 0; index < dataInfo.ImagesCount; index++)
  617. {
  618. dataReader.ReadWithMagicAndSize(ref images[index], TexdMagic);
  619. }
  620. return new ShaderProgramInfo(
  621. cBuffers,
  622. sBuffers,
  623. textures,
  624. images,
  625. dataInfo.Stage,
  626. dataInfo.UsesInstanceId,
  627. dataInfo.UsesDrawParameters,
  628. dataInfo.UsesRtLayer,
  629. dataInfo.ClipDistancesWritten,
  630. dataInfo.FragmentOutputMap);
  631. }
  632. /// <summary>
  633. /// Writes the shader program info into the cache.
  634. /// </summary>
  635. /// <param name="dataWriter">Cache data writer</param>
  636. /// <param name="info">Program info</param>
  637. private static void WriteShaderProgramInfo(ref BinarySerializer dataWriter, ShaderProgramInfo info)
  638. {
  639. if (info == null)
  640. {
  641. return;
  642. }
  643. DataShaderInfo dataInfo = new DataShaderInfo();
  644. dataInfo.CBuffersCount = (ushort)info.CBuffers.Count;
  645. dataInfo.SBuffersCount = (ushort)info.SBuffers.Count;
  646. dataInfo.TexturesCount = (ushort)info.Textures.Count;
  647. dataInfo.ImagesCount = (ushort)info.Images.Count;
  648. dataInfo.Stage = info.Stage;
  649. dataInfo.UsesInstanceId = info.UsesInstanceId;
  650. dataInfo.UsesDrawParameters = info.UsesDrawParameters;
  651. dataInfo.UsesRtLayer = info.UsesRtLayer;
  652. dataInfo.ClipDistancesWritten = info.ClipDistancesWritten;
  653. dataInfo.FragmentOutputMap = info.FragmentOutputMap;
  654. dataWriter.WriteWithMagicAndSize(ref dataInfo, ShdiMagic);
  655. for (int index = 0; index < info.CBuffers.Count; index++)
  656. {
  657. var entry = info.CBuffers[index];
  658. dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic);
  659. }
  660. for (int index = 0; index < info.SBuffers.Count; index++)
  661. {
  662. var entry = info.SBuffers[index];
  663. dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic);
  664. }
  665. for (int index = 0; index < info.Textures.Count; index++)
  666. {
  667. var entry = info.Textures[index];
  668. dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic);
  669. }
  670. for (int index = 0; index < info.Images.Count; index++)
  671. {
  672. var entry = info.Images[index];
  673. dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic);
  674. }
  675. }
  676. }
  677. }