DiskCacheHostStorage.cs 28 KB

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