ShaderSpecializationState.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. using Ryujinx.Common.Memory;
  2. using Ryujinx.Graphics.Gpu.Image;
  3. using Ryujinx.Graphics.Gpu.Memory;
  4. using Ryujinx.Graphics.GAL;
  5. using Ryujinx.Graphics.Gpu.Shader.DiskCache;
  6. using Ryujinx.Graphics.Shader;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Numerics;
  11. using System.Runtime.CompilerServices;
  12. using System.Runtime.InteropServices;
  13. namespace Ryujinx.Graphics.Gpu.Shader
  14. {
  15. class ShaderSpecializationState
  16. {
  17. private const uint ComsMagic = (byte)'C' | ((byte)'O' << 8) | ((byte)'M' << 16) | ((byte)'S' << 24);
  18. private const uint GfxsMagic = (byte)'G' | ((byte)'F' << 8) | ((byte)'X' << 16) | ((byte)'S' << 24);
  19. private const uint TfbdMagic = (byte)'T' | ((byte)'F' << 8) | ((byte)'B' << 16) | ((byte)'D' << 24);
  20. private const uint TexkMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'K' << 24);
  21. private const uint TexsMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'S' << 24);
  22. private const uint PgpsMagic = (byte)'P' | ((byte)'G' << 8) | ((byte)'P' << 16) | ((byte)'S' << 24);
  23. /// <summary>
  24. /// Flags indicating GPU state that is used by the shader.
  25. /// </summary>
  26. [Flags]
  27. private enum QueriedStateFlags
  28. {
  29. EarlyZForce = 1 << 0,
  30. PrimitiveTopology = 1 << 1,
  31. TessellationMode = 1 << 2,
  32. TransformFeedback = 1 << 3
  33. }
  34. private QueriedStateFlags _queriedState;
  35. private bool _compute;
  36. private byte _constantBufferUsePerStage;
  37. /// <summary>
  38. /// Compute engine state.
  39. /// </summary>
  40. public GpuChannelComputeState ComputeState;
  41. /// <summary>
  42. /// 3D engine state.
  43. /// </summary>
  44. public GpuChannelGraphicsState GraphicsState;
  45. /// <summary>
  46. /// Contant buffers bound at the time the shader was compiled, per stage.
  47. /// </summary>
  48. public Array5<uint> ConstantBufferUse;
  49. /// <summary>
  50. /// Pipeline state captured at the time of shader use.
  51. /// </summary>
  52. public ProgramPipelineState? PipelineState;
  53. /// <summary>
  54. /// Transform feedback buffers active at the time the shader was compiled.
  55. /// </summary>
  56. public TransformFeedbackDescriptor[] TransformFeedbackDescriptors;
  57. /// <summary>
  58. /// Flags indicating texture state that is used by the shader.
  59. /// </summary>
  60. [Flags]
  61. private enum QueriedTextureStateFlags
  62. {
  63. TextureFormat = 1 << 0,
  64. SamplerType = 1 << 1,
  65. CoordNormalized = 1 << 2
  66. }
  67. /// <summary>
  68. /// Reference type wrapping a value.
  69. /// </summary>
  70. private class Box<T>
  71. {
  72. /// <summary>
  73. /// Wrapped value.
  74. /// </summary>
  75. public T Value;
  76. }
  77. /// <summary>
  78. /// State of a texture or image that is accessed by the shader.
  79. /// </summary>
  80. private struct TextureSpecializationState
  81. {
  82. // New fields should be added to the end of the struct to keep disk shader cache compatibility.
  83. /// <summary>
  84. /// Flags indicating which state of the texture the shader depends on.
  85. /// </summary>
  86. public QueriedTextureStateFlags QueriedFlags;
  87. /// <summary>
  88. /// Encoded texture format value.
  89. /// </summary>
  90. public uint Format;
  91. /// <summary>
  92. /// True if the texture format is sRGB, false otherwise.
  93. /// </summary>
  94. public bool FormatSrgb;
  95. /// <summary>
  96. /// Texture target.
  97. /// </summary>
  98. public Image.TextureTarget TextureTarget;
  99. /// <summary>
  100. /// Indicates if the coordinates used to sample the texture are normalized or not (0.0..1.0 or 0..Width/Height).
  101. /// </summary>
  102. public bool CoordNormalized;
  103. }
  104. /// <summary>
  105. /// Texture binding information, used to identify each texture accessed by the shader.
  106. /// </summary>
  107. private struct TextureKey : IEquatable<TextureKey>
  108. {
  109. // New fields should be added to the end of the struct to keep disk shader cache compatibility.
  110. /// <summary>
  111. /// Shader stage where the texture is used.
  112. /// </summary>
  113. public readonly int StageIndex;
  114. /// <summary>
  115. /// Texture handle offset in words on the texture buffer.
  116. /// </summary>
  117. public readonly int Handle;
  118. /// <summary>
  119. /// Constant buffer slot of the texture buffer (-1 to use the texture buffer index GPU register).
  120. /// </summary>
  121. public readonly int CbufSlot;
  122. /// <summary>
  123. /// Creates a new texture key.
  124. /// </summary>
  125. /// <param name="stageIndex">Shader stage where the texture is used</param>
  126. /// <param name="handle">Texture handle offset in words on the texture buffer</param>
  127. /// <param name="cbufSlot">Constant buffer slot of the texture buffer (-1 to use the texture buffer index GPU register)</param>
  128. public TextureKey(int stageIndex, int handle, int cbufSlot)
  129. {
  130. StageIndex = stageIndex;
  131. Handle = handle;
  132. CbufSlot = cbufSlot;
  133. }
  134. public override bool Equals(object obj)
  135. {
  136. return obj is TextureKey textureKey && Equals(textureKey);
  137. }
  138. public bool Equals(TextureKey other)
  139. {
  140. return StageIndex == other.StageIndex && Handle == other.Handle && CbufSlot == other.CbufSlot;
  141. }
  142. public override int GetHashCode()
  143. {
  144. return HashCode.Combine(StageIndex, Handle, CbufSlot);
  145. }
  146. }
  147. private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
  148. private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures;
  149. private Box<TextureSpecializationState>[][] _textureByBinding;
  150. private Box<TextureSpecializationState>[][] _imageByBinding;
  151. /// <summary>
  152. /// Creates a new instance of the shader specialization state.
  153. /// </summary>
  154. private ShaderSpecializationState()
  155. {
  156. _textureSpecialization = new Dictionary<TextureKey, Box<TextureSpecializationState>>();
  157. }
  158. /// <summary>
  159. /// Creates a new instance of the shader specialization state.
  160. /// </summary>
  161. /// <param name="state">Current compute engine state</param>
  162. public ShaderSpecializationState(ref GpuChannelComputeState state) : this()
  163. {
  164. ComputeState = state;
  165. _compute = true;
  166. }
  167. /// <summary>
  168. /// Creates a new instance of the shader specialization state.
  169. /// </summary>
  170. /// <param name="state">Current 3D engine state</param>
  171. /// <param name="descriptors">Optional transform feedback buffers in use, if any</param>
  172. private ShaderSpecializationState(ref GpuChannelGraphicsState state, TransformFeedbackDescriptor[] descriptors) : this()
  173. {
  174. GraphicsState = state;
  175. _compute = false;
  176. if (descriptors != null)
  177. {
  178. TransformFeedbackDescriptors = descriptors;
  179. _queriedState |= QueriedStateFlags.TransformFeedback;
  180. }
  181. }
  182. /// <summary>
  183. /// Prepare the shader specialization state for quick binding lookups.
  184. /// </summary>
  185. /// <param name="stages">The shader stages</param>
  186. public void Prepare(CachedShaderStage[] stages)
  187. {
  188. _allTextures = _textureSpecialization.ToArray();
  189. _textureByBinding = new Box<TextureSpecializationState>[stages.Length][];
  190. _imageByBinding = new Box<TextureSpecializationState>[stages.Length][];
  191. for (int i = 0; i < stages.Length; i++)
  192. {
  193. CachedShaderStage stage = stages[i];
  194. if (stage?.Info != null)
  195. {
  196. var textures = stage.Info.Textures;
  197. var images = stage.Info.Images;
  198. var texBindings = new Box<TextureSpecializationState>[textures.Count];
  199. var imageBindings = new Box<TextureSpecializationState>[images.Count];
  200. int stageIndex = Math.Max(i - 1, 0); // Don't count VertexA for looking up spec state. No-Op for compute.
  201. for (int j = 0; j < textures.Count; j++)
  202. {
  203. var texture = textures[j];
  204. texBindings[j] = GetTextureSpecState(stageIndex, texture.HandleIndex, texture.CbufSlot);
  205. }
  206. for (int j = 0; j < images.Count; j++)
  207. {
  208. var image = images[j];
  209. imageBindings[j] = GetTextureSpecState(stageIndex, image.HandleIndex, image.CbufSlot);
  210. }
  211. _textureByBinding[i] = texBindings;
  212. _imageByBinding[i] = imageBindings;
  213. }
  214. }
  215. }
  216. /// <summary>
  217. /// Creates a new instance of the shader specialization state.
  218. /// </summary>
  219. /// <param name="state">Current 3D engine state</param>
  220. /// <param name="pipelineState">Current program pipeline state</param>
  221. /// <param name="descriptors">Optional transform feedback buffers in use, if any</param>
  222. public ShaderSpecializationState(
  223. ref GpuChannelGraphicsState state,
  224. ref ProgramPipelineState pipelineState,
  225. TransformFeedbackDescriptor[] descriptors) : this(ref state, descriptors)
  226. {
  227. PipelineState = pipelineState;
  228. }
  229. /// <summary>
  230. /// Creates a new instance of the shader specialization state.
  231. /// </summary>
  232. /// <param name="state">Current 3D engine state</param>
  233. /// <param name="pipelineState">Current program pipeline state</param>
  234. /// <param name="descriptors">Optional transform feedback buffers in use, if any</param>
  235. public ShaderSpecializationState(
  236. ref GpuChannelGraphicsState state,
  237. ProgramPipelineState? pipelineState,
  238. TransformFeedbackDescriptor[] descriptors) : this(ref state, descriptors)
  239. {
  240. PipelineState = pipelineState;
  241. }
  242. /// <summary>
  243. /// Indicates that the shader accesses the early Z force state.
  244. /// </summary>
  245. public void RecordEarlyZForce()
  246. {
  247. _queriedState |= QueriedStateFlags.EarlyZForce;
  248. }
  249. /// <summary>
  250. /// Indicates that the shader accesses the primitive topology state.
  251. /// </summary>
  252. public void RecordPrimitiveTopology()
  253. {
  254. _queriedState |= QueriedStateFlags.PrimitiveTopology;
  255. }
  256. /// <summary>
  257. /// Indicates that the shader accesses the tessellation mode state.
  258. /// </summary>
  259. public void RecordTessellationMode()
  260. {
  261. _queriedState |= QueriedStateFlags.TessellationMode;
  262. }
  263. /// <summary>
  264. /// Indicates that the shader accesses the constant buffer use state.
  265. /// </summary>
  266. /// <param name="stageIndex">Shader stage index</param>
  267. /// <param name="useMask">Mask indicating the constant buffers bound at the time of the shader compilation</param>
  268. public void RecordConstantBufferUse(int stageIndex, uint useMask)
  269. {
  270. ConstantBufferUse[stageIndex] = useMask;
  271. _constantBufferUsePerStage |= (byte)(1 << stageIndex);
  272. }
  273. /// <summary>
  274. /// Indicates that a given texture is accessed by the shader.
  275. /// </summary>
  276. /// <param name="stageIndex">Shader stage where the texture is used</param>
  277. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  278. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  279. /// <param name="descriptor">Descriptor of the texture</param>
  280. public void RegisterTexture(int stageIndex, int handle, int cbufSlot, Image.TextureDescriptor descriptor)
  281. {
  282. Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
  283. state.Value.Format = descriptor.UnpackFormat();
  284. state.Value.FormatSrgb = descriptor.UnpackSrgb();
  285. state.Value.TextureTarget = descriptor.UnpackTextureTarget();
  286. state.Value.CoordNormalized = descriptor.UnpackTextureCoordNormalized();
  287. }
  288. /// <summary>
  289. /// Indicates that a given texture is accessed by the shader.
  290. /// </summary>
  291. /// <param name="stageIndex">Shader stage where the texture is used</param>
  292. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  293. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  294. /// <param name="format">Maxwell texture format value</param>
  295. /// <param name="formatSrgb">Whenever the texture format is a sRGB format</param>
  296. /// <param name="target">Texture target type</param>
  297. /// <param name="coordNormalized">Whenever the texture coordinates used on the shader are considered normalized</param>
  298. public void RegisterTexture(
  299. int stageIndex,
  300. int handle,
  301. int cbufSlot,
  302. uint format,
  303. bool formatSrgb,
  304. Image.TextureTarget target,
  305. bool coordNormalized)
  306. {
  307. Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
  308. state.Value.Format = format;
  309. state.Value.FormatSrgb = formatSrgb;
  310. state.Value.TextureTarget = target;
  311. state.Value.CoordNormalized = coordNormalized;
  312. }
  313. /// <summary>
  314. /// Indicates that the format of a given texture was used during the shader translation process.
  315. /// </summary>
  316. /// <param name="stageIndex">Shader stage where the texture is used</param>
  317. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  318. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  319. public void RecordTextureFormat(int stageIndex, int handle, int cbufSlot)
  320. {
  321. Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
  322. state.Value.QueriedFlags |= QueriedTextureStateFlags.TextureFormat;
  323. }
  324. /// <summary>
  325. /// Indicates that the target of a given texture was used during the shader translation process.
  326. /// </summary>
  327. /// <param name="stageIndex">Shader stage where the texture is used</param>
  328. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  329. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  330. public void RecordTextureSamplerType(int stageIndex, int handle, int cbufSlot)
  331. {
  332. Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
  333. state.Value.QueriedFlags |= QueriedTextureStateFlags.SamplerType;
  334. }
  335. /// <summary>
  336. /// Indicates that the coordinate normalization state of a given texture was used during the shader translation process.
  337. /// </summary>
  338. /// <param name="stageIndex">Shader stage where the texture is used</param>
  339. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  340. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  341. public void RecordTextureCoordNormalized(int stageIndex, int handle, int cbufSlot)
  342. {
  343. Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
  344. state.Value.QueriedFlags |= QueriedTextureStateFlags.CoordNormalized;
  345. }
  346. /// <summary>
  347. /// Checks if a given texture was registerd on this specialization state.
  348. /// </summary>
  349. /// <param name="stageIndex">Shader stage where the texture is used</param>
  350. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  351. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  352. public bool TextureRegistered(int stageIndex, int handle, int cbufSlot)
  353. {
  354. return GetTextureSpecState(stageIndex, handle, cbufSlot) != null;
  355. }
  356. /// <summary>
  357. /// Gets the recorded format of a given texture.
  358. /// </summary>
  359. /// <param name="stageIndex">Shader stage where the texture is used</param>
  360. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  361. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  362. public (uint, bool) GetFormat(int stageIndex, int handle, int cbufSlot)
  363. {
  364. TextureSpecializationState state = GetTextureSpecState(stageIndex, handle, cbufSlot).Value;
  365. return (state.Format, state.FormatSrgb);
  366. }
  367. /// <summary>
  368. /// Gets the recorded target of a given texture.
  369. /// </summary>
  370. /// <param name="stageIndex">Shader stage where the texture is used</param>
  371. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  372. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  373. public Image.TextureTarget GetTextureTarget(int stageIndex, int handle, int cbufSlot)
  374. {
  375. return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.TextureTarget;
  376. }
  377. /// <summary>
  378. /// Gets the recorded coordinate normalization state of a given texture.
  379. /// </summary>
  380. /// <param name="stageIndex">Shader stage where the texture is used</param>
  381. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  382. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  383. public bool GetCoordNormalized(int stageIndex, int handle, int cbufSlot)
  384. {
  385. return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized;
  386. }
  387. /// <summary>
  388. /// Gets texture specialization state for a given texture, or create a new one if not present.
  389. /// </summary>
  390. /// <param name="stageIndex">Shader stage where the texture is used</param>
  391. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  392. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  393. /// <returns>Texture specialization state</returns>
  394. private Box<TextureSpecializationState> GetOrCreateTextureSpecState(int stageIndex, int handle, int cbufSlot)
  395. {
  396. TextureKey key = new TextureKey(stageIndex, handle, cbufSlot);
  397. if (!_textureSpecialization.TryGetValue(key, out Box<TextureSpecializationState> state))
  398. {
  399. _textureSpecialization.Add(key, state = new Box<TextureSpecializationState>());
  400. }
  401. return state;
  402. }
  403. /// <summary>
  404. /// Gets texture specialization state for a given texture.
  405. /// </summary>
  406. /// <param name="stageIndex">Shader stage where the texture is used</param>
  407. /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
  408. /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
  409. /// <returns>Texture specialization state</returns>
  410. private Box<TextureSpecializationState> GetTextureSpecState(int stageIndex, int handle, int cbufSlot)
  411. {
  412. TextureKey key = new TextureKey(stageIndex, handle, cbufSlot);
  413. if (_textureSpecialization.TryGetValue(key, out Box<TextureSpecializationState> state))
  414. {
  415. return state;
  416. }
  417. return null;
  418. }
  419. /// <summary>
  420. /// Checks if the recorded state matches the current GPU 3D engine state.
  421. /// </summary>
  422. /// <param name="channel">GPU channel</param>
  423. /// <param name="poolState">Texture pool state</param>
  424. /// <param name="graphicsState">Graphics state</param>
  425. /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
  426. /// <returns>True if the state matches, false otherwise</returns>
  427. public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState, bool checkTextures)
  428. {
  429. if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
  430. {
  431. return false;
  432. }
  433. bool thisA2cDitherEnable = GraphicsState.AlphaToCoverageEnable && GraphicsState.AlphaToCoverageDitherEnable;
  434. bool otherA2cDitherEnable = graphicsState.AlphaToCoverageEnable && graphicsState.AlphaToCoverageDitherEnable;
  435. if (otherA2cDitherEnable != thisA2cDitherEnable)
  436. {
  437. return false;
  438. }
  439. if (graphicsState.DepthMode != GraphicsState.DepthMode)
  440. {
  441. return false;
  442. }
  443. if (graphicsState.AlphaTestEnable != GraphicsState.AlphaTestEnable)
  444. {
  445. return false;
  446. }
  447. if (graphicsState.AlphaTestEnable &&
  448. (graphicsState.AlphaTestCompare != GraphicsState.AlphaTestCompare ||
  449. graphicsState.AlphaTestReference != GraphicsState.AlphaTestReference))
  450. {
  451. return false;
  452. }
  453. if (!graphicsState.AttributeTypes.AsSpan().SequenceEqual(GraphicsState.AttributeTypes.AsSpan()))
  454. {
  455. return false;
  456. }
  457. return Matches(channel, poolState, checkTextures, isCompute: false);
  458. }
  459. /// <summary>
  460. /// Checks if the recorded state matches the current GPU compute engine state.
  461. /// </summary>
  462. /// <param name="channel">GPU channel</param>
  463. /// <param name="poolState">Texture pool state</param>
  464. /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
  465. /// <returns>True if the state matches, false otherwise</returns>
  466. public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState, bool checkTextures)
  467. {
  468. return Matches(channel, poolState, checkTextures, isCompute: true);
  469. }
  470. /// <summary>
  471. /// Fetch the constant buffers used for a texture to cache.
  472. /// </summary>
  473. /// <param name="channel">GPU channel</param>
  474. /// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param>
  475. /// <param name="cachedTextureBufferIndex">The currently cached texture buffer index</param>
  476. /// <param name="cachedSamplerBufferIndex">The currently cached sampler buffer index</param>
  477. /// <param name="cachedTextureBuffer">The currently cached texture buffer data</param>
  478. /// <param name="cachedSamplerBuffer">The currently cached sampler buffer data</param>
  479. /// <param name="cachedStageIndex">The currently cached stage</param>
  480. /// <param name="textureBufferIndex">The new texture buffer index</param>
  481. /// <param name="samplerBufferIndex">The new sampler buffer index</param>
  482. /// <param name="stageIndex">Stage index of the constant buffer</param>
  483. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  484. private static void UpdateCachedBuffer(
  485. GpuChannel channel,
  486. bool isCompute,
  487. scoped ref int cachedTextureBufferIndex,
  488. scoped ref int cachedSamplerBufferIndex,
  489. scoped ref ReadOnlySpan<int> cachedTextureBuffer,
  490. scoped ref ReadOnlySpan<int> cachedSamplerBuffer,
  491. scoped ref int cachedStageIndex,
  492. int textureBufferIndex,
  493. int samplerBufferIndex,
  494. int stageIndex)
  495. {
  496. bool stageChange = stageIndex != cachedStageIndex;
  497. if (stageChange || textureBufferIndex != cachedTextureBufferIndex)
  498. {
  499. ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
  500. cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
  501. cachedTextureBufferIndex = textureBufferIndex;
  502. if (samplerBufferIndex == textureBufferIndex)
  503. {
  504. cachedSamplerBuffer = cachedTextureBuffer;
  505. cachedSamplerBufferIndex = samplerBufferIndex;
  506. }
  507. }
  508. if (stageChange || samplerBufferIndex != cachedSamplerBufferIndex)
  509. {
  510. ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
  511. cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Address, (int)bounds.Size));
  512. cachedSamplerBufferIndex = samplerBufferIndex;
  513. }
  514. cachedStageIndex = stageIndex;
  515. }
  516. /// <summary>
  517. /// Checks if the recorded state matches the current GPU state.
  518. /// </summary>
  519. /// <param name="channel">GPU channel</param>
  520. /// <param name="poolState">Texture pool state</param>
  521. /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
  522. /// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param>
  523. /// <returns>True if the state matches, false otherwise</returns>
  524. private bool Matches(GpuChannel channel, GpuChannelPoolState poolState, bool checkTextures, bool isCompute)
  525. {
  526. int constantBufferUsePerStageMask = _constantBufferUsePerStage;
  527. while (constantBufferUsePerStageMask != 0)
  528. {
  529. int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
  530. uint useMask = isCompute
  531. ? channel.BufferManager.GetComputeUniformBufferUseMask()
  532. : channel.BufferManager.GetGraphicsUniformBufferUseMask(index);
  533. if (ConstantBufferUse[index] != useMask)
  534. {
  535. return false;
  536. }
  537. constantBufferUsePerStageMask &= ~(1 << index);
  538. }
  539. if (checkTextures)
  540. {
  541. TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId);
  542. int cachedTextureBufferIndex = -1;
  543. int cachedSamplerBufferIndex = -1;
  544. int cachedStageIndex = -1;
  545. ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty;
  546. ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty;
  547. foreach (var kv in _allTextures)
  548. {
  549. TextureKey textureKey = kv.Key;
  550. (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(textureKey.CbufSlot, poolState.TextureBufferIndex);
  551. UpdateCachedBuffer(channel,
  552. isCompute,
  553. ref cachedTextureBufferIndex,
  554. ref cachedSamplerBufferIndex,
  555. ref cachedTextureBuffer,
  556. ref cachedSamplerBuffer,
  557. ref cachedStageIndex,
  558. textureBufferIndex,
  559. samplerBufferIndex,
  560. textureKey.StageIndex);
  561. int packedId = TextureHandle.ReadPackedId(textureKey.Handle, cachedTextureBuffer, cachedSamplerBuffer);
  562. int textureId = TextureHandle.UnpackTextureId(packedId);
  563. if (pool.IsValidId(textureId))
  564. {
  565. ref readonly Image.TextureDescriptor descriptor = ref pool.GetDescriptorRef(textureId);
  566. if (!MatchesTexture(kv.Value, descriptor))
  567. {
  568. return false;
  569. }
  570. }
  571. }
  572. }
  573. return true;
  574. }
  575. /// <summary>
  576. /// Checks if the recorded texture state matches the given texture descriptor.
  577. /// </summary>
  578. /// <param name="specializationState">Texture specialization state</param>
  579. /// <param name="descriptor">Texture descriptor</param>
  580. /// <returns>True if the state matches, false otherwise</returns>
  581. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  582. private bool MatchesTexture(Box<TextureSpecializationState> specializationState, in Image.TextureDescriptor descriptor)
  583. {
  584. if (specializationState != null)
  585. {
  586. if (specializationState.Value.QueriedFlags.HasFlag(QueriedTextureStateFlags.CoordNormalized) &&
  587. specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized())
  588. {
  589. return false;
  590. }
  591. }
  592. return true;
  593. }
  594. /// <summary>
  595. /// Checks if the recorded texture state for a given texture binding matches a texture descriptor.
  596. /// </summary>
  597. /// <param name="stage">The shader stage</param>
  598. /// <param name="index">The texture index</param>
  599. /// <param name="descriptor">Texture descriptor</param>
  600. /// <returns>True if the state matches, false otherwise</returns>
  601. public bool MatchesTexture(ShaderStage stage, int index, in Image.TextureDescriptor descriptor)
  602. {
  603. Box<TextureSpecializationState> specializationState = _textureByBinding[(int)stage][index];
  604. return MatchesTexture(specializationState, descriptor);
  605. }
  606. /// <summary>
  607. /// Checks if the recorded texture state for a given image binding matches a texture descriptor.
  608. /// </summary>
  609. /// <param name="stage">The shader stage</param>
  610. /// <param name="index">The texture index</param>
  611. /// <param name="descriptor">Texture descriptor</param>
  612. /// <returns>True if the state matches, false otherwise</returns>
  613. public bool MatchesImage(ShaderStage stage, int index, in Image.TextureDescriptor descriptor)
  614. {
  615. Box<TextureSpecializationState> specializationState = _imageByBinding[(int)stage][index];
  616. return MatchesTexture(specializationState, descriptor);
  617. }
  618. /// <summary>
  619. /// Reads shader specialization state that has been serialized.
  620. /// </summary>
  621. /// <param name="dataReader">Data reader</param>
  622. /// <returns>Shader specialization state</returns>
  623. public static ShaderSpecializationState Read(ref BinarySerializer dataReader)
  624. {
  625. ShaderSpecializationState specState = new ShaderSpecializationState();
  626. dataReader.Read(ref specState._queriedState);
  627. dataReader.Read(ref specState._compute);
  628. if (specState._compute)
  629. {
  630. dataReader.ReadWithMagicAndSize(ref specState.ComputeState, ComsMagic);
  631. }
  632. else
  633. {
  634. dataReader.ReadWithMagicAndSize(ref specState.GraphicsState, GfxsMagic);
  635. }
  636. dataReader.Read(ref specState._constantBufferUsePerStage);
  637. int constantBufferUsePerStageMask = specState._constantBufferUsePerStage;
  638. while (constantBufferUsePerStageMask != 0)
  639. {
  640. int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
  641. dataReader.Read(ref specState.ConstantBufferUse[index]);
  642. constantBufferUsePerStageMask &= ~(1 << index);
  643. }
  644. bool hasPipelineState = false;
  645. dataReader.Read(ref hasPipelineState);
  646. if (hasPipelineState)
  647. {
  648. ProgramPipelineState pipelineState = default;
  649. dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic);
  650. specState.PipelineState = pipelineState;
  651. }
  652. if (specState._queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
  653. {
  654. ushort tfCount = 0;
  655. dataReader.Read(ref tfCount);
  656. specState.TransformFeedbackDescriptors = new TransformFeedbackDescriptor[tfCount];
  657. for (int index = 0; index < tfCount; index++)
  658. {
  659. dataReader.ReadWithMagicAndSize(ref specState.TransformFeedbackDescriptors[index], TfbdMagic);
  660. }
  661. }
  662. ushort count = 0;
  663. dataReader.Read(ref count);
  664. for (int index = 0; index < count; index++)
  665. {
  666. TextureKey textureKey = default;
  667. Box<TextureSpecializationState> textureState = new Box<TextureSpecializationState>();
  668. dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic);
  669. dataReader.ReadWithMagicAndSize(ref textureState.Value, TexsMagic);
  670. specState._textureSpecialization[textureKey] = textureState;
  671. }
  672. return specState;
  673. }
  674. /// <summary>
  675. /// Serializes the shader specialization state.
  676. /// </summary>
  677. /// <param name="dataWriter">Data writer</param>
  678. public void Write(ref BinarySerializer dataWriter)
  679. {
  680. dataWriter.Write(ref _queriedState);
  681. dataWriter.Write(ref _compute);
  682. if (_compute)
  683. {
  684. dataWriter.WriteWithMagicAndSize(ref ComputeState, ComsMagic);
  685. }
  686. else
  687. {
  688. dataWriter.WriteWithMagicAndSize(ref GraphicsState, GfxsMagic);
  689. }
  690. dataWriter.Write(ref _constantBufferUsePerStage);
  691. int constantBufferUsePerStageMask = _constantBufferUsePerStage;
  692. while (constantBufferUsePerStageMask != 0)
  693. {
  694. int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
  695. dataWriter.Write(ref ConstantBufferUse[index]);
  696. constantBufferUsePerStageMask &= ~(1 << index);
  697. }
  698. bool hasPipelineState = PipelineState.HasValue;
  699. dataWriter.Write(ref hasPipelineState);
  700. if (hasPipelineState)
  701. {
  702. ProgramPipelineState pipelineState = PipelineState.Value;
  703. dataWriter.WriteWithMagicAndSize(ref pipelineState, PgpsMagic);
  704. }
  705. if (_queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
  706. {
  707. ushort tfCount = (ushort)TransformFeedbackDescriptors.Length;
  708. dataWriter.Write(ref tfCount);
  709. for (int index = 0; index < TransformFeedbackDescriptors.Length; index++)
  710. {
  711. dataWriter.WriteWithMagicAndSize(ref TransformFeedbackDescriptors[index], TfbdMagic);
  712. }
  713. }
  714. ushort count = (ushort)_textureSpecialization.Count;
  715. dataWriter.Write(ref count);
  716. foreach (var kv in _textureSpecialization)
  717. {
  718. var textureKey = kv.Key;
  719. var textureState = kv.Value;
  720. dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic);
  721. dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic);
  722. }
  723. }
  724. }
  725. }