ShaderSpecializationState.cs 36 KB

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