DiskCacheHostStorage.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  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 = 5311;
  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. ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(
  306. context,
  307. shaders,
  308. specState.PipelineState,
  309. specState.TransformFeedbackDescriptors != null);
  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. bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
  319. hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
  320. }
  321. CachedShaderProgram program = new CachedShaderProgram(hostProgram, specState, shaders);
  322. loader.QueueHostProgram(program, hostCode, programIndex, isCompute);
  323. }
  324. else
  325. {
  326. loadHostCache = false;
  327. }
  328. }
  329. if (!loadHostCache)
  330. {
  331. loader.QueueGuestProgram(guestShaders, specState, programIndex, isCompute);
  332. }
  333. loader.CheckCompilation();
  334. programIndex++;
  335. }
  336. }
  337. finally
  338. {
  339. _guestStorage.ClearMemoryCache();
  340. hostTocFileStream?.Dispose();
  341. hostDataFileStream?.Dispose();
  342. }
  343. }
  344. /// <summary>
  345. /// Reads the host code for a given shader, if existent.
  346. /// </summary>
  347. /// <param name="context">GPU context</param>
  348. /// <param name="tocFileStream">Host TOC file stream, intialized if needed</param>
  349. /// <param name="dataFileStream">Host data file stream, initialized if needed</param>
  350. /// <param name="guestShaders">Guest shader code for each active stage</param>
  351. /// <param name="programIndex">Index of the program on the cache</param>
  352. /// <param name="expectedTimestamp">Timestamp of the shared cache file. The host file must be newer than it</param>
  353. /// <returns>Host binary code, or null if not found</returns>
  354. private (byte[], CachedShaderStage[]) ReadHostCode(
  355. GpuContext context,
  356. ref Stream tocFileStream,
  357. ref Stream dataFileStream,
  358. GuestCodeAndCbData?[] guestShaders,
  359. int programIndex,
  360. ulong expectedTimestamp)
  361. {
  362. if (tocFileStream == null && dataFileStream == null)
  363. {
  364. string tocFilePath = Path.Combine(_basePath, GetHostTocFileName(context));
  365. string dataFilePath = Path.Combine(_basePath, GetHostDataFileName(context));
  366. if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath))
  367. {
  368. return (null, null);
  369. }
  370. tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: false);
  371. dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: false);
  372. BinarySerializer tempTocReader = new BinarySerializer(tocFileStream);
  373. TocHeader header = new TocHeader();
  374. tempTocReader.Read(ref header);
  375. if (header.Timestamp < expectedTimestamp)
  376. {
  377. return (null, null);
  378. }
  379. }
  380. int offset = Unsafe.SizeOf<TocHeader>() + programIndex * Unsafe.SizeOf<OffsetAndSize>();
  381. if (offset + Unsafe.SizeOf<OffsetAndSize>() > tocFileStream.Length)
  382. {
  383. return (null, null);
  384. }
  385. if ((ulong)offset >= (ulong)dataFileStream.Length)
  386. {
  387. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  388. }
  389. tocFileStream.Seek(offset, SeekOrigin.Begin);
  390. BinarySerializer tocReader = new BinarySerializer(tocFileStream);
  391. OffsetAndSize offsetAndSize = new OffsetAndSize();
  392. tocReader.Read(ref offsetAndSize);
  393. if (offsetAndSize.Offset >= (ulong)dataFileStream.Length)
  394. {
  395. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  396. }
  397. dataFileStream.Seek((long)offsetAndSize.Offset, SeekOrigin.Begin);
  398. byte[] hostCode = new byte[offsetAndSize.UncompressedSize];
  399. BinarySerializer.ReadCompressed(dataFileStream, hostCode);
  400. CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
  401. BinarySerializer dataReader = new BinarySerializer(dataFileStream);
  402. dataFileStream.Seek((long)(offsetAndSize.Offset + offsetAndSize.CompressedSize), SeekOrigin.Begin);
  403. dataReader.BeginCompression();
  404. for (int index = 0; index < guestShaders.Length; index++)
  405. {
  406. if (!guestShaders[index].HasValue)
  407. {
  408. continue;
  409. }
  410. GuestCodeAndCbData guestShader = guestShaders[index].Value;
  411. ShaderProgramInfo info = index != 0 || guestShaders.Length == 1 ? ReadShaderProgramInfo(ref dataReader) : null;
  412. shaders[index] = new CachedShaderStage(info, guestShader.Code, guestShader.Cb1Data);
  413. }
  414. dataReader.EndCompression();
  415. return (hostCode, shaders);
  416. }
  417. /// <summary>
  418. /// Gets output streams for the disk cache, for faster batch writing.
  419. /// </summary>
  420. /// <param name="context">The GPU context, used to determine the host disk cache</param>
  421. /// <returns>A collection of disk cache output streams</returns>
  422. public DiskCacheOutputStreams GetOutputStreams(GpuContext context)
  423. {
  424. var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  425. var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  426. var hostTocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  427. var hostDataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  428. return new DiskCacheOutputStreams(tocFileStream, dataFileStream, hostTocFileStream, hostDataFileStream);
  429. }
  430. /// <summary>
  431. /// Adds a shader to the cache.
  432. /// </summary>
  433. /// <param name="context">GPU context</param>
  434. /// <param name="program">Cached program</param>
  435. /// <param name="hostCode">Optional host binary code</param>
  436. /// <param name="streams">Output streams to use</param>
  437. public void AddShader(GpuContext context, CachedShaderProgram program, ReadOnlySpan<byte> hostCode, DiskCacheOutputStreams streams = null)
  438. {
  439. uint stagesBitMask = 0;
  440. for (int index = 0; index < program.Shaders.Length; index++)
  441. {
  442. var shader = program.Shaders[index];
  443. if (shader == null || (shader.Info != null && shader.Info.Stage == ShaderStage.Compute))
  444. {
  445. continue;
  446. }
  447. stagesBitMask |= 1u << index;
  448. }
  449. var tocFileStream = streams != null ? streams.TocFileStream : DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  450. var dataFileStream = streams != null ? streams.DataFileStream : DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  451. ulong timestamp = (ulong)DateTime.UtcNow.Subtract(DateTime.UnixEpoch).TotalSeconds;
  452. if (tocFileStream.Length == 0)
  453. {
  454. TocHeader header = new TocHeader();
  455. CreateToc(tocFileStream, ref header, TocsMagic, CodeGenVersion, timestamp);
  456. }
  457. tocFileStream.Seek(0, SeekOrigin.End);
  458. dataFileStream.Seek(0, SeekOrigin.End);
  459. BinarySerializer tocWriter = new BinarySerializer(tocFileStream);
  460. BinarySerializer dataWriter = new BinarySerializer(dataFileStream);
  461. ulong dataOffset = (ulong)dataFileStream.Position;
  462. tocWriter.Write(ref dataOffset);
  463. DataEntry entry = new DataEntry();
  464. entry.StagesBitMask = stagesBitMask;
  465. dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm());
  466. dataWriter.Write(ref entry);
  467. DataEntryPerStage stageEntry = new DataEntryPerStage();
  468. for (int index = 0; index < program.Shaders.Length; index++)
  469. {
  470. var shader = program.Shaders[index];
  471. if (shader == null)
  472. {
  473. continue;
  474. }
  475. stageEntry.GuestCodeIndex = _guestStorage.AddShader(shader.Code, shader.Cb1Data);
  476. dataWriter.Write(ref stageEntry);
  477. }
  478. program.SpecializationState.Write(ref dataWriter);
  479. dataWriter.EndCompression();
  480. if (streams == null)
  481. {
  482. tocFileStream.Dispose();
  483. dataFileStream.Dispose();
  484. }
  485. if (hostCode.IsEmpty)
  486. {
  487. return;
  488. }
  489. WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
  490. }
  491. /// <summary>
  492. /// Clears all content from the guest cache files.
  493. /// </summary>
  494. public void ClearGuestCache()
  495. {
  496. _guestStorage.ClearCache();
  497. }
  498. /// <summary>
  499. /// Clears all content from the shared cache files.
  500. /// </summary>
  501. /// <param name="context">GPU context</param>
  502. public void ClearSharedCache()
  503. {
  504. using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  505. using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  506. tocFileStream.SetLength(0);
  507. dataFileStream.SetLength(0);
  508. }
  509. /// <summary>
  510. /// Deletes all content from the host cache files.
  511. /// </summary>
  512. /// <param name="context">GPU context</param>
  513. public void ClearHostCache(GpuContext context)
  514. {
  515. using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  516. using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  517. tocFileStream.SetLength(0);
  518. dataFileStream.SetLength(0);
  519. }
  520. /// <summary>
  521. /// Writes the host binary code on the host cache.
  522. /// </summary>
  523. /// <param name="context">GPU context</param>
  524. /// <param name="hostCode">Host binary code</param>
  525. /// <param name="shaders">Shader stages to be added to the host cache</param>
  526. /// <param name="streams">Output streams to use</param>
  527. /// <param name="timestamp">File creation timestamp</param>
  528. private void WriteHostCode(
  529. GpuContext context,
  530. ReadOnlySpan<byte> hostCode,
  531. CachedShaderStage[] shaders,
  532. DiskCacheOutputStreams streams,
  533. ulong timestamp)
  534. {
  535. var tocFileStream = streams != null ? streams.HostTocFileStream : DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  536. var dataFileStream = streams != null ? streams.HostDataFileStream : DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  537. if (tocFileStream.Length == 0)
  538. {
  539. TocHeader header = new TocHeader();
  540. CreateToc(tocFileStream, ref header, TochMagic, 0, timestamp);
  541. }
  542. tocFileStream.Seek(0, SeekOrigin.End);
  543. dataFileStream.Seek(0, SeekOrigin.End);
  544. BinarySerializer tocWriter = new BinarySerializer(tocFileStream);
  545. BinarySerializer dataWriter = new BinarySerializer(dataFileStream);
  546. OffsetAndSize offsetAndSize = new OffsetAndSize();
  547. offsetAndSize.Offset = (ulong)dataFileStream.Position;
  548. offsetAndSize.UncompressedSize = (uint)hostCode.Length;
  549. long dataStartPosition = dataFileStream.Position;
  550. BinarySerializer.WriteCompressed(dataFileStream, hostCode, DiskCacheCommon.GetCompressionAlgorithm());
  551. offsetAndSize.CompressedSize = (uint)(dataFileStream.Position - dataStartPosition);
  552. tocWriter.Write(ref offsetAndSize);
  553. dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm());
  554. for (int index = 0; index < shaders.Length; index++)
  555. {
  556. if (shaders[index] != null)
  557. {
  558. WriteShaderProgramInfo(ref dataWriter, shaders[index].Info);
  559. }
  560. }
  561. dataWriter.EndCompression();
  562. if (streams == null)
  563. {
  564. tocFileStream.Dispose();
  565. dataFileStream.Dispose();
  566. }
  567. }
  568. /// <summary>
  569. /// Creates a TOC file for the host or shared cache.
  570. /// </summary>
  571. /// <param name="tocFileStream">TOC file stream</param>
  572. /// <param name="header">Set to the TOC file header</param>
  573. /// <param name="magic">Magic value to be written</param>
  574. /// <param name="codegenVersion">Shader codegen version, only valid for the host file</param>
  575. /// <param name="timestamp">File creation timestamp</param>
  576. private void CreateToc(Stream tocFileStream, ref TocHeader header, uint magic, uint codegenVersion, ulong timestamp)
  577. {
  578. BinarySerializer writer = new BinarySerializer(tocFileStream);
  579. header.Magic = magic;
  580. header.FormatVersion = FileFormatVersionPacked;
  581. header.CodeGenVersion = codegenVersion;
  582. header.Padding = 0;
  583. header.Reserved = 0;
  584. header.Timestamp = timestamp;
  585. if (tocFileStream.Length > 0)
  586. {
  587. tocFileStream.Seek(0, SeekOrigin.Begin);
  588. tocFileStream.SetLength(0);
  589. }
  590. writer.Write(ref header);
  591. }
  592. /// <summary>
  593. /// Reads the shader program info from the cache.
  594. /// </summary>
  595. /// <param name="dataReader">Cache data reader</param>
  596. /// <returns>Shader program info</returns>
  597. private static ShaderProgramInfo ReadShaderProgramInfo(ref BinarySerializer dataReader)
  598. {
  599. DataShaderInfo dataInfo = new DataShaderInfo();
  600. dataReader.ReadWithMagicAndSize(ref dataInfo, ShdiMagic);
  601. BufferDescriptor[] cBuffers = new BufferDescriptor[dataInfo.CBuffersCount];
  602. BufferDescriptor[] sBuffers = new BufferDescriptor[dataInfo.SBuffersCount];
  603. TextureDescriptor[] textures = new TextureDescriptor[dataInfo.TexturesCount];
  604. TextureDescriptor[] images = new TextureDescriptor[dataInfo.ImagesCount];
  605. for (int index = 0; index < dataInfo.CBuffersCount; index++)
  606. {
  607. dataReader.ReadWithMagicAndSize(ref cBuffers[index], BufdMagic);
  608. }
  609. for (int index = 0; index < dataInfo.SBuffersCount; index++)
  610. {
  611. dataReader.ReadWithMagicAndSize(ref sBuffers[index], BufdMagic);
  612. }
  613. for (int index = 0; index < dataInfo.TexturesCount; index++)
  614. {
  615. dataReader.ReadWithMagicAndSize(ref textures[index], TexdMagic);
  616. }
  617. for (int index = 0; index < dataInfo.ImagesCount; index++)
  618. {
  619. dataReader.ReadWithMagicAndSize(ref images[index], TexdMagic);
  620. }
  621. return new ShaderProgramInfo(
  622. cBuffers,
  623. sBuffers,
  624. textures,
  625. images,
  626. ShaderIdentification.None,
  627. 0,
  628. dataInfo.Stage,
  629. dataInfo.UsesInstanceId,
  630. dataInfo.UsesDrawParameters,
  631. dataInfo.UsesRtLayer,
  632. dataInfo.ClipDistancesWritten,
  633. dataInfo.FragmentOutputMap);
  634. }
  635. /// <summary>
  636. /// Writes the shader program info into the cache.
  637. /// </summary>
  638. /// <param name="dataWriter">Cache data writer</param>
  639. /// <param name="info">Program info</param>
  640. private static void WriteShaderProgramInfo(ref BinarySerializer dataWriter, ShaderProgramInfo info)
  641. {
  642. if (info == null)
  643. {
  644. return;
  645. }
  646. DataShaderInfo dataInfo = new DataShaderInfo();
  647. dataInfo.CBuffersCount = (ushort)info.CBuffers.Count;
  648. dataInfo.SBuffersCount = (ushort)info.SBuffers.Count;
  649. dataInfo.TexturesCount = (ushort)info.Textures.Count;
  650. dataInfo.ImagesCount = (ushort)info.Images.Count;
  651. dataInfo.Stage = info.Stage;
  652. dataInfo.UsesInstanceId = info.UsesInstanceId;
  653. dataInfo.UsesDrawParameters = info.UsesDrawParameters;
  654. dataInfo.UsesRtLayer = info.UsesRtLayer;
  655. dataInfo.ClipDistancesWritten = info.ClipDistancesWritten;
  656. dataInfo.FragmentOutputMap = info.FragmentOutputMap;
  657. dataWriter.WriteWithMagicAndSize(ref dataInfo, ShdiMagic);
  658. for (int index = 0; index < info.CBuffers.Count; index++)
  659. {
  660. var entry = info.CBuffers[index];
  661. dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic);
  662. }
  663. for (int index = 0; index < info.SBuffers.Count; index++)
  664. {
  665. var entry = info.SBuffers[index];
  666. dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic);
  667. }
  668. for (int index = 0; index < info.Textures.Count; index++)
  669. {
  670. var entry = info.Textures[index];
  671. dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic);
  672. }
  673. for (int index = 0; index < info.Images.Count; index++)
  674. {
  675. var entry = info.Images[index];
  676. dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic);
  677. }
  678. }
  679. }
  680. }