ResourceManager.cs
1 using Ryujinx.Common; 2 using Ryujinx.Graphics.Shader.IntermediateRepresentation; 3 using Ryujinx.Graphics.Shader.StructuredIr; 4 using System; 5 using System.Collections.Generic; 6 using System.Globalization; 7 8 namespace Ryujinx.Graphics.Shader.Translation 9 { 10 class ResourceManager 11 { 12 // Those values are used if the shader as local or shared memory access, 13 // but for some reason the supplied size was 0. 14 private const int DefaultLocalMemorySize = 128; 15 private const int DefaultSharedMemorySize = 4096; 16 17 private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; 18 19 private readonly IGpuAccessor _gpuAccessor; 20 private readonly ShaderStage _stage; 21 private readonly string _stagePrefix; 22 23 private readonly SetBindingPair[] _cbSlotToBindingMap; 24 private readonly SetBindingPair[] _sbSlotToBindingMap; 25 private uint _sbSlotWritten; 26 27 private readonly Dictionary<int, int> _sbSlots; 28 private readonly Dictionary<int, int> _sbSlotsReverse; 29 30 private readonly HashSet<int> _usedConstantBufferBindings; 31 32 private readonly record struct TextureInfo(int CbufSlot, int Handle, int ArrayLength, bool Separate, SamplerType Type, TextureFormat Format); 33 34 private struct TextureMeta 35 { 36 public int Set; 37 public int Binding; 38 public bool AccurateType; 39 public SamplerType Type; 40 public TextureUsageFlags UsageFlags; 41 } 42 43 private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures; 44 private readonly Dictionary<TextureInfo, TextureMeta> _usedImages; 45 46 public int LocalMemoryId { get; private set; } 47 public int SharedMemoryId { get; private set; } 48 49 public int LocalVertexDataMemoryId { get; private set; } 50 public int LocalTopologyRemapMemoryId { get; private set; } 51 public int LocalVertexIndexVertexRateMemoryId { get; private set; } 52 public int LocalVertexIndexInstanceRateMemoryId { get; private set; } 53 public int LocalGeometryOutputVertexCountMemoryId { get; private set; } 54 public int LocalGeometryOutputIndexCountMemoryId { get; private set; } 55 56 public ShaderProperties Properties { get; } 57 58 public ResourceReservations Reservations { get; } 59 60 public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ResourceReservations reservations = null) 61 { 62 _gpuAccessor = gpuAccessor; 63 Properties = new(); 64 Reservations = reservations; 65 _stage = stage; 66 _stagePrefix = GetShaderStagePrefix(stage); 67 68 _cbSlotToBindingMap = new SetBindingPair[18]; 69 _sbSlotToBindingMap = new SetBindingPair[16]; 70 _cbSlotToBindingMap.AsSpan().Fill(new(-1, -1)); 71 _sbSlotToBindingMap.AsSpan().Fill(new(-1, -1)); 72 73 _sbSlots = new(); 74 _sbSlotsReverse = new(); 75 76 _usedConstantBufferBindings = new(); 77 78 _usedTextures = new(); 79 _usedImages = new(); 80 81 Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, SupportBuffer.Binding, "support_buffer", SupportBuffer.GetStructureType())); 82 83 LocalMemoryId = -1; 84 SharedMemoryId = -1; 85 } 86 87 public void SetCurrentLocalMemory(int size, bool isUsed) 88 { 89 if (isUsed) 90 { 91 if (size <= 0) 92 { 93 size = DefaultLocalMemorySize; 94 } 95 96 var lmem = new MemoryDefinition("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); 97 98 LocalMemoryId = Properties.AddLocalMemory(lmem); 99 } 100 else 101 { 102 LocalMemoryId = -1; 103 } 104 } 105 106 public void SetCurrentSharedMemory(int size, bool isUsed) 107 { 108 if (isUsed) 109 { 110 if (size <= 0) 111 { 112 size = DefaultSharedMemorySize; 113 } 114 115 var smem = new MemoryDefinition("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); 116 117 SharedMemoryId = Properties.AddSharedMemory(smem); 118 } 119 else 120 { 121 SharedMemoryId = -1; 122 } 123 } 124 125 public void SetVertexAsComputeLocalMemories(ShaderStage stage, InputTopology inputTopology) 126 { 127 LocalVertexDataMemoryId = AddMemoryDefinition("local_vertex_data", AggregateType.Array | AggregateType.FP32, Reservations.OutputSizePerInvocation); 128 129 if (stage == ShaderStage.Vertex) 130 { 131 LocalVertexIndexVertexRateMemoryId = AddMemoryDefinition("local_vertex_index_vr", AggregateType.U32); 132 LocalVertexIndexInstanceRateMemoryId = AddMemoryDefinition("local_vertex_index_ir", AggregateType.U32); 133 } 134 else if (stage == ShaderStage.Geometry) 135 { 136 LocalTopologyRemapMemoryId = AddMemoryDefinition("local_topology_remap", AggregateType.Array | AggregateType.U32, inputTopology.ToInputVertices()); 137 138 LocalGeometryOutputVertexCountMemoryId = AddMemoryDefinition("local_geometry_output_vertex", AggregateType.U32); 139 LocalGeometryOutputIndexCountMemoryId = AddMemoryDefinition("local_geometry_output_index", AggregateType.U32); 140 } 141 } 142 143 private int AddMemoryDefinition(string name, AggregateType type, int arrayLength = 1) 144 { 145 return Properties.AddLocalMemory(new MemoryDefinition(name, type, arrayLength)); 146 } 147 148 public int GetConstantBufferBinding(int slot) 149 { 150 SetBindingPair setAndBinding = _cbSlotToBindingMap[slot]; 151 if (setAndBinding.Binding < 0) 152 { 153 setAndBinding = _gpuAccessor.CreateConstantBufferBinding(slot); 154 _cbSlotToBindingMap[slot] = setAndBinding; 155 string slotNumber = slot.ToString(CultureInfo.InvariantCulture); 156 AddNewConstantBuffer(setAndBinding.SetIndex, setAndBinding.Binding, $"{_stagePrefix}_c{slotNumber}"); 157 } 158 159 return setAndBinding.Binding; 160 } 161 162 public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding) 163 { 164 if (!TryGetSbSlot((byte)sbCbSlot, (ushort)sbCbOffset, out int slot)) 165 { 166 binding = 0; 167 return false; 168 } 169 170 SetBindingPair setAndBinding = _sbSlotToBindingMap[slot]; 171 172 if (setAndBinding.Binding < 0) 173 { 174 setAndBinding = _gpuAccessor.CreateStorageBufferBinding(slot); 175 _sbSlotToBindingMap[slot] = setAndBinding; 176 string slotNumber = slot.ToString(CultureInfo.InvariantCulture); 177 AddNewStorageBuffer(setAndBinding.SetIndex, setAndBinding.Binding, $"{_stagePrefix}_s{slotNumber}"); 178 } 179 180 if (write) 181 { 182 _sbSlotWritten |= 1u << slot; 183 } 184 185 binding = setAndBinding.Binding; 186 return true; 187 } 188 189 private bool TryGetSbSlot(byte sbCbSlot, ushort sbCbOffset, out int slot) 190 { 191 int key = PackSbCbInfo(sbCbSlot, sbCbOffset); 192 193 if (!_sbSlots.TryGetValue(key, out slot)) 194 { 195 slot = _sbSlots.Count; 196 197 if (slot >= _sbSlotToBindingMap.Length) 198 { 199 return false; 200 } 201 202 _sbSlots.Add(key, slot); 203 _sbSlotsReverse.Add(slot, key); 204 } 205 206 return true; 207 } 208 209 public bool TryGetConstantBufferSlot(int binding, out int slot) 210 { 211 for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++) 212 { 213 if (_cbSlotToBindingMap[slot].Binding == binding) 214 { 215 return true; 216 } 217 } 218 219 slot = 0; 220 return false; 221 } 222 223 public SetBindingPair GetTextureOrImageBinding( 224 Instruction inst, 225 SamplerType type, 226 TextureFormat format, 227 TextureFlags flags, 228 int cbufSlot, 229 int handle, 230 int arrayLength = 1, 231 bool separate = false) 232 { 233 inst &= Instruction.Mask; 234 bool isImage = inst.IsImage(); 235 bool isWrite = inst.IsImageStore(); 236 bool accurateType = !inst.IsTextureQuery(); 237 bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureQuerySize; 238 bool coherent = flags.HasFlag(TextureFlags.Coherent); 239 240 if (!isImage) 241 { 242 format = TextureFormat.Unknown; 243 } 244 245 SetBindingPair setAndBinding = GetTextureOrImageBinding( 246 cbufSlot, 247 handle, 248 arrayLength, 249 type, 250 format, 251 isImage, 252 intCoords, 253 isWrite, 254 accurateType, 255 coherent, 256 separate); 257 258 _gpuAccessor.RegisterTexture(handle, cbufSlot); 259 260 return setAndBinding; 261 } 262 263 private SetBindingPair GetTextureOrImageBinding( 264 int cbufSlot, 265 int handle, 266 int arrayLength, 267 SamplerType type, 268 TextureFormat format, 269 bool isImage, 270 bool intCoords, 271 bool write, 272 bool accurateType, 273 bool coherent, 274 bool separate) 275 { 276 var dimensions = type == SamplerType.None ? 0 : type.GetDimensions(); 277 var dict = isImage ? _usedImages : _usedTextures; 278 279 var usageFlags = TextureUsageFlags.None; 280 281 if (intCoords) 282 { 283 usageFlags |= TextureUsageFlags.NeedsScaleValue; 284 285 var canScale = _stage.SupportsRenderScale() && arrayLength == 1 && !write && dimensions == 2; 286 287 if (!canScale) 288 { 289 // Resolution scaling cannot be applied to this texture right now. 290 // Flag so that we know to blacklist scaling on related textures when binding them. 291 usageFlags |= TextureUsageFlags.ResScaleUnsupported; 292 } 293 } 294 295 if (write) 296 { 297 usageFlags |= TextureUsageFlags.ImageStore; 298 } 299 300 if (coherent) 301 { 302 usageFlags |= TextureUsageFlags.ImageCoherent; 303 } 304 305 // For array textures, we also want to use type as key, 306 // since we may have texture handles stores in the same buffer, but for textures with different types. 307 var keyType = arrayLength > 1 ? type : SamplerType.None; 308 var info = new TextureInfo(cbufSlot, handle, arrayLength, separate, keyType, format); 309 var meta = new TextureMeta() 310 { 311 AccurateType = accurateType, 312 Type = type, 313 UsageFlags = usageFlags, 314 }; 315 316 int setIndex; 317 int binding; 318 319 if (dict.TryGetValue(info, out var existingMeta)) 320 { 321 dict[info] = MergeTextureMeta(meta, existingMeta); 322 setIndex = existingMeta.Set; 323 binding = existingMeta.Binding; 324 } 325 else 326 { 327 if (arrayLength > 1 && (setIndex = _gpuAccessor.CreateExtraSet()) >= 0) 328 { 329 // We reserved an "extra set" for the array. 330 // In this case the binding is always the first one (0). 331 // Using separate sets for array is better as we need to do less descriptor set updates. 332 333 binding = 0; 334 } 335 else 336 { 337 bool isBuffer = (type & SamplerType.Mask) == SamplerType.TextureBuffer; 338 339 SetBindingPair setAndBinding = isImage 340 ? _gpuAccessor.CreateImageBinding(arrayLength, isBuffer) 341 : _gpuAccessor.CreateTextureBinding(arrayLength, isBuffer); 342 343 setIndex = setAndBinding.SetIndex; 344 binding = setAndBinding.Binding; 345 } 346 347 meta.Set = setIndex; 348 meta.Binding = binding; 349 350 dict.Add(info, meta); 351 } 352 353 string nameSuffix; 354 string prefix = isImage ? "i" : "t"; 355 356 if (arrayLength != 1 && type != SamplerType.None) 357 { 358 prefix += type.ToShortSamplerType(); 359 } 360 361 if (isImage) 362 { 363 nameSuffix = cbufSlot < 0 364 ? $"{prefix}_tcb_{handle:X}_{format.ToGlslFormat()}" 365 : $"{prefix}_cb{cbufSlot}_{handle:X}_{format.ToGlslFormat()}"; 366 } 367 else if (type == SamplerType.None) 368 { 369 nameSuffix = cbufSlot < 0 ? $"s_tcb_{handle:X}" : $"s_cb{cbufSlot}_{handle:X}"; 370 } 371 else 372 { 373 nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}"; 374 } 375 376 var definition = new TextureDefinition( 377 setIndex, 378 binding, 379 arrayLength, 380 separate, 381 $"{_stagePrefix}_{nameSuffix}", 382 meta.Type, 383 info.Format, 384 meta.UsageFlags); 385 386 if (isImage) 387 { 388 Properties.AddOrUpdateImage(definition); 389 } 390 else 391 { 392 Properties.AddOrUpdateTexture(definition); 393 } 394 395 return new SetBindingPair(setIndex, binding); 396 } 397 398 private static TextureMeta MergeTextureMeta(TextureMeta meta, TextureMeta existingMeta) 399 { 400 meta.Set = existingMeta.Set; 401 meta.Binding = existingMeta.Binding; 402 meta.UsageFlags |= existingMeta.UsageFlags; 403 404 // If the texture we have has inaccurate type information, then 405 // we prefer the most accurate one. 406 if (existingMeta.AccurateType) 407 { 408 meta.AccurateType = true; 409 meta.Type = existingMeta.Type; 410 } 411 412 return meta; 413 } 414 415 public void SetUsageFlagsForTextureQuery(int binding, SamplerType type) 416 { 417 TextureInfo selectedInfo = default; 418 TextureMeta selectedMeta = default; 419 bool found = false; 420 421 foreach ((TextureInfo info, TextureMeta meta) in _usedTextures) 422 { 423 if (meta.Binding == binding) 424 { 425 selectedInfo = info; 426 selectedMeta = meta; 427 found = true; 428 break; 429 } 430 } 431 432 if (found) 433 { 434 selectedMeta.UsageFlags |= TextureUsageFlags.NeedsScaleValue; 435 436 var dimensions = type.GetDimensions(); 437 var canScale = _stage.SupportsRenderScale() && selectedInfo.ArrayLength == 1 && dimensions == 2; 438 439 if (!canScale) 440 { 441 // Resolution scaling cannot be applied to this texture right now. 442 // Flag so that we know to blacklist scaling on related textures when binding them. 443 selectedMeta.UsageFlags |= TextureUsageFlags.ResScaleUnsupported; 444 } 445 446 _usedTextures[selectedInfo] = selectedMeta; 447 } 448 } 449 450 public void SetUsedConstantBufferBinding(int binding) 451 { 452 _usedConstantBufferBindings.Add(binding); 453 } 454 455 public BufferDescriptor[] GetConstantBufferDescriptors() 456 { 457 var descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count]; 458 459 int descriptorIndex = 0; 460 461 for (int slot = 0; slot < _cbSlotToBindingMap.Length; slot++) 462 { 463 SetBindingPair setAndBinding = _cbSlotToBindingMap[slot]; 464 465 if (setAndBinding.Binding >= 0 && _usedConstantBufferBindings.Contains(setAndBinding.Binding)) 466 { 467 descriptors[descriptorIndex++] = new BufferDescriptor(setAndBinding.SetIndex, setAndBinding.Binding, slot); 468 } 469 } 470 471 if (descriptors.Length != descriptorIndex) 472 { 473 Array.Resize(ref descriptors, descriptorIndex); 474 } 475 476 return descriptors; 477 } 478 479 public BufferDescriptor[] GetStorageBufferDescriptors() 480 { 481 var descriptors = new BufferDescriptor[_sbSlots.Count]; 482 483 int descriptorIndex = 0; 484 485 foreach ((int key, int slot) in _sbSlots) 486 { 487 SetBindingPair setAndBinding = _sbSlotToBindingMap[slot]; 488 489 if (setAndBinding.Binding >= 0) 490 { 491 (int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key); 492 BufferUsageFlags flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None; 493 descriptors[descriptorIndex++] = new BufferDescriptor(setAndBinding.SetIndex, setAndBinding.Binding, slot, sbCbSlot, sbCbOffset, flags); 494 } 495 } 496 497 if (descriptors.Length != descriptorIndex) 498 { 499 Array.Resize(ref descriptors, descriptorIndex); 500 } 501 502 return descriptors; 503 } 504 505 public TextureDescriptor[] GetTextureDescriptors(bool includeArrays = true) 506 { 507 return GetDescriptors(_usedTextures, includeArrays); 508 } 509 510 public TextureDescriptor[] GetImageDescriptors(bool includeArrays = true) 511 { 512 return GetDescriptors(_usedImages, includeArrays); 513 } 514 515 private static TextureDescriptor[] GetDescriptors(IReadOnlyDictionary<TextureInfo, TextureMeta> usedResources, bool includeArrays) 516 { 517 List<TextureDescriptor> descriptors = new(); 518 519 bool hasAnyArray = false; 520 521 foreach ((TextureInfo info, TextureMeta meta) in usedResources) 522 { 523 if (info.ArrayLength > 1) 524 { 525 hasAnyArray = true; 526 continue; 527 } 528 529 descriptors.Add(new TextureDescriptor( 530 meta.Set, 531 meta.Binding, 532 meta.Type, 533 info.Format, 534 info.CbufSlot, 535 info.Handle, 536 info.ArrayLength, 537 info.Separate, 538 meta.UsageFlags)); 539 } 540 541 if (hasAnyArray && includeArrays) 542 { 543 foreach ((TextureInfo info, TextureMeta meta) in usedResources) 544 { 545 if (info.ArrayLength <= 1) 546 { 547 continue; 548 } 549 550 descriptors.Add(new TextureDescriptor( 551 meta.Set, 552 meta.Binding, 553 meta.Type, 554 info.Format, 555 info.CbufSlot, 556 info.Handle, 557 info.ArrayLength, 558 info.Separate, 559 meta.UsageFlags)); 560 } 561 } 562 563 return descriptors.ToArray(); 564 } 565 566 public bool TryGetCbufSlotAndHandleForTexture(int binding, out int cbufSlot, out int handle) 567 { 568 foreach ((TextureInfo info, TextureMeta meta) in _usedTextures) 569 { 570 if (meta.Binding == binding) 571 { 572 cbufSlot = info.CbufSlot; 573 handle = info.Handle; 574 575 return true; 576 } 577 } 578 579 cbufSlot = 0; 580 handle = 0; 581 return false; 582 } 583 584 private static int FindDescriptorIndex(TextureDescriptor[] array, int binding) 585 { 586 return Array.FindIndex(array, x => x.Binding == binding); 587 } 588 589 public int FindTextureDescriptorIndex(int binding) 590 { 591 return FindDescriptorIndex(GetTextureDescriptors(), binding); 592 } 593 594 public int FindImageDescriptorIndex(int binding) 595 { 596 return FindDescriptorIndex(GetImageDescriptors(), binding); 597 } 598 599 public bool IsArrayOfTexturesOrImages(int binding, bool isImage) 600 { 601 foreach ((TextureInfo info, TextureMeta meta) in isImage ? _usedImages : _usedTextures) 602 { 603 if (meta.Binding == binding) 604 { 605 return info.ArrayLength != 1; 606 } 607 } 608 609 return false; 610 } 611 612 private void AddNewConstantBuffer(int setIndex, int binding, string name) 613 { 614 StructureType type = new(new[] 615 { 616 new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), 617 }); 618 619 Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, setIndex, binding, name, type)); 620 } 621 622 private void AddNewStorageBuffer(int setIndex, int binding, string name) 623 { 624 StructureType type = new(new[] 625 { 626 new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), 627 }); 628 629 Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type)); 630 } 631 632 public static string GetShaderStagePrefix(ShaderStage stage) 633 { 634 uint index = (uint)stage; 635 636 return index >= _stagePrefixes.Length ? "invalid" : _stagePrefixes[index]; 637 } 638 639 private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset) 640 { 641 return sbCbOffset | (sbCbSlot << 16); 642 } 643 644 private static (int, int) UnpackSbCbInfo(int key) 645 { 646 return ((byte)(key >> 16), (ushort)key); 647 } 648 } 649 }