DiskCacheHostStorage.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830
  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 = 3807;
  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. }
  138. private readonly DiskCacheGuestStorage _guestStorage;
  139. /// <summary>
  140. /// Creates a disk cache host storage.
  141. /// </summary>
  142. /// <param name="basePath">Base path of the shader cache</param>
  143. public DiskCacheHostStorage(string basePath)
  144. {
  145. _basePath = basePath;
  146. _guestStorage = new DiskCacheGuestStorage(basePath);
  147. if (CacheEnabled)
  148. {
  149. Directory.CreateDirectory(basePath);
  150. }
  151. }
  152. /// <summary>
  153. /// Gets the total of host programs on the cache.
  154. /// </summary>
  155. /// <returns>Host programs count</returns>
  156. public int GetProgramCount()
  157. {
  158. string tocFilePath = Path.Combine(_basePath, SharedTocFileName);
  159. if (!File.Exists(tocFilePath))
  160. {
  161. return 0;
  162. }
  163. return Math.Max((int)((new FileInfo(tocFilePath).Length - Unsafe.SizeOf<TocHeader>()) / sizeof(ulong)), 0);
  164. }
  165. /// <summary>
  166. /// Guest the name of the host program cache file, with extension.
  167. /// </summary>
  168. /// <param name="context">GPU context</param>
  169. /// <returns>Name of the file, without extension</returns>
  170. private static string GetHostFileName(GpuContext context)
  171. {
  172. string apiName = context.Capabilities.Api.ToString().ToLowerInvariant();
  173. string vendorName = RemoveInvalidCharacters(context.Capabilities.VendorName.ToLowerInvariant());
  174. return $"{apiName}_{vendorName}";
  175. }
  176. /// <summary>
  177. /// Removes invalid path characters and spaces from a file name.
  178. /// </summary>
  179. /// <param name="fileName">File name</param>
  180. /// <returns>Filtered file name</returns>
  181. private static string RemoveInvalidCharacters(string fileName)
  182. {
  183. int indexOfSpace = fileName.IndexOf(' ');
  184. if (indexOfSpace >= 0)
  185. {
  186. fileName = fileName.Substring(0, indexOfSpace);
  187. }
  188. return string.Concat(fileName.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries));
  189. }
  190. /// <summary>
  191. /// Gets the name of the TOC host file.
  192. /// </summary>
  193. /// <param name="context">GPU context</param>
  194. /// <returns>File name</returns>
  195. private static string GetHostTocFileName(GpuContext context)
  196. {
  197. return GetHostFileName(context) + ".toc";
  198. }
  199. /// <summary>
  200. /// Gets the name of the data host file.
  201. /// </summary>
  202. /// <param name="context">GPU context</param>
  203. /// <returns>File name</returns>
  204. private static string GetHostDataFileName(GpuContext context)
  205. {
  206. return GetHostFileName(context) + ".data";
  207. }
  208. /// <summary>
  209. /// Checks if a disk cache exists for the current application.
  210. /// </summary>
  211. /// <returns>True if a disk cache exists, false otherwise</returns>
  212. public bool CacheExists()
  213. {
  214. string tocFilePath = Path.Combine(_basePath, SharedTocFileName);
  215. string dataFilePath = Path.Combine(_basePath, SharedDataFileName);
  216. if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath) || !_guestStorage.TocFileExists() || !_guestStorage.DataFileExists())
  217. {
  218. return false;
  219. }
  220. return true;
  221. }
  222. /// <summary>
  223. /// Loads all shaders from the cache.
  224. /// </summary>
  225. /// <param name="context">GPU context</param>
  226. /// <param name="loader">Parallel disk cache loader</param>
  227. public void LoadShaders(GpuContext context, ParallelDiskCacheLoader loader)
  228. {
  229. if (!CacheExists())
  230. {
  231. return;
  232. }
  233. Stream hostTocFileStream = null;
  234. Stream hostDataFileStream = null;
  235. try
  236. {
  237. using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: false);
  238. using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: false);
  239. using var guestTocFileStream = _guestStorage.OpenTocFileStream();
  240. using var guestDataFileStream = _guestStorage.OpenDataFileStream();
  241. BinarySerializer tocReader = new BinarySerializer(tocFileStream);
  242. BinarySerializer dataReader = new BinarySerializer(dataFileStream);
  243. TocHeader header = new TocHeader();
  244. if (!tocReader.TryRead(ref header) || header.Magic != TocsMagic)
  245. {
  246. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  247. }
  248. if (header.FormatVersion != FileFormatVersionPacked)
  249. {
  250. throw new DiskCacheLoadException(DiskCacheLoadResult.IncompatibleVersion);
  251. }
  252. bool loadHostCache = header.CodeGenVersion == CodeGenVersion;
  253. int programIndex = 0;
  254. DataEntry entry = new DataEntry();
  255. while (tocFileStream.Position < tocFileStream.Length && loader.Active)
  256. {
  257. ulong dataOffset = 0;
  258. tocReader.Read(ref dataOffset);
  259. if ((ulong)dataOffset >= (ulong)dataFileStream.Length)
  260. {
  261. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  262. }
  263. dataFileStream.Seek((long)dataOffset, SeekOrigin.Begin);
  264. dataReader.BeginCompression();
  265. dataReader.Read(ref entry);
  266. uint stagesBitMask = entry.StagesBitMask;
  267. if ((stagesBitMask & ~0x3fu) != 0)
  268. {
  269. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  270. }
  271. bool isCompute = stagesBitMask == 0;
  272. if (isCompute)
  273. {
  274. stagesBitMask = 1;
  275. }
  276. GuestCodeAndCbData?[] guestShaders = new GuestCodeAndCbData?[isCompute ? 1 : Constants.ShaderStages + 1];
  277. DataEntryPerStage stageEntry = new DataEntryPerStage();
  278. while (stagesBitMask != 0)
  279. {
  280. int stageIndex = BitOperations.TrailingZeroCount(stagesBitMask);
  281. dataReader.Read(ref stageEntry);
  282. guestShaders[stageIndex] = _guestStorage.LoadShader(
  283. guestTocFileStream,
  284. guestDataFileStream,
  285. stageEntry.GuestCodeIndex);
  286. stagesBitMask &= ~(1u << stageIndex);
  287. }
  288. ShaderSpecializationState specState = ShaderSpecializationState.Read(ref dataReader);
  289. dataReader.EndCompression();
  290. if (loadHostCache)
  291. {
  292. (byte[] hostCode, CachedShaderStage[] shaders) = ReadHostCode(
  293. context,
  294. ref hostTocFileStream,
  295. ref hostDataFileStream,
  296. guestShaders,
  297. programIndex,
  298. header.Timestamp);
  299. if (hostCode != null)
  300. {
  301. bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null;
  302. int fragmentOutputMap = hasFragmentShader ? shaders[5].Info.FragmentOutputMap : -1;
  303. ShaderInfo shaderInfo = specState.PipelineState.HasValue
  304. ? new ShaderInfo(fragmentOutputMap, specState.PipelineState.Value, fromCache: true)
  305. : new ShaderInfo(fragmentOutputMap, fromCache: true);
  306. IProgram hostProgram;
  307. if (context.Capabilities.Api == TargetApi.Vulkan)
  308. {
  309. ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode, isCompute);
  310. hostProgram = context.Renderer.CreateProgram(shaderSources, shaderInfo);
  311. }
  312. else
  313. {
  314. hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, shaderInfo);
  315. }
  316. CachedShaderProgram program = new CachedShaderProgram(hostProgram, specState, shaders);
  317. loader.QueueHostProgram(program, hostCode, programIndex, isCompute);
  318. }
  319. else
  320. {
  321. loadHostCache = false;
  322. }
  323. }
  324. if (!loadHostCache)
  325. {
  326. loader.QueueGuestProgram(guestShaders, specState, programIndex, isCompute);
  327. }
  328. loader.CheckCompilation();
  329. programIndex++;
  330. }
  331. }
  332. finally
  333. {
  334. _guestStorage.ClearMemoryCache();
  335. hostTocFileStream?.Dispose();
  336. hostDataFileStream?.Dispose();
  337. }
  338. }
  339. /// <summary>
  340. /// Reads the host code for a given shader, if existent.
  341. /// </summary>
  342. /// <param name="context">GPU context</param>
  343. /// <param name="tocFileStream">Host TOC file stream, intialized if needed</param>
  344. /// <param name="dataFileStream">Host data file stream, initialized if needed</param>
  345. /// <param name="guestShaders">Guest shader code for each active stage</param>
  346. /// <param name="programIndex">Index of the program on the cache</param>
  347. /// <param name="expectedTimestamp">Timestamp of the shared cache file. The host file must be newer than it</param>
  348. /// <returns>Host binary code, or null if not found</returns>
  349. private (byte[], CachedShaderStage[]) ReadHostCode(
  350. GpuContext context,
  351. ref Stream tocFileStream,
  352. ref Stream dataFileStream,
  353. GuestCodeAndCbData?[] guestShaders,
  354. int programIndex,
  355. ulong expectedTimestamp)
  356. {
  357. if (tocFileStream == null && dataFileStream == null)
  358. {
  359. string tocFilePath = Path.Combine(_basePath, GetHostTocFileName(context));
  360. string dataFilePath = Path.Combine(_basePath, GetHostDataFileName(context));
  361. if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath))
  362. {
  363. return (null, null);
  364. }
  365. tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: false);
  366. dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: false);
  367. BinarySerializer tempTocReader = new BinarySerializer(tocFileStream);
  368. TocHeader header = new TocHeader();
  369. tempTocReader.Read(ref header);
  370. if (header.Timestamp < expectedTimestamp)
  371. {
  372. return (null, null);
  373. }
  374. }
  375. int offset = Unsafe.SizeOf<TocHeader>() + programIndex * Unsafe.SizeOf<OffsetAndSize>();
  376. if (offset + Unsafe.SizeOf<OffsetAndSize>() > tocFileStream.Length)
  377. {
  378. return (null, null);
  379. }
  380. if ((ulong)offset >= (ulong)dataFileStream.Length)
  381. {
  382. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  383. }
  384. tocFileStream.Seek(offset, SeekOrigin.Begin);
  385. BinarySerializer tocReader = new BinarySerializer(tocFileStream);
  386. OffsetAndSize offsetAndSize = new OffsetAndSize();
  387. tocReader.Read(ref offsetAndSize);
  388. if (offsetAndSize.Offset >= (ulong)dataFileStream.Length)
  389. {
  390. throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric);
  391. }
  392. dataFileStream.Seek((long)offsetAndSize.Offset, SeekOrigin.Begin);
  393. byte[] hostCode = new byte[offsetAndSize.UncompressedSize];
  394. BinarySerializer.ReadCompressed(dataFileStream, hostCode);
  395. CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
  396. BinarySerializer dataReader = new BinarySerializer(dataFileStream);
  397. dataFileStream.Seek((long)(offsetAndSize.Offset + offsetAndSize.CompressedSize), SeekOrigin.Begin);
  398. dataReader.BeginCompression();
  399. for (int index = 0; index < guestShaders.Length; index++)
  400. {
  401. if (!guestShaders[index].HasValue)
  402. {
  403. continue;
  404. }
  405. GuestCodeAndCbData guestShader = guestShaders[index].Value;
  406. ShaderProgramInfo info = index != 0 || guestShaders.Length == 1 ? ReadShaderProgramInfo(ref dataReader) : null;
  407. shaders[index] = new CachedShaderStage(info, guestShader.Code, guestShader.Cb1Data);
  408. }
  409. dataReader.EndCompression();
  410. return (hostCode, shaders);
  411. }
  412. /// <summary>
  413. /// Gets output streams for the disk cache, for faster batch writing.
  414. /// </summary>
  415. /// <param name="context">The GPU context, used to determine the host disk cache</param>
  416. /// <returns>A collection of disk cache output streams</returns>
  417. public DiskCacheOutputStreams GetOutputStreams(GpuContext context)
  418. {
  419. var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  420. var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  421. var hostTocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  422. var hostDataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  423. return new DiskCacheOutputStreams(tocFileStream, dataFileStream, hostTocFileStream, hostDataFileStream);
  424. }
  425. /// <summary>
  426. /// Adds a shader to the cache.
  427. /// </summary>
  428. /// <param name="context">GPU context</param>
  429. /// <param name="program">Cached program</param>
  430. /// <param name="hostCode">Optional host binary code</param>
  431. /// <param name="streams">Output streams to use</param>
  432. public void AddShader(GpuContext context, CachedShaderProgram program, ReadOnlySpan<byte> hostCode, DiskCacheOutputStreams streams = null)
  433. {
  434. uint stagesBitMask = 0;
  435. for (int index = 0; index < program.Shaders.Length; index++)
  436. {
  437. var shader = program.Shaders[index];
  438. if (shader == null || (shader.Info != null && shader.Info.Stage == ShaderStage.Compute))
  439. {
  440. continue;
  441. }
  442. stagesBitMask |= 1u << index;
  443. }
  444. var tocFileStream = streams != null ? streams.TocFileStream : DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  445. var dataFileStream = streams != null ? streams.DataFileStream : DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  446. ulong timestamp = (ulong)DateTime.UtcNow.Subtract(DateTime.UnixEpoch).TotalSeconds;
  447. if (tocFileStream.Length == 0)
  448. {
  449. TocHeader header = new TocHeader();
  450. CreateToc(tocFileStream, ref header, TocsMagic, CodeGenVersion, timestamp);
  451. }
  452. tocFileStream.Seek(0, SeekOrigin.End);
  453. dataFileStream.Seek(0, SeekOrigin.End);
  454. BinarySerializer tocWriter = new BinarySerializer(tocFileStream);
  455. BinarySerializer dataWriter = new BinarySerializer(dataFileStream);
  456. ulong dataOffset = (ulong)dataFileStream.Position;
  457. tocWriter.Write(ref dataOffset);
  458. DataEntry entry = new DataEntry();
  459. entry.StagesBitMask = stagesBitMask;
  460. dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm());
  461. dataWriter.Write(ref entry);
  462. DataEntryPerStage stageEntry = new DataEntryPerStage();
  463. for (int index = 0; index < program.Shaders.Length; index++)
  464. {
  465. var shader = program.Shaders[index];
  466. if (shader == null)
  467. {
  468. continue;
  469. }
  470. stageEntry.GuestCodeIndex = _guestStorage.AddShader(shader.Code, shader.Cb1Data);
  471. dataWriter.Write(ref stageEntry);
  472. }
  473. program.SpecializationState.Write(ref dataWriter);
  474. dataWriter.EndCompression();
  475. if (streams == null)
  476. {
  477. tocFileStream.Dispose();
  478. dataFileStream.Dispose();
  479. }
  480. if (hostCode.IsEmpty)
  481. {
  482. return;
  483. }
  484. WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
  485. }
  486. /// <summary>
  487. /// Clears all content from the guest cache files.
  488. /// </summary>
  489. public void ClearGuestCache()
  490. {
  491. _guestStorage.ClearCache();
  492. }
  493. /// <summary>
  494. /// Clears all content from the shared cache files.
  495. /// </summary>
  496. /// <param name="context">GPU context</param>
  497. public void ClearSharedCache()
  498. {
  499. using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: true);
  500. using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: true);
  501. tocFileStream.SetLength(0);
  502. dataFileStream.SetLength(0);
  503. }
  504. /// <summary>
  505. /// Deletes all content from the host cache files.
  506. /// </summary>
  507. /// <param name="context">GPU context</param>
  508. public void ClearHostCache(GpuContext context)
  509. {
  510. using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  511. using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  512. tocFileStream.SetLength(0);
  513. dataFileStream.SetLength(0);
  514. }
  515. /// <summary>
  516. /// Writes the host binary code on the host cache.
  517. /// </summary>
  518. /// <param name="context">GPU context</param>
  519. /// <param name="hostCode">Host binary code</param>
  520. /// <param name="shaders">Shader stages to be added to the host cache</param>
  521. /// <param name="streams">Output streams to use</param>
  522. /// <param name="timestamp">File creation timestamp</param>
  523. private void WriteHostCode(
  524. GpuContext context,
  525. ReadOnlySpan<byte> hostCode,
  526. CachedShaderStage[] shaders,
  527. DiskCacheOutputStreams streams,
  528. ulong timestamp)
  529. {
  530. var tocFileStream = streams != null ? streams.HostTocFileStream : DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: true);
  531. var dataFileStream = streams != null ? streams.HostDataFileStream : DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: true);
  532. if (tocFileStream.Length == 0)
  533. {
  534. TocHeader header = new TocHeader();
  535. CreateToc(tocFileStream, ref header, TochMagic, 0, timestamp);
  536. }
  537. tocFileStream.Seek(0, SeekOrigin.End);
  538. dataFileStream.Seek(0, SeekOrigin.End);
  539. BinarySerializer tocWriter = new BinarySerializer(tocFileStream);
  540. BinarySerializer dataWriter = new BinarySerializer(dataFileStream);
  541. OffsetAndSize offsetAndSize = new OffsetAndSize();
  542. offsetAndSize.Offset = (ulong)dataFileStream.Position;
  543. offsetAndSize.UncompressedSize = (uint)hostCode.Length;
  544. long dataStartPosition = dataFileStream.Position;
  545. BinarySerializer.WriteCompressed(dataFileStream, hostCode, DiskCacheCommon.GetCompressionAlgorithm());
  546. offsetAndSize.CompressedSize = (uint)(dataFileStream.Position - dataStartPosition);
  547. tocWriter.Write(ref offsetAndSize);
  548. dataWriter.BeginCompression(DiskCacheCommon.GetCompressionAlgorithm());
  549. for (int index = 0; index < shaders.Length; index++)
  550. {
  551. if (shaders[index] != null)
  552. {
  553. WriteShaderProgramInfo(ref dataWriter, shaders[index].Info);
  554. }
  555. }
  556. dataWriter.EndCompression();
  557. if (streams == null)
  558. {
  559. tocFileStream.Dispose();
  560. dataFileStream.Dispose();
  561. }
  562. }
  563. /// <summary>
  564. /// Creates a TOC file for the host or shared cache.
  565. /// </summary>
  566. /// <param name="tocFileStream">TOC file stream</param>
  567. /// <param name="header">Set to the TOC file header</param>
  568. /// <param name="magic">Magic value to be written</param>
  569. /// <param name="codegenVersion">Shader codegen version, only valid for the host file</param>
  570. /// <param name="timestamp">File creation timestamp</param>
  571. private void CreateToc(Stream tocFileStream, ref TocHeader header, uint magic, uint codegenVersion, ulong timestamp)
  572. {
  573. BinarySerializer writer = new BinarySerializer(tocFileStream);
  574. header.Magic = magic;
  575. header.FormatVersion = FileFormatVersionPacked;
  576. header.CodeGenVersion = codegenVersion;
  577. header.Padding = 0;
  578. header.Reserved = 0;
  579. header.Timestamp = timestamp;
  580. if (tocFileStream.Length > 0)
  581. {
  582. tocFileStream.Seek(0, SeekOrigin.Begin);
  583. tocFileStream.SetLength(0);
  584. }
  585. writer.Write(ref header);
  586. }
  587. /// <summary>
  588. /// Reads the shader program info from the cache.
  589. /// </summary>
  590. /// <param name="dataReader">Cache data reader</param>
  591. /// <returns>Shader program info</returns>
  592. private static ShaderProgramInfo ReadShaderProgramInfo(ref BinarySerializer dataReader)
  593. {
  594. DataShaderInfo dataInfo = new DataShaderInfo();
  595. dataReader.ReadWithMagicAndSize(ref dataInfo, ShdiMagic);
  596. BufferDescriptor[] cBuffers = new BufferDescriptor[dataInfo.CBuffersCount];
  597. BufferDescriptor[] sBuffers = new BufferDescriptor[dataInfo.SBuffersCount];
  598. TextureDescriptor[] textures = new TextureDescriptor[dataInfo.TexturesCount];
  599. TextureDescriptor[] images = new TextureDescriptor[dataInfo.ImagesCount];
  600. for (int index = 0; index < dataInfo.CBuffersCount; index++)
  601. {
  602. dataReader.ReadWithMagicAndSize(ref cBuffers[index], BufdMagic);
  603. }
  604. for (int index = 0; index < dataInfo.SBuffersCount; index++)
  605. {
  606. dataReader.ReadWithMagicAndSize(ref sBuffers[index], BufdMagic);
  607. }
  608. for (int index = 0; index < dataInfo.TexturesCount; index++)
  609. {
  610. dataReader.ReadWithMagicAndSize(ref textures[index], TexdMagic);
  611. }
  612. for (int index = 0; index < dataInfo.ImagesCount; index++)
  613. {
  614. dataReader.ReadWithMagicAndSize(ref images[index], TexdMagic);
  615. }
  616. return new ShaderProgramInfo(
  617. cBuffers,
  618. sBuffers,
  619. textures,
  620. images,
  621. dataInfo.Stage,
  622. dataInfo.UsesInstanceId,
  623. dataInfo.UsesRtLayer,
  624. dataInfo.ClipDistancesWritten,
  625. dataInfo.FragmentOutputMap);
  626. }
  627. /// <summary>
  628. /// Writes the shader program info into the cache.
  629. /// </summary>
  630. /// <param name="dataWriter">Cache data writer</param>
  631. /// <param name="info">Program info</param>
  632. private static void WriteShaderProgramInfo(ref BinarySerializer dataWriter, ShaderProgramInfo info)
  633. {
  634. if (info == null)
  635. {
  636. return;
  637. }
  638. DataShaderInfo dataInfo = new DataShaderInfo();
  639. dataInfo.CBuffersCount = (ushort)info.CBuffers.Count;
  640. dataInfo.SBuffersCount = (ushort)info.SBuffers.Count;
  641. dataInfo.TexturesCount = (ushort)info.Textures.Count;
  642. dataInfo.ImagesCount = (ushort)info.Images.Count;
  643. dataInfo.Stage = info.Stage;
  644. dataInfo.UsesInstanceId = info.UsesInstanceId;
  645. dataInfo.UsesRtLayer = info.UsesRtLayer;
  646. dataInfo.ClipDistancesWritten = info.ClipDistancesWritten;
  647. dataInfo.FragmentOutputMap = info.FragmentOutputMap;
  648. dataWriter.WriteWithMagicAndSize(ref dataInfo, ShdiMagic);
  649. for (int index = 0; index < info.CBuffers.Count; index++)
  650. {
  651. var entry = info.CBuffers[index];
  652. dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic);
  653. }
  654. for (int index = 0; index < info.SBuffers.Count; index++)
  655. {
  656. var entry = info.SBuffers[index];
  657. dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic);
  658. }
  659. for (int index = 0; index < info.Textures.Count; index++)
  660. {
  661. var entry = info.Textures[index];
  662. dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic);
  663. }
  664. for (int index = 0; index < info.Images.Count; index++)
  665. {
  666. var entry = info.Images[index];
  667. dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic);
  668. }
  669. }
  670. }
  671. }