ShaderSpecializationState.cs
1 using Ryujinx.Common.Memory; 2 using Ryujinx.Graphics.GAL; 3 using Ryujinx.Graphics.Gpu.Image; 4 using Ryujinx.Graphics.Gpu.Memory; 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 14 namespace Ryujinx.Graphics.Gpu.Shader 15 { 16 class ShaderSpecializationState 17 { 18 private const uint ComsMagic = (byte)'C' | ((byte)'O' << 8) | ((byte)'M' << 16) | ((byte)'S' << 24); 19 private const uint GfxsMagic = (byte)'G' | ((byte)'F' << 8) | ((byte)'X' << 16) | ((byte)'S' << 24); 20 private const uint TfbdMagic = (byte)'T' | ((byte)'F' << 8) | ((byte)'B' << 16) | ((byte)'D' << 24); 21 private const uint TexkMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'K' << 24); 22 private const uint TexsMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'S' << 24); 23 private const uint PgpsMagic = (byte)'P' | ((byte)'G' << 8) | ((byte)'P' << 16) | ((byte)'S' << 24); 24 25 /// <summary> 26 /// Flags indicating GPU state that is used by the shader. 27 /// </summary> 28 [Flags] 29 private enum QueriedStateFlags 30 { 31 PrimitiveTopology = 1 << 1, 32 TransformFeedback = 1 << 3, 33 TextureArrayFromBuffer = 1 << 4, 34 TextureArrayFromPool = 1 << 5, 35 } 36 37 private QueriedStateFlags _queriedState; 38 private bool _compute; 39 private byte _constantBufferUsePerStage; 40 41 /// <summary> 42 /// Compute engine state. 43 /// </summary> 44 public GpuChannelComputeState ComputeState; 45 46 /// <summary> 47 /// 3D engine state. 48 /// </summary> 49 public GpuChannelGraphicsState GraphicsState; 50 51 /// <summary> 52 /// Contant buffers bound at the time the shader was compiled, per stage. 53 /// </summary> 54 public Array5<uint> ConstantBufferUse; 55 56 /// <summary> 57 /// Pipeline state captured at the time of shader use. 58 /// </summary> 59 public ProgramPipelineState? PipelineState; 60 61 /// <summary> 62 /// Transform feedback buffers active at the time the shader was compiled. 63 /// </summary> 64 public TransformFeedbackDescriptor[] TransformFeedbackDescriptors; 65 66 /// <summary> 67 /// Flags indicating texture state that is used by the shader. 68 /// </summary> 69 [Flags] 70 private enum QueriedTextureStateFlags 71 { 72 TextureFormat = 1 << 0, 73 SamplerType = 1 << 1, 74 CoordNormalized = 1 << 2, 75 } 76 77 /// <summary> 78 /// Reference type wrapping a value. 79 /// </summary> 80 private class Box<T> 81 { 82 /// <summary> 83 /// Wrapped value. 84 /// </summary> 85 public T Value; 86 } 87 88 /// <summary> 89 /// State of a texture or image that is accessed by the shader. 90 /// </summary> 91 private struct TextureSpecializationState 92 { 93 // New fields should be added to the end of the struct to keep disk shader cache compatibility. 94 95 /// <summary> 96 /// Flags indicating which state of the texture the shader depends on. 97 /// </summary> 98 public QueriedTextureStateFlags QueriedFlags; 99 100 /// <summary> 101 /// Encoded texture format value. 102 /// </summary> 103 public uint Format; 104 105 /// <summary> 106 /// True if the texture format is sRGB, false otherwise. 107 /// </summary> 108 public bool FormatSrgb; 109 110 /// <summary> 111 /// Texture target. 112 /// </summary> 113 public TextureTarget TextureTarget; 114 115 /// <summary> 116 /// Indicates if the coordinates used to sample the texture are normalized or not (0.0..1.0 or 0..Width/Height). 117 /// </summary> 118 public bool CoordNormalized; 119 } 120 121 /// <summary> 122 /// Texture binding information, used to identify each texture accessed by the shader. 123 /// </summary> 124 private readonly record struct TextureKey 125 { 126 // New fields should be added to the end of the struct to keep disk shader cache compatibility. 127 128 /// <summary> 129 /// Shader stage where the texture is used. 130 /// </summary> 131 public readonly int StageIndex; 132 133 /// <summary> 134 /// Texture handle offset in words on the texture buffer. 135 /// </summary> 136 public readonly int Handle; 137 138 /// <summary> 139 /// Constant buffer slot of the texture buffer (-1 to use the texture buffer index GPU register). 140 /// </summary> 141 public readonly int CbufSlot; 142 143 /// <summary> 144 /// Creates a new texture key. 145 /// </summary> 146 /// <param name="stageIndex">Shader stage where the texture is used</param> 147 /// <param name="handle">Texture handle offset in words on the texture buffer</param> 148 /// <param name="cbufSlot">Constant buffer slot of the texture buffer (-1 to use the texture buffer index GPU register)</param> 149 public TextureKey(int stageIndex, int handle, int cbufSlot) 150 { 151 StageIndex = stageIndex; 152 Handle = handle; 153 CbufSlot = cbufSlot; 154 } 155 } 156 157 private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization; 158 private readonly Dictionary<TextureKey, int> _textureArrayFromBufferSpecialization; 159 private readonly Dictionary<bool, int> _textureArrayFromPoolSpecialization; 160 private KeyValuePair<TextureKey, Box<TextureSpecializationState>>[] _allTextures; 161 private Box<TextureSpecializationState>[][] _textureByBinding; 162 private Box<TextureSpecializationState>[][] _imageByBinding; 163 164 /// <summary> 165 /// Creates a new instance of the shader specialization state. 166 /// </summary> 167 private ShaderSpecializationState() 168 { 169 _textureSpecialization = new Dictionary<TextureKey, Box<TextureSpecializationState>>(); 170 _textureArrayFromBufferSpecialization = new Dictionary<TextureKey, int>(); 171 _textureArrayFromPoolSpecialization = new Dictionary<bool, int>(); 172 } 173 174 /// <summary> 175 /// Creates a new instance of the shader specialization state. 176 /// </summary> 177 /// <param name="state">Current compute engine state</param> 178 public ShaderSpecializationState(ref GpuChannelComputeState state) : this() 179 { 180 ComputeState = state; 181 _compute = true; 182 } 183 184 /// <summary> 185 /// Creates a new instance of the shader specialization state. 186 /// </summary> 187 /// <param name="state">Current 3D engine state</param> 188 /// <param name="descriptors">Optional transform feedback buffers in use, if any</param> 189 private ShaderSpecializationState(ref GpuChannelGraphicsState state, TransformFeedbackDescriptor[] descriptors) : this() 190 { 191 GraphicsState = state; 192 _compute = false; 193 194 if (descriptors != null) 195 { 196 TransformFeedbackDescriptors = descriptors; 197 _queriedState |= QueriedStateFlags.TransformFeedback; 198 } 199 } 200 201 /// <summary> 202 /// Prepare the shader specialization state for quick binding lookups. 203 /// </summary> 204 /// <param name="stages">The shader stages</param> 205 public void Prepare(CachedShaderStage[] stages) 206 { 207 _allTextures = _textureSpecialization.ToArray(); 208 209 _textureByBinding = new Box<TextureSpecializationState>[stages.Length][]; 210 _imageByBinding = new Box<TextureSpecializationState>[stages.Length][]; 211 212 for (int i = 0; i < stages.Length; i++) 213 { 214 CachedShaderStage stage = stages[i]; 215 if (stage?.Info != null) 216 { 217 var textures = stage.Info.Textures; 218 var images = stage.Info.Images; 219 220 var texBindings = new Box<TextureSpecializationState>[textures.Count]; 221 var imageBindings = new Box<TextureSpecializationState>[images.Count]; 222 223 int stageIndex = Math.Max(i - 1, 0); // Don't count VertexA for looking up spec state. No-Op for compute. 224 225 for (int j = 0; j < textures.Count; j++) 226 { 227 var texture = textures[j]; 228 texBindings[j] = GetTextureSpecState(stageIndex, texture.HandleIndex, texture.CbufSlot); 229 } 230 231 for (int j = 0; j < images.Count; j++) 232 { 233 var image = images[j]; 234 imageBindings[j] = GetTextureSpecState(stageIndex, image.HandleIndex, image.CbufSlot); 235 } 236 237 _textureByBinding[i] = texBindings; 238 _imageByBinding[i] = imageBindings; 239 } 240 } 241 } 242 243 /// <summary> 244 /// Creates a new instance of the shader specialization state. 245 /// </summary> 246 /// <param name="state">Current 3D engine state</param> 247 /// <param name="pipelineState">Current program pipeline state</param> 248 /// <param name="descriptors">Optional transform feedback buffers in use, if any</param> 249 public ShaderSpecializationState( 250 ref GpuChannelGraphicsState state, 251 ref ProgramPipelineState pipelineState, 252 TransformFeedbackDescriptor[] descriptors) : this(ref state, descriptors) 253 { 254 PipelineState = pipelineState; 255 } 256 257 /// <summary> 258 /// Creates a new instance of the shader specialization state. 259 /// </summary> 260 /// <param name="state">Current 3D engine state</param> 261 /// <param name="pipelineState">Current program pipeline state</param> 262 /// <param name="descriptors">Optional transform feedback buffers in use, if any</param> 263 public ShaderSpecializationState( 264 ref GpuChannelGraphicsState state, 265 ProgramPipelineState? pipelineState, 266 TransformFeedbackDescriptor[] descriptors) : this(ref state, descriptors) 267 { 268 PipelineState = pipelineState; 269 } 270 271 /// <summary> 272 /// Indicates that the shader accesses the primitive topology state. 273 /// </summary> 274 public void RecordPrimitiveTopology() 275 { 276 _queriedState |= QueriedStateFlags.PrimitiveTopology; 277 } 278 279 /// <summary> 280 /// Indicates that the shader accesses the constant buffer use state. 281 /// </summary> 282 /// <param name="stageIndex">Shader stage index</param> 283 /// <param name="useMask">Mask indicating the constant buffers bound at the time of the shader compilation</param> 284 public void RecordConstantBufferUse(int stageIndex, uint useMask) 285 { 286 ConstantBufferUse[stageIndex] = useMask; 287 _constantBufferUsePerStage |= (byte)(1 << stageIndex); 288 } 289 290 /// <summary> 291 /// Indicates that a given texture is accessed by the shader. 292 /// </summary> 293 /// <param name="stageIndex">Shader stage where the texture is used</param> 294 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 295 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 296 /// <param name="descriptor">Descriptor of the texture</param> 297 public void RegisterTexture(int stageIndex, int handle, int cbufSlot, Image.TextureDescriptor descriptor) 298 { 299 Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot); 300 state.Value.Format = descriptor.UnpackFormat(); 301 state.Value.FormatSrgb = descriptor.UnpackSrgb(); 302 state.Value.TextureTarget = descriptor.UnpackTextureTarget(); 303 state.Value.CoordNormalized = descriptor.UnpackTextureCoordNormalized(); 304 } 305 306 /// <summary> 307 /// Indicates that a given texture is accessed by the shader. 308 /// </summary> 309 /// <param name="stageIndex">Shader stage where the texture is used</param> 310 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 311 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 312 /// <param name="format">Maxwell texture format value</param> 313 /// <param name="formatSrgb">Whenever the texture format is a sRGB format</param> 314 /// <param name="target">Texture target type</param> 315 /// <param name="coordNormalized">Whenever the texture coordinates used on the shader are considered normalized</param> 316 public void RegisterTexture( 317 int stageIndex, 318 int handle, 319 int cbufSlot, 320 uint format, 321 bool formatSrgb, 322 TextureTarget target, 323 bool coordNormalized) 324 { 325 Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot); 326 state.Value.Format = format; 327 state.Value.FormatSrgb = formatSrgb; 328 state.Value.TextureTarget = target; 329 state.Value.CoordNormalized = coordNormalized; 330 } 331 332 /// <summary> 333 /// Registers the length of a texture array calculated from a constant buffer size. 334 /// </summary> 335 /// <param name="stageIndex">Shader stage where the texture is used</param> 336 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 337 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 338 /// <param name="length">Number of elements in the texture array</param> 339 public void RegisterTextureArrayLengthFromBuffer(int stageIndex, int handle, int cbufSlot, int length) 340 { 341 _textureArrayFromBufferSpecialization[new TextureKey(stageIndex, handle, cbufSlot)] = length; 342 _queriedState |= QueriedStateFlags.TextureArrayFromBuffer; 343 } 344 345 /// <summary> 346 /// Registers the length of a texture array calculated from a texture or sampler pool capacity. 347 /// </summary> 348 /// <param name="isSampler">True for sampler pool, false for texture pool</param> 349 /// <param name="length">Number of elements in the texture array</param> 350 public void RegisterTextureArrayLengthFromPool(bool isSampler, int length) 351 { 352 _textureArrayFromPoolSpecialization[isSampler] = length; 353 _queriedState |= QueriedStateFlags.TextureArrayFromPool; 354 } 355 356 /// <summary> 357 /// Indicates that the format of a given texture was used during the shader translation process. 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 void RecordTextureFormat(int stageIndex, int handle, int cbufSlot) 363 { 364 Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot); 365 state.Value.QueriedFlags |= QueriedTextureStateFlags.TextureFormat; 366 } 367 368 /// <summary> 369 /// Indicates that the target of a given texture was used during the shader translation process. 370 /// </summary> 371 /// <param name="stageIndex">Shader stage where the texture is used</param> 372 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 373 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 374 public void RecordTextureSamplerType(int stageIndex, int handle, int cbufSlot) 375 { 376 Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot); 377 state.Value.QueriedFlags |= QueriedTextureStateFlags.SamplerType; 378 } 379 380 /// <summary> 381 /// Indicates that the coordinate normalization state of a given texture was used during the shader translation process. 382 /// </summary> 383 /// <param name="stageIndex">Shader stage where the texture is used</param> 384 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 385 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 386 public void RecordTextureCoordNormalized(int stageIndex, int handle, int cbufSlot) 387 { 388 Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot); 389 state.Value.QueriedFlags |= QueriedTextureStateFlags.CoordNormalized; 390 } 391 392 /// <summary> 393 /// Checks if primitive topology was queried by the shader. 394 /// </summary> 395 /// <returns>True if queried, false otherwise</returns> 396 public bool IsPrimitiveTopologyQueried() 397 { 398 return _queriedState.HasFlag(QueriedStateFlags.PrimitiveTopology); 399 } 400 401 /// <summary> 402 /// Checks if a given texture was registered on this specialization state. 403 /// </summary> 404 /// <param name="stageIndex">Shader stage where the texture is used</param> 405 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 406 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 407 public bool TextureRegistered(int stageIndex, int handle, int cbufSlot) 408 { 409 return GetTextureSpecState(stageIndex, handle, cbufSlot) != null; 410 } 411 412 /// <summary> 413 /// Checks if a given texture array (from constant buffer) was registered on this specialization state. 414 /// </summary> 415 /// <param name="stageIndex">Shader stage where the texture is used</param> 416 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 417 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 418 /// <returns>True if the length for the given buffer and stage exists, false otherwise</returns> 419 public bool TextureArrayFromBufferRegistered(int stageIndex, int handle, int cbufSlot) 420 { 421 return _textureArrayFromBufferSpecialization.ContainsKey(new TextureKey(stageIndex, handle, cbufSlot)); 422 } 423 424 /// <summary> 425 /// Checks if a given texture array (from a sampler pool or texture pool) was registered on this specialization state. 426 /// </summary> 427 /// <param name="isSampler">True for sampler pool, false for texture pool</param> 428 /// <returns>True if the length for the given pool, false otherwise</returns> 429 public bool TextureArrayFromPoolRegistered(bool isSampler) 430 { 431 return _textureArrayFromPoolSpecialization.ContainsKey(isSampler); 432 } 433 434 /// <summary> 435 /// Gets the recorded format of a given texture. 436 /// </summary> 437 /// <param name="stageIndex">Shader stage where the texture is used</param> 438 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 439 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 440 /// <returns>Format and sRGB tuple</returns> 441 public (uint, bool) GetFormat(int stageIndex, int handle, int cbufSlot) 442 { 443 TextureSpecializationState state = GetTextureSpecState(stageIndex, handle, cbufSlot).Value; 444 return (state.Format, state.FormatSrgb); 445 } 446 447 /// <summary> 448 /// Gets the recorded target of a given texture. 449 /// </summary> 450 /// <param name="stageIndex">Shader stage where the texture is used</param> 451 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 452 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 453 /// <returns>Texture target</returns> 454 public TextureTarget GetTextureTarget(int stageIndex, int handle, int cbufSlot) 455 { 456 return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.TextureTarget; 457 } 458 459 /// <summary> 460 /// Gets the recorded coordinate normalization state of a given texture. 461 /// </summary> 462 /// <param name="stageIndex">Shader stage where the texture is used</param> 463 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 464 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 465 /// <returns>True if coordinates are normalized, false otherwise</returns> 466 public bool GetCoordNormalized(int stageIndex, int handle, int cbufSlot) 467 { 468 return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized; 469 } 470 471 /// <summary> 472 /// Gets the recorded length of a given texture array (from constant buffer). 473 /// </summary> 474 /// <param name="stageIndex">Shader stage where the texture is used</param> 475 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 476 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 477 /// <returns>Texture array length</returns> 478 public int GetTextureArrayFromBufferLength(int stageIndex, int handle, int cbufSlot) 479 { 480 return _textureArrayFromBufferSpecialization[new TextureKey(stageIndex, handle, cbufSlot)]; 481 } 482 483 /// <summary> 484 /// Gets the recorded length of a given texture array (from a sampler or texture pool). 485 /// </summary> 486 /// <param name="isSampler">True to get the sampler pool length, false to get the texture pool length</param> 487 /// <returns>Texture array length</returns> 488 public int GetTextureArrayFromPoolLength(bool isSampler) 489 { 490 return _textureArrayFromPoolSpecialization[isSampler]; 491 } 492 493 /// <summary> 494 /// Gets texture specialization state for a given texture, or create a new one if not present. 495 /// </summary> 496 /// <param name="stageIndex">Shader stage where the texture is used</param> 497 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 498 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 499 /// <returns>Texture specialization state</returns> 500 private Box<TextureSpecializationState> GetOrCreateTextureSpecState(int stageIndex, int handle, int cbufSlot) 501 { 502 TextureKey key = new(stageIndex, handle, cbufSlot); 503 504 if (!_textureSpecialization.TryGetValue(key, out Box<TextureSpecializationState> state)) 505 { 506 _textureSpecialization.Add(key, state = new Box<TextureSpecializationState>()); 507 } 508 509 return state; 510 } 511 512 /// <summary> 513 /// Gets texture specialization state for a given texture. 514 /// </summary> 515 /// <param name="stageIndex">Shader stage where the texture is used</param> 516 /// <param name="handle">Offset in words of the texture handle on the texture buffer</param> 517 /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param> 518 /// <returns>Texture specialization state</returns> 519 private Box<TextureSpecializationState> GetTextureSpecState(int stageIndex, int handle, int cbufSlot) 520 { 521 TextureKey key = new(stageIndex, handle, cbufSlot); 522 523 if (_textureSpecialization.TryGetValue(key, out Box<TextureSpecializationState> state)) 524 { 525 return state; 526 } 527 528 return null; 529 } 530 531 /// <summary> 532 /// Checks if the recorded state matches the current GPU 3D engine state. 533 /// </summary> 534 /// <param name="channel">GPU channel</param> 535 /// <param name="poolState">Texture pool state</param> 536 /// <param name="graphicsState">Graphics state</param> 537 /// <param name="vertexAsCompute">Indicates that the vertex shader has been converted into a compute shader</param> 538 /// <param name="usesDrawParameters">Indicates whether the vertex shader accesses draw parameters</param> 539 /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param> 540 /// <returns>True if the state matches, false otherwise</returns> 541 public bool MatchesGraphics( 542 GpuChannel channel, 543 ref GpuChannelPoolState poolState, 544 ref GpuChannelGraphicsState graphicsState, 545 bool vertexAsCompute, 546 bool usesDrawParameters, 547 bool checkTextures) 548 { 549 if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable) 550 { 551 return false; 552 } 553 554 bool thisA2cDitherEnable = GraphicsState.AlphaToCoverageEnable && GraphicsState.AlphaToCoverageDitherEnable; 555 bool otherA2cDitherEnable = graphicsState.AlphaToCoverageEnable && graphicsState.AlphaToCoverageDitherEnable; 556 557 if (otherA2cDitherEnable != thisA2cDitherEnable) 558 { 559 return false; 560 } 561 562 if (graphicsState.DepthMode != GraphicsState.DepthMode) 563 { 564 return false; 565 } 566 567 if (graphicsState.AlphaTestEnable != GraphicsState.AlphaTestEnable) 568 { 569 return false; 570 } 571 572 if (graphicsState.AlphaTestEnable && 573 (graphicsState.AlphaTestCompare != GraphicsState.AlphaTestCompare || 574 graphicsState.AlphaTestReference != GraphicsState.AlphaTestReference)) 575 { 576 return false; 577 } 578 579 if (ShaderCache.MayConvertVtgToCompute(ref channel.Capabilities) && !vertexAsCompute) 580 { 581 for (int index = 0; index < graphicsState.AttributeTypes.Length; index++) 582 { 583 AttributeType lType = FilterAttributeType(channel, graphicsState.AttributeTypes[index]); 584 AttributeType rType = FilterAttributeType(channel, GraphicsState.AttributeTypes[index]); 585 586 if (lType != rType) 587 { 588 return false; 589 } 590 } 591 } 592 else 593 { 594 if (!graphicsState.AttributeTypes.AsSpan().SequenceEqual(GraphicsState.AttributeTypes.AsSpan())) 595 { 596 return false; 597 } 598 } 599 600 if (usesDrawParameters && graphicsState.HasConstantBufferDrawParameters != GraphicsState.HasConstantBufferDrawParameters) 601 { 602 return false; 603 } 604 605 if (graphicsState.HasUnalignedStorageBuffer != GraphicsState.HasUnalignedStorageBuffer) 606 { 607 return false; 608 } 609 610 if (channel.Capabilities.NeedsFragmentOutputSpecialization && !graphicsState.FragmentOutputTypes.AsSpan().SequenceEqual(GraphicsState.FragmentOutputTypes.AsSpan())) 611 { 612 return false; 613 } 614 615 if (graphicsState.DualSourceBlendEnable != GraphicsState.DualSourceBlendEnable) 616 { 617 return false; 618 } 619 620 if (graphicsState.YNegateEnabled != GraphicsState.YNegateEnabled) 621 { 622 return false; 623 } 624 625 return Matches(channel, ref poolState, checkTextures, isCompute: false); 626 } 627 628 /// <summary> 629 /// Converts special vertex attribute groups to their generic equivalents, for comparison purposes. 630 /// </summary> 631 /// <param name="channel">GPU channel</param> 632 /// <param name="type">Vertex attribute type</param> 633 /// <returns>Filtered attribute</returns> 634 private static AttributeType FilterAttributeType(GpuChannel channel, AttributeType type) 635 { 636 type &= ~(AttributeType.Packed | AttributeType.PackedRgb10A2Signed); 637 638 if (channel.Capabilities.SupportsScaledVertexFormats && 639 (type == AttributeType.Sscaled || type == AttributeType.Uscaled)) 640 { 641 type = AttributeType.Float; 642 } 643 644 return type; 645 } 646 647 /// <summary> 648 /// Checks if the recorded state matches the current GPU compute engine state. 649 /// </summary> 650 /// <param name="channel">GPU channel</param> 651 /// <param name="poolState">Texture pool state</param> 652 /// <param name="computeState">Compute state</param> 653 /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param> 654 /// <returns>True if the state matches, false otherwise</returns> 655 public bool MatchesCompute(GpuChannel channel, ref GpuChannelPoolState poolState, GpuChannelComputeState computeState, bool checkTextures) 656 { 657 if (computeState.HasUnalignedStorageBuffer != ComputeState.HasUnalignedStorageBuffer) 658 { 659 return false; 660 } 661 662 return Matches(channel, ref poolState, checkTextures, isCompute: true); 663 } 664 665 /// <summary> 666 /// Fetch the constant buffers used for a texture to cache. 667 /// </summary> 668 /// <param name="channel">GPU channel</param> 669 /// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param> 670 /// <param name="cachedTextureBufferIndex">The currently cached texture buffer index</param> 671 /// <param name="cachedSamplerBufferIndex">The currently cached sampler buffer index</param> 672 /// <param name="cachedTextureBuffer">The currently cached texture buffer data</param> 673 /// <param name="cachedSamplerBuffer">The currently cached sampler buffer data</param> 674 /// <param name="cachedStageIndex">The currently cached stage</param> 675 /// <param name="textureBufferIndex">The new texture buffer index</param> 676 /// <param name="samplerBufferIndex">The new sampler buffer index</param> 677 /// <param name="stageIndex">Stage index of the constant buffer</param> 678 [MethodImpl(MethodImplOptions.AggressiveInlining)] 679 private static void UpdateCachedBuffer( 680 GpuChannel channel, 681 bool isCompute, 682 scoped ref int cachedTextureBufferIndex, 683 scoped ref int cachedSamplerBufferIndex, 684 scoped ref ReadOnlySpan<int> cachedTextureBuffer, 685 scoped ref ReadOnlySpan<int> cachedSamplerBuffer, 686 scoped ref int cachedStageIndex, 687 int textureBufferIndex, 688 int samplerBufferIndex, 689 int stageIndex) 690 { 691 bool stageChange = stageIndex != cachedStageIndex; 692 693 if (stageChange || textureBufferIndex != cachedTextureBufferIndex) 694 { 695 ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex); 696 697 cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range)); 698 cachedTextureBufferIndex = textureBufferIndex; 699 700 if (samplerBufferIndex == textureBufferIndex) 701 { 702 cachedSamplerBuffer = cachedTextureBuffer; 703 cachedSamplerBufferIndex = samplerBufferIndex; 704 } 705 } 706 707 if (stageChange || samplerBufferIndex != cachedSamplerBufferIndex) 708 { 709 ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex); 710 711 cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range)); 712 cachedSamplerBufferIndex = samplerBufferIndex; 713 } 714 715 cachedStageIndex = stageIndex; 716 } 717 718 /// <summary> 719 /// Checks if the recorded state matches the current GPU state. 720 /// </summary> 721 /// <param name="channel">GPU channel</param> 722 /// <param name="poolState">Texture pool state</param> 723 /// <param name="checkTextures">Indicates whether texture descriptors should be checked</param> 724 /// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param> 725 /// <returns>True if the state matches, false otherwise</returns> 726 private bool Matches(GpuChannel channel, ref GpuChannelPoolState poolState, bool checkTextures, bool isCompute) 727 { 728 int constantBufferUsePerStageMask = _constantBufferUsePerStage; 729 730 while (constantBufferUsePerStageMask != 0) 731 { 732 int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); 733 734 uint useMask = isCompute 735 ? channel.BufferManager.GetComputeUniformBufferUseMask() 736 : channel.BufferManager.GetGraphicsUniformBufferUseMask(index); 737 738 if (ConstantBufferUse[index] != useMask) 739 { 740 return false; 741 } 742 743 constantBufferUsePerStageMask &= ~(1 << index); 744 } 745 746 if (checkTextures && _allTextures.Length > 0) 747 { 748 TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId); 749 750 int cachedTextureBufferIndex = -1; 751 int cachedSamplerBufferIndex = -1; 752 int cachedStageIndex = -1; 753 ReadOnlySpan<int> cachedTextureBuffer = Span<int>.Empty; 754 ReadOnlySpan<int> cachedSamplerBuffer = Span<int>.Empty; 755 756 foreach (var kv in _allTextures) 757 { 758 TextureKey textureKey = kv.Key; 759 760 (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(textureKey.CbufSlot, poolState.TextureBufferIndex); 761 762 UpdateCachedBuffer(channel, 763 isCompute, 764 ref cachedTextureBufferIndex, 765 ref cachedSamplerBufferIndex, 766 ref cachedTextureBuffer, 767 ref cachedSamplerBuffer, 768 ref cachedStageIndex, 769 textureBufferIndex, 770 samplerBufferIndex, 771 textureKey.StageIndex); 772 773 int packedId = TextureHandle.ReadPackedId(textureKey.Handle, cachedTextureBuffer, cachedSamplerBuffer); 774 int textureId = TextureHandle.UnpackTextureId(packedId); 775 776 if (pool.IsValidId(textureId)) 777 { 778 ref readonly Image.TextureDescriptor descriptor = ref pool.GetDescriptorRef(textureId); 779 780 if (!MatchesTexture(kv.Value, descriptor)) 781 { 782 return false; 783 } 784 } 785 } 786 } 787 788 return true; 789 } 790 791 /// <summary> 792 /// Checks if the recorded texture state matches the given texture descriptor. 793 /// </summary> 794 /// <param name="specializationState">Texture specialization state</param> 795 /// <param name="descriptor">Texture descriptor</param> 796 /// <returns>True if the state matches, false otherwise</returns> 797 [MethodImpl(MethodImplOptions.AggressiveInlining)] 798 private static bool MatchesTexture(Box<TextureSpecializationState> specializationState, in Image.TextureDescriptor descriptor) 799 { 800 if (specializationState != null) 801 { 802 if (specializationState.Value.QueriedFlags.HasFlag(QueriedTextureStateFlags.CoordNormalized) && 803 specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized()) 804 { 805 return false; 806 } 807 } 808 809 return true; 810 } 811 812 /// <summary> 813 /// Checks if the recorded texture state for a given texture binding matches a texture descriptor. 814 /// </summary> 815 /// <param name="stage">The shader stage</param> 816 /// <param name="index">The texture index</param> 817 /// <param name="descriptor">Texture descriptor</param> 818 /// <returns>True if the state matches, false otherwise</returns> 819 public bool MatchesTexture(ShaderStage stage, int index, in Image.TextureDescriptor descriptor) 820 { 821 Box<TextureSpecializationState> specializationState = _textureByBinding[(int)stage][index]; 822 823 return MatchesTexture(specializationState, descriptor); 824 } 825 826 /// <summary> 827 /// Checks if the recorded texture state for a given image binding matches a texture descriptor. 828 /// </summary> 829 /// <param name="stage">The shader stage</param> 830 /// <param name="index">The texture index</param> 831 /// <param name="descriptor">Texture descriptor</param> 832 /// <returns>True if the state matches, false otherwise</returns> 833 public bool MatchesImage(ShaderStage stage, int index, in Image.TextureDescriptor descriptor) 834 { 835 Box<TextureSpecializationState> specializationState = _imageByBinding[(int)stage][index]; 836 837 return MatchesTexture(specializationState, descriptor); 838 } 839 840 /// <summary> 841 /// Populates pipeline state that doesn't exist in older caches with default values 842 /// based on specialization state. 843 /// </summary> 844 /// <param name="pipelineState">Pipeline state to prepare</param> 845 private void PreparePipelineState(ref ProgramPipelineState pipelineState) 846 { 847 if (!_compute) 848 { 849 pipelineState.DepthMode = GraphicsState.DepthMode ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne; 850 } 851 } 852 853 /// <summary> 854 /// Reads shader specialization state that has been serialized. 855 /// </summary> 856 /// <param name="dataReader">Data reader</param> 857 /// <returns>Shader specialization state</returns> 858 public static ShaderSpecializationState Read(ref BinarySerializer dataReader) 859 { 860 ShaderSpecializationState specState = new(); 861 862 dataReader.Read(ref specState._queriedState); 863 dataReader.Read(ref specState._compute); 864 865 if (specState._compute) 866 { 867 dataReader.ReadWithMagicAndSize(ref specState.ComputeState, ComsMagic); 868 } 869 else 870 { 871 dataReader.ReadWithMagicAndSize(ref specState.GraphicsState, GfxsMagic); 872 } 873 874 dataReader.Read(ref specState._constantBufferUsePerStage); 875 876 int constantBufferUsePerStageMask = specState._constantBufferUsePerStage; 877 878 while (constantBufferUsePerStageMask != 0) 879 { 880 int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); 881 dataReader.Read(ref specState.ConstantBufferUse[index]); 882 constantBufferUsePerStageMask &= ~(1 << index); 883 } 884 885 bool hasPipelineState = false; 886 887 dataReader.Read(ref hasPipelineState); 888 889 if (hasPipelineState) 890 { 891 ProgramPipelineState pipelineState = default; 892 dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic); 893 894 specState.PreparePipelineState(ref pipelineState); 895 specState.PipelineState = pipelineState; 896 } 897 898 if (specState._queriedState.HasFlag(QueriedStateFlags.TransformFeedback)) 899 { 900 ushort tfCount = 0; 901 dataReader.Read(ref tfCount); 902 specState.TransformFeedbackDescriptors = new TransformFeedbackDescriptor[tfCount]; 903 904 for (int index = 0; index < tfCount; index++) 905 { 906 dataReader.ReadWithMagicAndSize(ref specState.TransformFeedbackDescriptors[index], TfbdMagic); 907 } 908 } 909 910 ushort count = 0; 911 dataReader.Read(ref count); 912 913 for (int index = 0; index < count; index++) 914 { 915 TextureKey textureKey = default; 916 Box<TextureSpecializationState> textureState = new(); 917 918 dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic); 919 dataReader.ReadWithMagicAndSize(ref textureState.Value, TexsMagic); 920 921 specState._textureSpecialization[textureKey] = textureState; 922 } 923 924 if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer)) 925 { 926 dataReader.Read(ref count); 927 928 for (int index = 0; index < count; index++) 929 { 930 TextureKey textureKey = default; 931 int length = 0; 932 933 dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic); 934 dataReader.Read(ref length); 935 936 specState._textureArrayFromBufferSpecialization[textureKey] = length; 937 } 938 } 939 940 if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool)) 941 { 942 dataReader.Read(ref count); 943 944 for (int index = 0; index < count; index++) 945 { 946 bool textureKey = default; 947 int length = 0; 948 949 dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic); 950 dataReader.Read(ref length); 951 952 specState._textureArrayFromPoolSpecialization[textureKey] = length; 953 } 954 } 955 956 return specState; 957 } 958 959 /// <summary> 960 /// Serializes the shader specialization state. 961 /// </summary> 962 /// <param name="dataWriter">Data writer</param> 963 public void Write(ref BinarySerializer dataWriter) 964 { 965 dataWriter.Write(ref _queriedState); 966 dataWriter.Write(ref _compute); 967 968 if (_compute) 969 { 970 dataWriter.WriteWithMagicAndSize(ref ComputeState, ComsMagic); 971 } 972 else 973 { 974 dataWriter.WriteWithMagicAndSize(ref GraphicsState, GfxsMagic); 975 } 976 977 dataWriter.Write(ref _constantBufferUsePerStage); 978 979 int constantBufferUsePerStageMask = _constantBufferUsePerStage; 980 981 while (constantBufferUsePerStageMask != 0) 982 { 983 int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); 984 dataWriter.Write(ref ConstantBufferUse[index]); 985 constantBufferUsePerStageMask &= ~(1 << index); 986 } 987 988 bool hasPipelineState = PipelineState.HasValue; 989 990 dataWriter.Write(ref hasPipelineState); 991 992 if (hasPipelineState) 993 { 994 ProgramPipelineState pipelineState = PipelineState.Value; 995 dataWriter.WriteWithMagicAndSize(ref pipelineState, PgpsMagic); 996 } 997 998 if (_queriedState.HasFlag(QueriedStateFlags.TransformFeedback)) 999 { 1000 ushort tfCount = (ushort)TransformFeedbackDescriptors.Length; 1001 dataWriter.Write(ref tfCount); 1002 1003 for (int index = 0; index < TransformFeedbackDescriptors.Length; index++) 1004 { 1005 dataWriter.WriteWithMagicAndSize(ref TransformFeedbackDescriptors[index], TfbdMagic); 1006 } 1007 } 1008 1009 ushort count = (ushort)_textureSpecialization.Count; 1010 dataWriter.Write(ref count); 1011 1012 foreach (var kv in _textureSpecialization) 1013 { 1014 var textureKey = kv.Key; 1015 var textureState = kv.Value; 1016 1017 dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic); 1018 dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic); 1019 } 1020 1021 if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer)) 1022 { 1023 count = (ushort)_textureArrayFromBufferSpecialization.Count; 1024 dataWriter.Write(ref count); 1025 1026 foreach (var kv in _textureArrayFromBufferSpecialization) 1027 { 1028 var textureKey = kv.Key; 1029 var length = kv.Value; 1030 1031 dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic); 1032 dataWriter.Write(ref length); 1033 } 1034 } 1035 1036 if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool)) 1037 { 1038 count = (ushort)_textureArrayFromPoolSpecialization.Count; 1039 dataWriter.Write(ref count); 1040 1041 foreach (var kv in _textureArrayFromPoolSpecialization) 1042 { 1043 var textureKey = kv.Key; 1044 var length = kv.Value; 1045 1046 dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic); 1047 dataWriter.Write(ref length); 1048 } 1049 } 1050 } 1051 } 1052 }