DescriptorSetUpdater.cs
1 using Ryujinx.Common.Memory; 2 using Ryujinx.Graphics.GAL; 3 using Ryujinx.Graphics.Shader; 4 using Silk.NET.Vulkan; 5 using System; 6 using System.Buffers; 7 using System.Collections.Generic; 8 using System.Runtime.CompilerServices; 9 using System.Runtime.InteropServices; 10 using CompareOp = Ryujinx.Graphics.GAL.CompareOp; 11 using Format = Ryujinx.Graphics.GAL.Format; 12 using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; 13 14 namespace Ryujinx.Graphics.Vulkan 15 { 16 class DescriptorSetUpdater 17 { 18 private const ulong StorageBufferMaxMirrorable = 0x2000; 19 20 private const int ArrayGrowthSize = 16; 21 22 private record struct BufferRef 23 { 24 public Auto<DisposableBuffer> Buffer; 25 public int Offset; 26 public bool Write; 27 28 public BufferRef(Auto<DisposableBuffer> buffer) 29 { 30 Buffer = buffer; 31 Offset = 0; 32 Write = true; 33 } 34 35 public BufferRef(Auto<DisposableBuffer> buffer, ref BufferRange range) 36 { 37 Buffer = buffer; 38 Offset = range.Offset; 39 Write = range.Write; 40 } 41 } 42 43 private record struct TextureRef 44 { 45 public ShaderStage Stage; 46 public TextureView View; 47 public Auto<DisposableImageView> ImageView; 48 public Auto<DisposableSampler> Sampler; 49 50 public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler) 51 { 52 Stage = stage; 53 View = view; 54 ImageView = imageView; 55 Sampler = sampler; 56 } 57 } 58 59 private record struct ImageRef 60 { 61 public ShaderStage Stage; 62 public TextureView View; 63 public Auto<DisposableImageView> ImageView; 64 65 public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView) 66 { 67 Stage = stage; 68 View = view; 69 ImageView = imageView; 70 } 71 } 72 73 private readonly record struct ArrayRef<T>(ShaderStage Stage, T Array); 74 75 private readonly VulkanRenderer _gd; 76 private readonly Device _device; 77 private ShaderCollection _program; 78 79 private readonly BufferRef[] _uniformBufferRefs; 80 private readonly BufferRef[] _storageBufferRefs; 81 private readonly TextureRef[] _textureRefs; 82 private readonly ImageRef[] _imageRefs; 83 private readonly TextureBuffer[] _bufferTextureRefs; 84 private readonly TextureBuffer[] _bufferImageRefs; 85 86 private ArrayRef<TextureArray>[] _textureArrayRefs; 87 private ArrayRef<ImageArray>[] _imageArrayRefs; 88 89 private ArrayRef<TextureArray>[] _textureArrayExtraRefs; 90 private ArrayRef<ImageArray>[] _imageArrayExtraRefs; 91 92 private readonly DescriptorBufferInfo[] _uniformBuffers; 93 private readonly DescriptorBufferInfo[] _storageBuffers; 94 private readonly DescriptorImageInfo[] _textures; 95 private readonly DescriptorImageInfo[] _images; 96 private readonly BufferView[] _bufferTextures; 97 private readonly BufferView[] _bufferImages; 98 99 private readonly DescriptorSetTemplateUpdater _templateUpdater; 100 101 private BitMapStruct<Array2<long>> _uniformSet; 102 private BitMapStruct<Array2<long>> _storageSet; 103 private BitMapStruct<Array2<long>> _uniformMirrored; 104 private BitMapStruct<Array2<long>> _storageMirrored; 105 private readonly int[] _uniformSetPd; 106 private int _pdSequence = 1; 107 108 private bool _updateDescriptorCacheCbIndex; 109 110 [Flags] 111 private enum DirtyFlags 112 { 113 None = 0, 114 Uniform = 1 << 0, 115 Storage = 1 << 1, 116 Texture = 1 << 2, 117 Image = 1 << 3, 118 All = Uniform | Storage | Texture | Image, 119 } 120 121 private DirtyFlags _dirty; 122 123 private readonly BufferHolder _dummyBuffer; 124 private readonly TextureView _dummyTexture; 125 private readonly SamplerHolder _dummySampler; 126 127 public List<TextureView> FeedbackLoopHazards { get; private set; } 128 129 public DescriptorSetUpdater(VulkanRenderer gd, Device device) 130 { 131 _gd = gd; 132 _device = device; 133 134 // Some of the bindings counts needs to be multiplied by 2 because we have buffer and 135 // regular textures/images interleaved on the same descriptor set. 136 137 _uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; 138 _storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; 139 _textureRefs = new TextureRef[Constants.MaxTextureBindings * 2]; 140 _imageRefs = new ImageRef[Constants.MaxImageBindings * 2]; 141 _bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2]; 142 _bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2]; 143 144 _textureArrayRefs = Array.Empty<ArrayRef<TextureArray>>(); 145 _imageArrayRefs = Array.Empty<ArrayRef<ImageArray>>(); 146 147 _textureArrayExtraRefs = Array.Empty<ArrayRef<TextureArray>>(); 148 _imageArrayExtraRefs = Array.Empty<ArrayRef<ImageArray>>(); 149 150 _uniformBuffers = new DescriptorBufferInfo[Constants.MaxUniformBufferBindings]; 151 _storageBuffers = new DescriptorBufferInfo[Constants.MaxStorageBufferBindings]; 152 _textures = new DescriptorImageInfo[Constants.MaxTexturesPerStage]; 153 _images = new DescriptorImageInfo[Constants.MaxImagesPerStage]; 154 _bufferTextures = new BufferView[Constants.MaxTexturesPerStage]; 155 _bufferImages = new BufferView[Constants.MaxImagesPerStage]; 156 157 _uniformSetPd = new int[Constants.MaxUniformBufferBindings]; 158 159 var initialImageInfo = new DescriptorImageInfo 160 { 161 ImageLayout = ImageLayout.General, 162 }; 163 164 _textures.AsSpan().Fill(initialImageInfo); 165 _images.AsSpan().Fill(initialImageInfo); 166 167 if (gd.Capabilities.SupportsNullDescriptors) 168 { 169 // If null descriptors are supported, we can pass null as the handle. 170 _dummyBuffer = null; 171 } 172 else 173 { 174 // If null descriptors are not supported, we need to pass the handle of a dummy buffer on unused bindings. 175 _dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, baseType: BufferAllocationType.DeviceLocal); 176 } 177 178 _dummyTexture = gd.CreateTextureView(new TextureCreateInfo( 179 1, 180 1, 181 1, 182 1, 183 1, 184 1, 185 1, 186 4, 187 Format.R8G8B8A8Unorm, 188 DepthStencilMode.Depth, 189 Target.Texture2D, 190 SwizzleComponent.Red, 191 SwizzleComponent.Green, 192 SwizzleComponent.Blue, 193 SwizzleComponent.Alpha)); 194 195 _dummySampler = (SamplerHolder)gd.CreateSampler(new SamplerCreateInfo( 196 MinFilter.Nearest, 197 MagFilter.Nearest, 198 false, 199 AddressMode.Repeat, 200 AddressMode.Repeat, 201 AddressMode.Repeat, 202 CompareMode.None, 203 CompareOp.Always, 204 new ColorF(0, 0, 0, 0), 205 0, 206 0, 207 0, 208 1f)); 209 210 _templateUpdater = new(); 211 } 212 213 public void Initialize(bool isMainPipeline) 214 { 215 MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4); 216 _dummyTexture.SetData(dummyTextureData); 217 218 if (isMainPipeline) 219 { 220 FeedbackLoopHazards = new(); 221 } 222 } 223 224 private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size) 225 { 226 return offset < bindingOffset + (int)info.Range && (offset + size) > bindingOffset; 227 } 228 229 internal void Rebind(Auto<DisposableBuffer> buffer, int offset, int size) 230 { 231 if (_program == null) 232 { 233 return; 234 } 235 236 // Check stage bindings 237 238 _uniformMirrored.Union(_uniformSet).SignalSet((int binding, int count) => 239 { 240 for (int i = 0; i < count; i++) 241 { 242 ref BufferRef bufferRef = ref _uniformBufferRefs[binding]; 243 if (bufferRef.Buffer == buffer) 244 { 245 ref DescriptorBufferInfo info = ref _uniformBuffers[binding]; 246 int bindingOffset = bufferRef.Offset; 247 248 if (BindingOverlaps(ref info, bindingOffset, offset, size)) 249 { 250 _uniformSet.Clear(binding); 251 _uniformSetPd[binding] = 0; 252 SignalDirty(DirtyFlags.Uniform); 253 } 254 } 255 256 binding++; 257 } 258 }); 259 260 _storageMirrored.Union(_storageSet).SignalSet((int binding, int count) => 261 { 262 for (int i = 0; i < count; i++) 263 { 264 ref BufferRef bufferRef = ref _storageBufferRefs[binding]; 265 if (bufferRef.Buffer == buffer) 266 { 267 ref DescriptorBufferInfo info = ref _storageBuffers[binding]; 268 int bindingOffset = bufferRef.Offset; 269 270 if (BindingOverlaps(ref info, bindingOffset, offset, size)) 271 { 272 _storageSet.Clear(binding); 273 SignalDirty(DirtyFlags.Storage); 274 } 275 } 276 277 binding++; 278 } 279 }); 280 } 281 282 public void InsertBindingBarriers(CommandBufferScoped cbs) 283 { 284 if ((FeedbackLoopHazards?.Count ?? 0) > 0) 285 { 286 // Clear existing hazards - they will be rebuilt. 287 288 foreach (TextureView hazard in FeedbackLoopHazards) 289 { 290 hazard.DecrementHazardUses(); 291 } 292 293 FeedbackLoopHazards.Clear(); 294 } 295 296 foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex]) 297 { 298 if (segment.Type == ResourceType.TextureAndSampler) 299 { 300 if (!segment.IsArray) 301 { 302 for (int i = 0; i < segment.Count; i++) 303 { 304 ref var texture = ref _textureRefs[segment.Binding + i]; 305 texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); 306 } 307 } 308 else 309 { 310 ref var arrayRef = ref _textureArrayRefs[segment.Binding]; 311 PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags(); 312 arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags); 313 } 314 } 315 } 316 317 foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.ImageSetIndex]) 318 { 319 if (segment.Type == ResourceType.Image) 320 { 321 if (!segment.IsArray) 322 { 323 for (int i = 0; i < segment.Count; i++) 324 { 325 ref var image = ref _imageRefs[segment.Binding + i]; 326 image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); 327 } 328 } 329 else 330 { 331 ref var arrayRef = ref _imageArrayRefs[segment.Binding]; 332 PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags(); 333 arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags); 334 } 335 } 336 } 337 338 for (int setIndex = PipelineBase.DescriptorSetLayouts; setIndex < _program.BindingSegments.Length; setIndex++) 339 { 340 var bindingSegments = _program.BindingSegments[setIndex]; 341 342 if (bindingSegments.Length == 0) 343 { 344 continue; 345 } 346 347 ResourceBindingSegment segment = bindingSegments[0]; 348 349 if (segment.IsArray) 350 { 351 if (segment.Type == ResourceType.Texture || 352 segment.Type == ResourceType.Sampler || 353 segment.Type == ResourceType.TextureAndSampler || 354 segment.Type == ResourceType.BufferTexture) 355 { 356 ref var arrayRef = ref _textureArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts]; 357 PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags(); 358 arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags); 359 } 360 else if (segment.Type == ResourceType.Image || segment.Type == ResourceType.BufferImage) 361 { 362 ref var arrayRef = ref _imageArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts]; 363 PipelineStageFlags stageFlags = arrayRef.Stage.ConvertToPipelineStageFlags(); 364 arrayRef.Array?.QueueWriteToReadBarriers(cbs, stageFlags); 365 } 366 } 367 } 368 } 369 370 public void AdvancePdSequence() 371 { 372 if (++_pdSequence == 0) 373 { 374 _pdSequence = 1; 375 } 376 } 377 378 public void SetProgram(CommandBufferScoped cbs, ShaderCollection program, bool isBound) 379 { 380 if (!program.HasSameLayout(_program)) 381 { 382 // When the pipeline layout changes, push descriptor bindings are invalidated. 383 384 AdvancePdSequence(); 385 } 386 387 _program = program; 388 _updateDescriptorCacheCbIndex = true; 389 _dirty = DirtyFlags.All; 390 } 391 392 public void SetImage(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture image) 393 { 394 if (image is TextureBuffer imageBuffer) 395 { 396 _bufferImageRefs[binding] = imageBuffer; 397 } 398 else if (image is TextureView view) 399 { 400 ref ImageRef iRef = ref _imageRefs[binding]; 401 402 iRef.View?.ClearUsage(FeedbackLoopHazards); 403 view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); 404 405 iRef = new(stage, view, view.GetIdentityImageView()); 406 } 407 else 408 { 409 _imageRefs[binding] = default; 410 _bufferImageRefs[binding] = null; 411 } 412 413 SignalDirty(DirtyFlags.Image); 414 } 415 416 public void SetImage(int binding, Auto<DisposableImageView> image) 417 { 418 _imageRefs[binding] = new(ShaderStage.Compute, null, image); 419 420 SignalDirty(DirtyFlags.Image); 421 } 422 423 public void SetStorageBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers) 424 { 425 for (int i = 0; i < buffers.Length; i++) 426 { 427 var assignment = buffers[i]; 428 var buffer = assignment.Range; 429 int index = assignment.Binding; 430 431 Auto<DisposableBuffer> vkBuffer = buffer.Handle == BufferHandle.Null 432 ? null 433 : _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, buffer.Write, isSSBO: true); 434 435 ref BufferRef currentBufferRef = ref _storageBufferRefs[index]; 436 437 DescriptorBufferInfo info = new() 438 { 439 Offset = (ulong)buffer.Offset, 440 Range = (ulong)buffer.Size, 441 }; 442 443 var newRef = new BufferRef(vkBuffer, ref buffer); 444 445 ref DescriptorBufferInfo currentInfo = ref _storageBuffers[index]; 446 447 if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range) 448 { 449 _storageSet.Clear(index); 450 451 currentInfo = info; 452 currentBufferRef = newRef; 453 } 454 } 455 456 SignalDirty(DirtyFlags.Storage); 457 } 458 459 public void SetStorageBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers) 460 { 461 for (int i = 0; i < buffers.Length; i++) 462 { 463 var vkBuffer = buffers[i]; 464 int index = first + i; 465 466 ref BufferRef currentBufferRef = ref _storageBufferRefs[index]; 467 468 DescriptorBufferInfo info = new() 469 { 470 Offset = 0, 471 Range = Vk.WholeSize, 472 }; 473 474 BufferRef newRef = new(vkBuffer); 475 476 ref DescriptorBufferInfo currentInfo = ref _storageBuffers[index]; 477 478 if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range) 479 { 480 _storageSet.Clear(index); 481 482 currentInfo = info; 483 currentBufferRef = newRef; 484 } 485 } 486 487 SignalDirty(DirtyFlags.Storage); 488 } 489 490 public void SetTextureAndSampler( 491 CommandBufferScoped cbs, 492 ShaderStage stage, 493 int binding, 494 ITexture texture, 495 ISampler sampler) 496 { 497 if (texture is TextureBuffer textureBuffer) 498 { 499 _bufferTextureRefs[binding] = textureBuffer; 500 } 501 else if (texture is TextureView view) 502 { 503 ref TextureRef iRef = ref _textureRefs[binding]; 504 505 iRef.View?.ClearUsage(FeedbackLoopHazards); 506 view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); 507 508 iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler()); 509 } 510 else 511 { 512 _textureRefs[binding] = default; 513 _bufferTextureRefs[binding] = null; 514 } 515 516 SignalDirty(DirtyFlags.Texture); 517 } 518 519 public void SetTextureAndSamplerIdentitySwizzle( 520 CommandBufferScoped cbs, 521 ShaderStage stage, 522 int binding, 523 ITexture texture, 524 ISampler sampler) 525 { 526 if (texture is TextureView view) 527 { 528 view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); 529 530 _textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler()); 531 532 SignalDirty(DirtyFlags.Texture); 533 } 534 else 535 { 536 SetTextureAndSampler(cbs, stage, binding, texture, sampler); 537 } 538 } 539 540 public void SetTextureArray(CommandBufferScoped cbs, ShaderStage stage, int binding, ITextureArray array) 541 { 542 ref ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _textureArrayRefs, binding, ArrayGrowthSize); 543 544 if (arrayRef.Stage != stage || arrayRef.Array != array) 545 { 546 arrayRef.Array?.DecrementBindCount(); 547 548 if (array is TextureArray textureArray) 549 { 550 textureArray.IncrementBindCount(); 551 textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); 552 } 553 554 arrayRef = new ArrayRef<TextureArray>(stage, array as TextureArray); 555 556 SignalDirty(DirtyFlags.Texture); 557 } 558 } 559 560 public void SetTextureArraySeparate(CommandBufferScoped cbs, ShaderStage stage, int setIndex, ITextureArray array) 561 { 562 ref ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _textureArrayExtraRefs, setIndex - PipelineBase.DescriptorSetLayouts); 563 564 if (arrayRef.Stage != stage || arrayRef.Array != array) 565 { 566 arrayRef.Array?.DecrementBindCount(); 567 568 if (array is TextureArray textureArray) 569 { 570 textureArray.IncrementBindCount(); 571 textureArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); 572 } 573 574 arrayRef = new ArrayRef<TextureArray>(stage, array as TextureArray); 575 576 SignalDirty(DirtyFlags.Texture); 577 } 578 } 579 580 public void SetImageArray(CommandBufferScoped cbs, ShaderStage stage, int binding, IImageArray array) 581 { 582 ref ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _imageArrayRefs, binding, ArrayGrowthSize); 583 584 if (arrayRef.Stage != stage || arrayRef.Array != array) 585 { 586 arrayRef.Array?.DecrementBindCount(); 587 588 if (array is ImageArray imageArray) 589 { 590 imageArray.IncrementBindCount(); 591 imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); 592 } 593 594 arrayRef = new ArrayRef<ImageArray>(stage, array as ImageArray); 595 596 SignalDirty(DirtyFlags.Image); 597 } 598 } 599 600 public void SetImageArraySeparate(CommandBufferScoped cbs, ShaderStage stage, int setIndex, IImageArray array) 601 { 602 ref ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _imageArrayExtraRefs, setIndex - PipelineBase.DescriptorSetLayouts); 603 604 if (arrayRef.Stage != stage || arrayRef.Array != array) 605 { 606 arrayRef.Array?.DecrementBindCount(); 607 608 if (array is ImageArray imageArray) 609 { 610 imageArray.IncrementBindCount(); 611 imageArray.QueueWriteToReadBarriers(cbs, stage.ConvertToPipelineStageFlags()); 612 } 613 614 arrayRef = new ArrayRef<ImageArray>(stage, array as ImageArray); 615 616 SignalDirty(DirtyFlags.Image); 617 } 618 } 619 620 private static ref ArrayRef<T> GetArrayRef<T>(ref ArrayRef<T>[] array, int index, int growthSize = 1) 621 { 622 ArgumentOutOfRangeException.ThrowIfNegative(index); 623 624 if (array.Length <= index) 625 { 626 Array.Resize(ref array, index + growthSize); 627 } 628 629 return ref array[index]; 630 } 631 632 public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers) 633 { 634 for (int i = 0; i < buffers.Length; i++) 635 { 636 var assignment = buffers[i]; 637 var buffer = assignment.Range; 638 int index = assignment.Binding; 639 640 Auto<DisposableBuffer> vkBuffer = buffer.Handle == BufferHandle.Null 641 ? null 642 : _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); 643 644 ref BufferRef currentBufferRef = ref _uniformBufferRefs[index]; 645 646 DescriptorBufferInfo info = new() 647 { 648 Offset = (ulong)buffer.Offset, 649 Range = (ulong)buffer.Size, 650 }; 651 652 BufferRef newRef = new(vkBuffer, ref buffer); 653 654 ref DescriptorBufferInfo currentInfo = ref _uniformBuffers[index]; 655 656 if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range) 657 { 658 _uniformSet.Clear(index); 659 _uniformSetPd[index] = 0; 660 661 currentInfo = info; 662 currentBufferRef = newRef; 663 } 664 } 665 666 SignalDirty(DirtyFlags.Uniform); 667 } 668 669 private void SignalDirty(DirtyFlags flag) 670 { 671 _dirty |= flag; 672 } 673 674 public void UpdateAndBindDescriptorSets(CommandBufferScoped cbs, PipelineBindPoint pbp) 675 { 676 if ((_dirty & DirtyFlags.All) == 0) 677 { 678 return; 679 } 680 681 var program = _program; 682 683 if (_dirty.HasFlag(DirtyFlags.Uniform)) 684 { 685 if (program.UsePushDescriptors) 686 { 687 UpdateAndBindUniformBufferPd(cbs); 688 } 689 else 690 { 691 UpdateAndBind(cbs, program, PipelineBase.UniformSetIndex, pbp); 692 } 693 } 694 695 if (_dirty.HasFlag(DirtyFlags.Storage)) 696 { 697 UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp); 698 } 699 700 if (_dirty.HasFlag(DirtyFlags.Texture)) 701 { 702 if (program.UpdateTexturesWithoutTemplate) 703 { 704 UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp); 705 } 706 else 707 { 708 UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp); 709 } 710 } 711 712 if (_dirty.HasFlag(DirtyFlags.Image)) 713 { 714 UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp); 715 } 716 717 if (program.BindingSegments.Length > PipelineBase.DescriptorSetLayouts) 718 { 719 // Program is using extra sets, we need to bind those too. 720 721 BindExtraSets(cbs, program, pbp); 722 } 723 724 _dirty = DirtyFlags.None; 725 } 726 727 [MethodImpl(MethodImplOptions.AggressiveInlining)] 728 private static bool UpdateBuffer( 729 CommandBufferScoped cbs, 730 ref DescriptorBufferInfo info, 731 ref BufferRef buffer, 732 Auto<DisposableBuffer> dummyBuffer, 733 bool mirrorable) 734 { 735 int offset = buffer.Offset; 736 bool mirrored = false; 737 738 if (mirrorable) 739 { 740 info.Buffer = buffer.Buffer?.GetMirrorable(cbs, ref offset, (int)info.Range, out mirrored).Value ?? default; 741 } 742 else 743 { 744 info.Buffer = buffer.Buffer?.Get(cbs, offset, (int)info.Range, buffer.Write).Value ?? default; 745 } 746 747 info.Offset = (ulong)offset; 748 749 // The spec requires that buffers with null handle have offset as 0 and range as VK_WHOLE_SIZE. 750 if (info.Buffer.Handle == 0) 751 { 752 info.Buffer = dummyBuffer?.Get(cbs).Value ?? default; 753 info.Offset = 0; 754 info.Range = Vk.WholeSize; 755 } 756 757 return mirrored; 758 } 759 760 [MethodImpl(MethodImplOptions.AggressiveInlining)] 761 private void UpdateAndBind(CommandBufferScoped cbs, ShaderCollection program, int setIndex, PipelineBindPoint pbp) 762 { 763 var bindingSegments = program.BindingSegments[setIndex]; 764 765 if (bindingSegments.Length == 0) 766 { 767 return; 768 } 769 770 var dummyBuffer = _dummyBuffer?.GetBuffer(); 771 772 if (_updateDescriptorCacheCbIndex) 773 { 774 _updateDescriptorCacheCbIndex = false; 775 program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex); 776 } 777 778 var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs); 779 780 if (!program.HasMinimalLayout) 781 { 782 if (isNew) 783 { 784 Initialize(cbs, setIndex, dsc); 785 } 786 } 787 788 DescriptorSetTemplate template = program.Templates[setIndex]; 789 790 DescriptorSetTemplateWriter tu = _templateUpdater.Begin(template); 791 792 foreach (ResourceBindingSegment segment in bindingSegments) 793 { 794 int binding = segment.Binding; 795 int count = segment.Count; 796 797 if (setIndex == PipelineBase.UniformSetIndex) 798 { 799 for (int i = 0; i < count; i++) 800 { 801 int index = binding + i; 802 803 if (_uniformSet.Set(index)) 804 { 805 ref BufferRef buffer = ref _uniformBufferRefs[index]; 806 807 bool mirrored = UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true); 808 809 _uniformMirrored.Set(index, mirrored); 810 } 811 } 812 813 ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; 814 815 tu.Push(uniformBuffers.Slice(binding, count)); 816 } 817 else if (setIndex == PipelineBase.StorageSetIndex) 818 { 819 for (int i = 0; i < count; i++) 820 { 821 int index = binding + i; 822 823 ref BufferRef buffer = ref _storageBufferRefs[index]; 824 825 if (_storageSet.Set(index)) 826 { 827 ref var info = ref _storageBuffers[index]; 828 829 bool mirrored = UpdateBuffer(cbs, 830 ref info, 831 ref _storageBufferRefs[index], 832 dummyBuffer, 833 !buffer.Write && info.Range <= StorageBufferMaxMirrorable); 834 835 _storageMirrored.Set(index, mirrored); 836 } 837 } 838 839 ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers; 840 841 tu.Push(storageBuffers.Slice(binding, count)); 842 } 843 else if (setIndex == PipelineBase.TextureSetIndex) 844 { 845 if (!segment.IsArray) 846 { 847 if (segment.Type != ResourceType.BufferTexture) 848 { 849 Span<DescriptorImageInfo> textures = _textures; 850 851 for (int i = 0; i < count; i++) 852 { 853 ref var texture = ref textures[i]; 854 ref var refs = ref _textureRefs[binding + i]; 855 856 texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default; 857 texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; 858 859 if (texture.ImageView.Handle == 0) 860 { 861 texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; 862 } 863 864 if (texture.Sampler.Handle == 0) 865 { 866 texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; 867 } 868 } 869 870 tu.Push<DescriptorImageInfo>(textures[..count]); 871 } 872 else 873 { 874 Span<BufferView> bufferTextures = _bufferTextures; 875 876 for (int i = 0; i < count; i++) 877 { 878 bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default; 879 } 880 881 tu.Push<BufferView>(bufferTextures[..count]); 882 } 883 } 884 else 885 { 886 if (segment.Type != ResourceType.BufferTexture) 887 { 888 tu.Push(_textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler)); 889 } 890 else 891 { 892 tu.Push(_textureArrayRefs[binding].Array.GetBufferViews(cbs)); 893 } 894 } 895 } 896 else if (setIndex == PipelineBase.ImageSetIndex) 897 { 898 if (!segment.IsArray) 899 { 900 if (segment.Type != ResourceType.BufferImage) 901 { 902 Span<DescriptorImageInfo> images = _images; 903 904 for (int i = 0; i < count; i++) 905 { 906 images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default; 907 } 908 909 tu.Push<DescriptorImageInfo>(images[..count]); 910 } 911 else 912 { 913 Span<BufferView> bufferImages = _bufferImages; 914 915 for (int i = 0; i < count; i++) 916 { 917 bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default; 918 } 919 920 tu.Push<BufferView>(bufferImages[..count]); 921 } 922 } 923 else 924 { 925 if (segment.Type != ResourceType.BufferTexture) 926 { 927 tu.Push(_imageArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture)); 928 } 929 else 930 { 931 tu.Push(_imageArrayRefs[binding].Array.GetBufferViews(cbs)); 932 } 933 } 934 } 935 } 936 937 var sets = dsc.GetSets(); 938 _templateUpdater.Commit(_gd, _device, sets[0]); 939 940 _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty); 941 } 942 943 private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp) 944 { 945 int setIndex = PipelineBase.TextureSetIndex; 946 var bindingSegments = program.BindingSegments[setIndex]; 947 948 if (bindingSegments.Length == 0) 949 { 950 return; 951 } 952 953 if (_updateDescriptorCacheCbIndex) 954 { 955 _updateDescriptorCacheCbIndex = false; 956 program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex); 957 } 958 959 var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs); 960 961 foreach (ResourceBindingSegment segment in bindingSegments) 962 { 963 int binding = segment.Binding; 964 int count = segment.Count; 965 966 if (!segment.IsArray) 967 { 968 if (segment.Type != ResourceType.BufferTexture) 969 { 970 Span<DescriptorImageInfo> textures = _textures; 971 972 for (int i = 0; i < count; i++) 973 { 974 ref var texture = ref textures[i]; 975 ref var refs = ref _textureRefs[binding + i]; 976 977 texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default; 978 texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; 979 980 if (texture.ImageView.Handle == 0) 981 { 982 texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; 983 } 984 985 if (texture.Sampler.Handle == 0) 986 { 987 texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; 988 } 989 } 990 991 dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler); 992 } 993 else 994 { 995 Span<BufferView> bufferTextures = _bufferTextures; 996 997 for (int i = 0; i < count; i++) 998 { 999 bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default; 1000 } 1001 1002 dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer); 1003 } 1004 } 1005 else 1006 { 1007 if (segment.Type != ResourceType.BufferTexture) 1008 { 1009 dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler); 1010 } 1011 else 1012 { 1013 dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer); 1014 } 1015 } 1016 } 1017 1018 var sets = dsc.GetSets(); 1019 1020 _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty); 1021 } 1022 1023 [MethodImpl(MethodImplOptions.AggressiveInlining)] 1024 private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs) 1025 { 1026 int sequence = _pdSequence; 1027 var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex]; 1028 var dummyBuffer = _dummyBuffer?.GetBuffer(); 1029 1030 long updatedBindings = 0; 1031 DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>()); 1032 1033 foreach (ResourceBindingSegment segment in bindingSegments) 1034 { 1035 int binding = segment.Binding; 1036 int count = segment.Count; 1037 1038 ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; 1039 1040 for (int i = 0; i < count; i++) 1041 { 1042 int index = binding + i; 1043 1044 if (_uniformSet.Set(index)) 1045 { 1046 ref BufferRef buffer = ref _uniformBufferRefs[index]; 1047 1048 bool mirrored = UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true); 1049 1050 _uniformMirrored.Set(index, mirrored); 1051 } 1052 1053 if (_uniformSetPd[index] != sequence) 1054 { 1055 // Need to set this push descriptor (even if the buffer binding has not changed) 1056 1057 _uniformSetPd[index] = sequence; 1058 updatedBindings |= 1L << index; 1059 1060 writer.Push(MemoryMarshal.CreateReadOnlySpan(ref _uniformBuffers[index], 1)); 1061 } 1062 } 1063 } 1064 1065 if (updatedBindings > 0) 1066 { 1067 DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings); 1068 _templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout); 1069 } 1070 } 1071 1072 private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc) 1073 { 1074 // We don't support clearing texture descriptors currently. 1075 if (setIndex != PipelineBase.UniformSetIndex && setIndex != PipelineBase.StorageSetIndex) 1076 { 1077 return; 1078 } 1079 1080 var dummyBuffer = _dummyBuffer?.GetBuffer().Get(cbs).Value ?? default; 1081 1082 foreach (ResourceBindingSegment segment in _program.ClearSegments[setIndex]) 1083 { 1084 dsc.InitializeBuffers(0, segment.Binding, segment.Count, segment.Type.Convert(), dummyBuffer); 1085 } 1086 } 1087 1088 private void BindExtraSets(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp) 1089 { 1090 for (int setIndex = PipelineBase.DescriptorSetLayouts; setIndex < program.BindingSegments.Length; setIndex++) 1091 { 1092 var bindingSegments = program.BindingSegments[setIndex]; 1093 1094 if (bindingSegments.Length == 0) 1095 { 1096 continue; 1097 } 1098 1099 ResourceBindingSegment segment = bindingSegments[0]; 1100 1101 if (segment.IsArray) 1102 { 1103 DescriptorSet[] sets = null; 1104 1105 if (segment.Type == ResourceType.Texture || 1106 segment.Type == ResourceType.Sampler || 1107 segment.Type == ResourceType.TextureAndSampler || 1108 segment.Type == ResourceType.BufferTexture) 1109 { 1110 sets = _textureArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts].Array.GetDescriptorSets( 1111 _device, 1112 cbs, 1113 _templateUpdater, 1114 program, 1115 setIndex, 1116 _dummyTexture, 1117 _dummySampler); 1118 } 1119 else if (segment.Type == ResourceType.Image || segment.Type == ResourceType.BufferImage) 1120 { 1121 sets = _imageArrayExtraRefs[setIndex - PipelineBase.DescriptorSetLayouts].Array.GetDescriptorSets( 1122 _device, 1123 cbs, 1124 _templateUpdater, 1125 program, 1126 setIndex, 1127 _dummyTexture); 1128 } 1129 1130 if (sets != null) 1131 { 1132 _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty); 1133 } 1134 } 1135 } 1136 } 1137 1138 public void SignalCommandBufferChange() 1139 { 1140 _updateDescriptorCacheCbIndex = true; 1141 _dirty = DirtyFlags.All; 1142 1143 _uniformSet.Clear(); 1144 _storageSet.Clear(); 1145 AdvancePdSequence(); 1146 } 1147 1148 public void ForceTextureDirty() 1149 { 1150 SignalDirty(DirtyFlags.Texture); 1151 } 1152 1153 public void ForceImageDirty() 1154 { 1155 SignalDirty(DirtyFlags.Image); 1156 } 1157 1158 private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) 1159 { 1160 for (int i = 0; i < list.Length; i++) 1161 { 1162 if (list[i].Buffer == from) 1163 { 1164 list[i].Buffer = to; 1165 } 1166 } 1167 } 1168 1169 public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) 1170 { 1171 SwapBuffer(_uniformBufferRefs, from, to); 1172 SwapBuffer(_storageBufferRefs, from, to); 1173 } 1174 1175 protected virtual void Dispose(bool disposing) 1176 { 1177 if (disposing) 1178 { 1179 _dummyTexture.Dispose(); 1180 _dummySampler.Dispose(); 1181 _templateUpdater.Dispose(); 1182 } 1183 } 1184 1185 public void Dispose() 1186 { 1187 Dispose(true); 1188 } 1189 } 1190 }