PipelineBase.cs
1 using Ryujinx.Graphics.GAL; 2 using Ryujinx.Graphics.Shader; 3 using Silk.NET.Vulkan; 4 using System; 5 using System.Collections.Generic; 6 using System.Linq; 7 using System.Numerics; 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 FrontFace = Ryujinx.Graphics.GAL.FrontFace; 13 using IndexType = Ryujinx.Graphics.GAL.IndexType; 14 using PolygonMode = Ryujinx.Graphics.GAL.PolygonMode; 15 using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology; 16 using Viewport = Ryujinx.Graphics.GAL.Viewport; 17 18 namespace Ryujinx.Graphics.Vulkan 19 { 20 class PipelineBase : IDisposable 21 { 22 public const int DescriptorSetLayouts = 4; 23 24 public const int UniformSetIndex = 0; 25 public const int StorageSetIndex = 1; 26 public const int TextureSetIndex = 2; 27 public const int ImageSetIndex = 3; 28 29 protected readonly VulkanRenderer Gd; 30 protected readonly Device Device; 31 public readonly PipelineCache PipelineCache; 32 33 public readonly AutoFlushCounter AutoFlush; 34 public readonly Action EndRenderPassDelegate; 35 36 protected PipelineDynamicState DynamicState; 37 protected bool IsMainPipeline; 38 private PipelineState _newState; 39 private bool _graphicsStateDirty; 40 private bool _computeStateDirty; 41 private bool _bindingBarriersDirty; 42 private PrimitiveTopology _topology; 43 44 private ulong _currentPipelineHandle; 45 46 protected Auto<DisposablePipeline> Pipeline; 47 48 protected PipelineBindPoint Pbp; 49 50 protected CommandBufferScoped Cbs; 51 protected CommandBufferScoped? PreloadCbs; 52 protected CommandBuffer CommandBuffer; 53 54 public CommandBufferScoped CurrentCommandBuffer => Cbs; 55 56 private ShaderCollection _program; 57 58 protected FramebufferParams FramebufferParams; 59 private Auto<DisposableFramebuffer> _framebuffer; 60 private RenderPassHolder _rpHolder; 61 private Auto<DisposableRenderPass> _renderPass; 62 private RenderPassHolder _nullRenderPass; 63 private int _writtenAttachmentCount; 64 65 private bool _framebufferUsingColorWriteMask; 66 67 private ITexture[] _preMaskColors; 68 private ITexture _preMaskDepthStencil; 69 70 private readonly DescriptorSetUpdater _descriptorSetUpdater; 71 72 private IndexBufferState _indexBuffer; 73 private IndexBufferPattern _indexBufferPattern; 74 private readonly BufferState[] _transformFeedbackBuffers; 75 private readonly VertexBufferState[] _vertexBuffers; 76 private ulong _vertexBuffersDirty; 77 protected Rectangle<int> ClearScissor; 78 79 private readonly VertexBufferUpdater _vertexBufferUpdater; 80 81 public IndexBufferPattern QuadsToTrisPattern; 82 public IndexBufferPattern TriFanToTrisPattern; 83 84 private bool _needsIndexBufferRebind; 85 private bool _needsTransformFeedbackBuffersRebind; 86 87 private bool _tfEnabled; 88 private bool _tfActive; 89 90 private FeedbackLoopAspects _feedbackLoop; 91 private bool _passWritesDepthStencil; 92 93 private readonly PipelineColorBlendAttachmentState[] _storedBlend; 94 public ulong DrawCount { get; private set; } 95 public bool RenderPassActive { get; private set; } 96 97 public unsafe PipelineBase(VulkanRenderer gd, Device device) 98 { 99 Gd = gd; 100 Device = device; 101 102 AutoFlush = new AutoFlushCounter(gd); 103 EndRenderPassDelegate = EndRenderPass; 104 105 var pipelineCacheCreateInfo = new PipelineCacheCreateInfo 106 { 107 SType = StructureType.PipelineCacheCreateInfo, 108 }; 109 110 gd.Api.CreatePipelineCache(device, in pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); 111 112 _descriptorSetUpdater = new DescriptorSetUpdater(gd, device); 113 _vertexBufferUpdater = new VertexBufferUpdater(gd); 114 115 _transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers]; 116 _vertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers + 1]; 117 118 const int EmptyVbSize = 16; 119 120 using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize); 121 emptyVb.SetData(0, new byte[EmptyVbSize]); 122 _vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, 0, EmptyVbSize); 123 _vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length); 124 125 ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff); 126 127 _storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets]; 128 129 _newState.Initialize(); 130 } 131 132 public void Initialize() 133 { 134 _descriptorSetUpdater.Initialize(IsMainPipeline); 135 136 QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false); 137 TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true); 138 } 139 140 public unsafe void Barrier() 141 { 142 Gd.Barriers.QueueMemoryBarrier(); 143 } 144 145 public void ComputeBarrier() 146 { 147 MemoryBarrier memoryBarrier = new() 148 { 149 SType = StructureType.MemoryBarrier, 150 SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, 151 DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit, 152 }; 153 154 Gd.Api.CmdPipelineBarrier( 155 CommandBuffer, 156 PipelineStageFlags.ComputeShaderBit, 157 PipelineStageFlags.AllCommandsBit, 158 0, 159 1, 160 new ReadOnlySpan<MemoryBarrier>(in memoryBarrier), 161 0, 162 ReadOnlySpan<BufferMemoryBarrier>.Empty, 163 0, 164 ReadOnlySpan<ImageMemoryBarrier>.Empty); 165 } 166 167 public void BeginTransformFeedback(PrimitiveTopology topology) 168 { 169 Gd.Barriers.EnableTfbBarriers(true); 170 _tfEnabled = true; 171 } 172 173 public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) 174 { 175 EndRenderPass(); 176 177 var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, offset, size, true).Get(Cbs, offset, size, true).Value; 178 179 BufferHolder.InsertBufferBarrier( 180 Gd, 181 Cbs.CommandBuffer, 182 dst, 183 BufferHolder.DefaultAccessFlags, 184 AccessFlags.TransferWriteBit, 185 PipelineStageFlags.AllCommandsBit, 186 PipelineStageFlags.TransferBit, 187 offset, 188 size); 189 190 Gd.Api.CmdFillBuffer(CommandBuffer, dst, (ulong)offset, (ulong)size, value); 191 192 BufferHolder.InsertBufferBarrier( 193 Gd, 194 Cbs.CommandBuffer, 195 dst, 196 AccessFlags.TransferWriteBit, 197 BufferHolder.DefaultAccessFlags, 198 PipelineStageFlags.TransferBit, 199 PipelineStageFlags.AllCommandsBit, 200 offset, 201 size); 202 } 203 204 public unsafe void ClearRenderTargetColor(int index, int layer, int layerCount, ColorF color) 205 { 206 if (FramebufferParams == null || !FramebufferParams.IsValidColorAttachment(index)) 207 { 208 return; 209 } 210 211 if (_renderPass == null) 212 { 213 CreateRenderPass(); 214 } 215 216 Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate); 217 218 BeginRenderPass(); 219 220 var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha)); 221 var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue); 222 var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); 223 224 Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); 225 } 226 227 public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, bool stencilMask) 228 { 229 if (FramebufferParams == null || !FramebufferParams.HasDepthStencil) 230 { 231 return; 232 } 233 234 var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue)); 235 var flags = depthMask ? ImageAspectFlags.DepthBit : 0; 236 237 if (stencilMask) 238 { 239 flags |= ImageAspectFlags.StencilBit; 240 } 241 242 flags &= FramebufferParams.GetDepthStencilAspectFlags(); 243 244 if (flags == ImageAspectFlags.None) 245 { 246 return; 247 } 248 249 if (_renderPass == null) 250 { 251 CreateRenderPass(); 252 } 253 254 Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate); 255 256 BeginRenderPass(); 257 258 var attachment = new ClearAttachment(flags, 0, clearValue); 259 var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); 260 261 Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); 262 } 263 264 public unsafe void CommandBufferBarrier() 265 { 266 Gd.Barriers.QueueCommandBufferBarrier(); 267 } 268 269 public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) 270 { 271 EndRenderPass(); 272 273 var src = Gd.BufferManager.GetBuffer(CommandBuffer, source, srcOffset, size, false); 274 var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, dstOffset, size, true); 275 276 BufferHolder.Copy(Gd, Cbs, src, dst, srcOffset, dstOffset, size); 277 } 278 279 public void DirtyVertexBuffer(Auto<DisposableBuffer> buffer) 280 { 281 for (int i = 0; i < _vertexBuffers.Length; i++) 282 { 283 if (_vertexBuffers[i].BoundEquals(buffer)) 284 { 285 _vertexBuffersDirty |= 1UL << i; 286 } 287 } 288 } 289 290 public void DirtyIndexBuffer(Auto<DisposableBuffer> buffer) 291 { 292 if (_indexBuffer.BoundEquals(buffer)) 293 { 294 _needsIndexBufferRebind = true; 295 } 296 } 297 298 public void DispatchCompute(int groupsX, int groupsY, int groupsZ) 299 { 300 if (!_program.IsLinked) 301 { 302 return; 303 } 304 305 EndRenderPass(); 306 RecreateComputePipelineIfNeeded(); 307 308 Gd.Api.CmdDispatch(CommandBuffer, (uint)groupsX, (uint)groupsY, (uint)groupsZ); 309 } 310 311 public void DispatchComputeIndirect(Auto<DisposableBuffer> indirectBuffer, int indirectBufferOffset) 312 { 313 if (!_program.IsLinked) 314 { 315 return; 316 } 317 318 EndRenderPass(); 319 RecreateComputePipelineIfNeeded(); 320 321 Gd.Api.CmdDispatchIndirect(CommandBuffer, indirectBuffer.Get(Cbs, indirectBufferOffset, 12).Value, (ulong)indirectBufferOffset); 322 } 323 324 public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) 325 { 326 if (vertexCount == 0) 327 { 328 return; 329 } 330 331 if (!RecreateGraphicsPipelineIfNeeded()) 332 { 333 return; 334 } 335 336 BeginRenderPass(); 337 DrawCount++; 338 339 if (Gd.TopologyUnsupported(_topology)) 340 { 341 // Temporarily bind a conversion pattern as an index buffer. 342 _needsIndexBufferRebind = true; 343 344 IndexBufferPattern pattern = _topology switch 345 { 346 PrimitiveTopology.Quads => QuadsToTrisPattern, 347 PrimitiveTopology.TriangleFan or 348 PrimitiveTopology.Polygon => TriFanToTrisPattern, 349 _ => throw new NotSupportedException($"Unsupported topology: {_topology}"), 350 }; 351 352 BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount); 353 var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, handle, false); 354 355 Gd.Api.CmdBindIndexBuffer(CommandBuffer, buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value, 0, Silk.NET.Vulkan.IndexType.Uint32); 356 357 BeginRenderPass(); // May have been interrupted to set buffer data. 358 ResumeTransformFeedbackInternal(); 359 360 Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance); 361 } 362 else 363 { 364 ResumeTransformFeedbackInternal(); 365 366 Gd.Api.CmdDraw(CommandBuffer, (uint)vertexCount, (uint)instanceCount, (uint)firstVertex, (uint)firstInstance); 367 } 368 } 369 370 private void UpdateIndexBufferPattern() 371 { 372 IndexBufferPattern pattern = null; 373 374 if (Gd.TopologyUnsupported(_topology)) 375 { 376 pattern = _topology switch 377 { 378 PrimitiveTopology.Quads => QuadsToTrisPattern, 379 PrimitiveTopology.TriangleFan or 380 PrimitiveTopology.Polygon => TriFanToTrisPattern, 381 _ => throw new NotSupportedException($"Unsupported topology: {_topology}"), 382 }; 383 } 384 385 if (_indexBufferPattern != pattern) 386 { 387 _indexBufferPattern = pattern; 388 _needsIndexBufferRebind = true; 389 } 390 } 391 392 public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) 393 { 394 if (indexCount == 0) 395 { 396 return; 397 } 398 399 UpdateIndexBufferPattern(); 400 401 if (!RecreateGraphicsPipelineIfNeeded()) 402 { 403 return; 404 } 405 406 BeginRenderPass(); 407 DrawCount++; 408 409 if (_indexBufferPattern != null) 410 { 411 // Convert the index buffer into a supported topology. 412 IndexBufferPattern pattern = _indexBufferPattern; 413 414 int convertedCount = pattern.GetConvertedCount(indexCount); 415 416 if (_needsIndexBufferRebind) 417 { 418 _indexBuffer.BindConvertedIndexBuffer(Gd, Cbs, firstIndex, indexCount, convertedCount, pattern); 419 420 _needsIndexBufferRebind = false; 421 } 422 423 BeginRenderPass(); // May have been interrupted to set buffer data. 424 ResumeTransformFeedbackInternal(); 425 426 Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)convertedCount, (uint)instanceCount, 0, firstVertex, (uint)firstInstance); 427 } 428 else 429 { 430 ResumeTransformFeedbackInternal(); 431 432 Gd.Api.CmdDrawIndexed(CommandBuffer, (uint)indexCount, (uint)instanceCount, (uint)firstIndex, firstVertex, (uint)firstInstance); 433 } 434 } 435 436 public void DrawIndexedIndirect(BufferRange indirectBuffer) 437 { 438 var buffer = Gd.BufferManager 439 .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) 440 .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; 441 442 UpdateIndexBufferPattern(); 443 444 if (!RecreateGraphicsPipelineIfNeeded()) 445 { 446 return; 447 } 448 449 BeginRenderPass(); 450 DrawCount++; 451 452 if (_indexBufferPattern != null) 453 { 454 // Convert the index buffer into a supported topology. 455 IndexBufferPattern pattern = _indexBufferPattern; 456 457 Auto<DisposableBuffer> indirectBufferAuto = _indexBuffer.BindConvertedIndexBufferIndirect( 458 Gd, 459 Cbs, 460 indirectBuffer, 461 BufferRange.Empty, 462 pattern, 463 false, 464 1, 465 indirectBuffer.Size); 466 467 _needsIndexBufferRebind = false; 468 469 BeginRenderPass(); // May have been interrupted to set buffer data. 470 ResumeTransformFeedbackInternal(); 471 472 Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value, 0, 1, (uint)indirectBuffer.Size); 473 } 474 else 475 { 476 ResumeTransformFeedbackInternal(); 477 478 Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size); 479 } 480 } 481 482 public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) 483 { 484 var countBuffer = Gd.BufferManager 485 .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) 486 .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; 487 488 var buffer = Gd.BufferManager 489 .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) 490 .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; 491 492 UpdateIndexBufferPattern(); 493 494 if (!RecreateGraphicsPipelineIfNeeded()) 495 { 496 return; 497 } 498 499 BeginRenderPass(); 500 DrawCount++; 501 502 if (_indexBufferPattern != null) 503 { 504 // Convert the index buffer into a supported topology. 505 IndexBufferPattern pattern = _indexBufferPattern; 506 507 Auto<DisposableBuffer> indirectBufferAuto = _indexBuffer.BindConvertedIndexBufferIndirect( 508 Gd, 509 Cbs, 510 indirectBuffer, 511 parameterBuffer, 512 pattern, 513 true, 514 maxDrawCount, 515 stride); 516 517 _needsIndexBufferRebind = false; 518 519 BeginRenderPass(); // May have been interrupted to set buffer data. 520 ResumeTransformFeedbackInternal(); 521 522 if (Gd.Capabilities.SupportsIndirectParameters) 523 { 524 Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount( 525 CommandBuffer, 526 indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value, 527 0, 528 countBuffer, 529 (ulong)parameterBuffer.Offset, 530 (uint)maxDrawCount, 531 (uint)stride); 532 } 533 else 534 { 535 // This is also fine because the indirect data conversion always zeros 536 // the entries that are past the current draw count. 537 538 Gd.Api.CmdDrawIndexedIndirect( 539 CommandBuffer, 540 indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value, 541 0, 542 (uint)maxDrawCount, 543 (uint)stride); 544 } 545 } 546 else 547 { 548 ResumeTransformFeedbackInternal(); 549 550 if (Gd.Capabilities.SupportsIndirectParameters) 551 { 552 Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount( 553 CommandBuffer, 554 buffer, 555 (ulong)indirectBuffer.Offset, 556 countBuffer, 557 (ulong)parameterBuffer.Offset, 558 (uint)maxDrawCount, 559 (uint)stride); 560 } 561 else 562 { 563 // Not fully correct, but we can't do much better if the host does not support indirect count. 564 Gd.Api.CmdDrawIndexedIndirect( 565 CommandBuffer, 566 buffer, 567 (ulong)indirectBuffer.Offset, 568 (uint)maxDrawCount, 569 (uint)stride); 570 } 571 } 572 } 573 574 public void DrawIndirect(BufferRange indirectBuffer) 575 { 576 // TODO: Support quads and other unsupported topologies. 577 578 var buffer = Gd.BufferManager 579 .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) 580 .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size, false).Value; 581 582 if (!RecreateGraphicsPipelineIfNeeded()) 583 { 584 return; 585 } 586 587 BeginRenderPass(); 588 ResumeTransformFeedbackInternal(); 589 DrawCount++; 590 591 Gd.Api.CmdDrawIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size); 592 } 593 594 public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) 595 { 596 if (!Gd.Capabilities.SupportsIndirectParameters) 597 { 598 // TODO: Fallback for when this is not supported. 599 throw new NotSupportedException(); 600 } 601 602 var buffer = Gd.BufferManager 603 .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) 604 .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size, false).Value; 605 606 var countBuffer = Gd.BufferManager 607 .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) 608 .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size, false).Value; 609 610 // TODO: Support quads and other unsupported topologies. 611 612 if (!RecreateGraphicsPipelineIfNeeded()) 613 { 614 return; 615 } 616 617 BeginRenderPass(); 618 ResumeTransformFeedbackInternal(); 619 DrawCount++; 620 621 Gd.DrawIndirectCountApi.CmdDrawIndirectCount( 622 CommandBuffer, 623 buffer, 624 (ulong)indirectBuffer.Offset, 625 countBuffer, 626 (ulong)parameterBuffer.Offset, 627 (uint)maxDrawCount, 628 (uint)stride); 629 } 630 631 public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) 632 { 633 if (texture is TextureView srcTexture) 634 { 635 var oldCullMode = _newState.CullMode; 636 var oldStencilTestEnable = _newState.StencilTestEnable; 637 var oldDepthTestEnable = _newState.DepthTestEnable; 638 var oldDepthWriteEnable = _newState.DepthWriteEnable; 639 var oldViewports = DynamicState.Viewports; 640 var oldViewportsCount = _newState.ViewportsCount; 641 var oldTopology = _topology; 642 643 _newState.CullMode = CullModeFlags.None; 644 _newState.StencilTestEnable = false; 645 _newState.DepthTestEnable = false; 646 _newState.DepthWriteEnable = false; 647 SignalStateChange(); 648 649 Gd.HelperShader.DrawTexture( 650 Gd, 651 this, 652 srcTexture, 653 sampler, 654 srcRegion, 655 dstRegion); 656 657 _newState.CullMode = oldCullMode; 658 _newState.StencilTestEnable = oldStencilTestEnable; 659 _newState.DepthTestEnable = oldDepthTestEnable; 660 _newState.DepthWriteEnable = oldDepthWriteEnable; 661 SetPrimitiveTopology(oldTopology); 662 663 DynamicState.SetViewports(ref oldViewports, oldViewportsCount); 664 665 _newState.ViewportsCount = oldViewportsCount; 666 SignalStateChange(); 667 } 668 } 669 670 public void EndTransformFeedback() 671 { 672 Gd.Barriers.EnableTfbBarriers(false); 673 PauseTransformFeedbackInternal(); 674 _tfEnabled = false; 675 } 676 677 public bool IsCommandBufferActive(CommandBuffer cb) 678 { 679 return CommandBuffer.Handle == cb.Handle; 680 } 681 682 internal void Rebind(Auto<DisposableBuffer> buffer, int offset, int size) 683 { 684 _descriptorSetUpdater.Rebind(buffer, offset, size); 685 686 if (_indexBuffer.Overlaps(buffer, offset, size)) 687 { 688 _indexBuffer.BindIndexBuffer(Gd, Cbs); 689 } 690 691 for (int i = 0; i < _vertexBuffers.Length; i++) 692 { 693 if (_vertexBuffers[i].Overlaps(buffer, offset, size)) 694 { 695 _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater); 696 } 697 } 698 699 _vertexBufferUpdater.Commit(Cbs); 700 } 701 702 public void SetAlphaTest(bool enable, float reference, CompareOp op) 703 { 704 // This is currently handled using shader specialization, as Vulkan does not support alpha test. 705 // In the future, we may want to use this to write the reference value into the support buffer, 706 // to avoid creating one version of the shader per reference value used. 707 } 708 709 public void SetBlendState(AdvancedBlendDescriptor blend) 710 { 711 for (int index = 0; index < Constants.MaxRenderTargets; index++) 712 { 713 ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index]; 714 715 if (index == 0) 716 { 717 var blendOp = blend.Op.Convert(); 718 719 vkBlend = new PipelineColorBlendAttachmentState( 720 blendEnable: true, 721 colorBlendOp: blendOp, 722 alphaBlendOp: blendOp, 723 colorWriteMask: vkBlend.ColorWriteMask); 724 725 if (Gd.Capabilities.SupportsBlendEquationAdvancedNonPreMultipliedSrcColor) 726 { 727 _newState.AdvancedBlendSrcPreMultiplied = blend.SrcPreMultiplied; 728 } 729 730 if (Gd.Capabilities.SupportsBlendEquationAdvancedCorrelatedOverlap) 731 { 732 _newState.AdvancedBlendOverlap = blend.Overlap.Convert(); 733 } 734 } 735 else 736 { 737 vkBlend = new PipelineColorBlendAttachmentState( 738 colorWriteMask: vkBlend.ColorWriteMask); 739 } 740 741 if (vkBlend.ColorWriteMask == 0) 742 { 743 _storedBlend[index] = vkBlend; 744 745 vkBlend = new PipelineColorBlendAttachmentState(); 746 } 747 } 748 749 SignalStateChange(); 750 } 751 752 public void SetBlendState(int index, BlendDescriptor blend) 753 { 754 ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index]; 755 756 if (blend.Enable) 757 { 758 vkBlend.BlendEnable = blend.Enable; 759 vkBlend.SrcColorBlendFactor = blend.ColorSrcFactor.Convert(); 760 vkBlend.DstColorBlendFactor = blend.ColorDstFactor.Convert(); 761 vkBlend.ColorBlendOp = blend.ColorOp.Convert(); 762 vkBlend.SrcAlphaBlendFactor = blend.AlphaSrcFactor.Convert(); 763 vkBlend.DstAlphaBlendFactor = blend.AlphaDstFactor.Convert(); 764 vkBlend.AlphaBlendOp = blend.AlphaOp.Convert(); 765 } 766 else 767 { 768 vkBlend = new PipelineColorBlendAttachmentState( 769 colorWriteMask: vkBlend.ColorWriteMask); 770 } 771 772 if (vkBlend.ColorWriteMask == 0) 773 { 774 _storedBlend[index] = vkBlend; 775 776 vkBlend = new PipelineColorBlendAttachmentState(); 777 } 778 779 DynamicState.SetBlendConstants( 780 blend.BlendConstant.Red, 781 blend.BlendConstant.Green, 782 blend.BlendConstant.Blue, 783 blend.BlendConstant.Alpha); 784 785 // Reset advanced blend state back defaults to the cache to help the pipeline cache. 786 _newState.AdvancedBlendSrcPreMultiplied = true; 787 _newState.AdvancedBlendDstPreMultiplied = true; 788 _newState.AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt; 789 790 SignalStateChange(); 791 } 792 793 public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) 794 { 795 DynamicState.SetDepthBias(factor, units, clamp); 796 797 _newState.DepthBiasEnable = enables != 0; 798 SignalStateChange(); 799 } 800 801 public void SetDepthClamp(bool clamp) 802 { 803 _newState.DepthClampEnable = clamp; 804 SignalStateChange(); 805 } 806 807 public void SetDepthMode(DepthMode mode) 808 { 809 bool oldMode = _newState.DepthMode; 810 _newState.DepthMode = mode == DepthMode.MinusOneToOne; 811 if (_newState.DepthMode != oldMode) 812 { 813 SignalStateChange(); 814 } 815 } 816 817 public void SetDepthTest(DepthTestDescriptor depthTest) 818 { 819 _newState.DepthTestEnable = depthTest.TestEnable; 820 _newState.DepthWriteEnable = depthTest.WriteEnable; 821 _newState.DepthCompareOp = depthTest.Func.Convert(); 822 823 UpdatePassDepthStencil(); 824 SignalStateChange(); 825 } 826 827 public void SetFaceCulling(bool enable, Face face) 828 { 829 _newState.CullMode = enable ? face.Convert() : CullModeFlags.None; 830 SignalStateChange(); 831 } 832 833 public void SetFrontFace(FrontFace frontFace) 834 { 835 _newState.FrontFace = frontFace.Convert(); 836 SignalStateChange(); 837 } 838 839 public void SetImage(ShaderStage stage, int binding, ITexture image) 840 { 841 _descriptorSetUpdater.SetImage(Cbs, stage, binding, image); 842 } 843 844 public void SetImage(int binding, Auto<DisposableImageView> image) 845 { 846 _descriptorSetUpdater.SetImage(binding, image); 847 } 848 849 public void SetImageArray(ShaderStage stage, int binding, IImageArray array) 850 { 851 _descriptorSetUpdater.SetImageArray(Cbs, stage, binding, array); 852 } 853 854 public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) 855 { 856 _descriptorSetUpdater.SetImageArraySeparate(Cbs, stage, setIndex, array); 857 } 858 859 public void SetIndexBuffer(BufferRange buffer, IndexType type) 860 { 861 if (buffer.Handle != BufferHandle.Null) 862 { 863 _indexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type.Convert()); 864 } 865 else 866 { 867 _indexBuffer = IndexBufferState.Null; 868 } 869 870 _needsIndexBufferRebind = true; 871 } 872 873 public void SetLineParameters(float width, bool smooth) 874 { 875 _newState.LineWidth = width; 876 SignalStateChange(); 877 } 878 879 public void SetLogicOpState(bool enable, LogicalOp op) 880 { 881 _newState.LogicOpEnable = enable; 882 _newState.LogicOp = op.Convert(); 883 SignalStateChange(); 884 } 885 886 public void SetMultisampleState(MultisampleDescriptor multisample) 887 { 888 _newState.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable; 889 _newState.AlphaToOneEnable = multisample.AlphaToOneEnable; 890 SignalStateChange(); 891 } 892 893 public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel) 894 { 895 _newState.PatchControlPoints = (uint)vertices; 896 SignalStateChange(); 897 898 // TODO: Default levels (likely needs emulation on shaders?) 899 } 900 901 public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) 902 { 903 // TODO. 904 } 905 906 public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) 907 { 908 // TODO. 909 } 910 911 public void SetPrimitiveRestart(bool enable, int index) 912 { 913 _newState.PrimitiveRestartEnable = enable; 914 // TODO: What to do about the index? 915 SignalStateChange(); 916 } 917 918 public void SetPrimitiveTopology(PrimitiveTopology topology) 919 { 920 _topology = topology; 921 922 var vkTopology = Gd.TopologyRemap(topology).Convert(); 923 924 _newState.Topology = vkTopology; 925 926 SignalStateChange(); 927 } 928 929 public void SetProgram(IProgram program) 930 { 931 var internalProgram = (ShaderCollection)program; 932 var stages = internalProgram.GetInfos(); 933 934 _program = internalProgram; 935 936 _descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0); 937 _bindingBarriersDirty = true; 938 939 _newState.PipelineLayout = internalProgram.PipelineLayout; 940 _newState.HasTessellationControlShader = internalProgram.HasTessellationControlShader; 941 _newState.StagesCount = (uint)stages.Length; 942 943 stages.CopyTo(_newState.Stages.AsSpan()[..stages.Length]); 944 945 SignalStateChange(); 946 947 if (internalProgram.IsCompute) 948 { 949 EndRenderPass(); 950 } 951 } 952 953 public void Specialize<T>(in T data) where T : unmanaged 954 { 955 var dataSpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in data), 1)); 956 957 if (!dataSpan.SequenceEqual(_newState.SpecializationData.Span)) 958 { 959 _newState.SpecializationData = new SpecData(dataSpan); 960 961 SignalStateChange(); 962 } 963 } 964 965 protected virtual void SignalAttachmentChange() 966 { 967 } 968 969 public void SetRasterizerDiscard(bool discard) 970 { 971 _newState.RasterizerDiscardEnable = discard; 972 SignalStateChange(); 973 974 if (!discard && Gd.IsQualcommProprietary) 975 { 976 // On Adreno, enabling rasterizer discard somehow corrupts the viewport state. 977 // Force it to be updated on next use to work around this bug. 978 DynamicState.ForceAllDirty(); 979 } 980 } 981 982 public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask) 983 { 984 int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length); 985 int writtenAttachments = 0; 986 987 for (int i = 0; i < count; i++) 988 { 989 ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i]; 990 var newMask = (ColorComponentFlags)componentMask[i]; 991 992 // When color write mask is 0, remove all blend state to help the pipeline cache. 993 // Restore it when the mask becomes non-zero. 994 if (vkBlend.ColorWriteMask != newMask) 995 { 996 if (newMask == 0) 997 { 998 _storedBlend[i] = vkBlend; 999 1000 vkBlend = new PipelineColorBlendAttachmentState(); 1001 } 1002 else if (vkBlend.ColorWriteMask == 0) 1003 { 1004 vkBlend = _storedBlend[i]; 1005 } 1006 } 1007 1008 vkBlend.ColorWriteMask = newMask; 1009 1010 if (componentMask[i] != 0) 1011 { 1012 writtenAttachments++; 1013 } 1014 } 1015 1016 if (_framebufferUsingColorWriteMask) 1017 { 1018 SetRenderTargetsInternal(_preMaskColors, _preMaskDepthStencil, true); 1019 } 1020 else 1021 { 1022 SignalStateChange(); 1023 1024 if (writtenAttachments != _writtenAttachmentCount) 1025 { 1026 SignalAttachmentChange(); 1027 _writtenAttachmentCount = writtenAttachments; 1028 } 1029 } 1030 } 1031 1032 private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) 1033 { 1034 CreateFramebuffer(colors, depthStencil, filterWriteMasked); 1035 CreateRenderPass(); 1036 SignalStateChange(); 1037 SignalAttachmentChange(); 1038 } 1039 1040 public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) 1041 { 1042 _framebufferUsingColorWriteMask = false; 1043 SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR); 1044 } 1045 1046 public void SetScissors(ReadOnlySpan<Rectangle<int>> regions) 1047 { 1048 int maxScissors = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; 1049 int count = Math.Min(maxScissors, regions.Length); 1050 if (count > 0) 1051 { 1052 ClearScissor = regions[0]; 1053 } 1054 1055 for (int i = 0; i < count; i++) 1056 { 1057 var region = regions[i]; 1058 var offset = new Offset2D(region.X, region.Y); 1059 var extent = new Extent2D((uint)region.Width, (uint)region.Height); 1060 1061 DynamicState.SetScissor(i, new Rect2D(offset, extent)); 1062 } 1063 1064 DynamicState.ScissorsCount = count; 1065 1066 _newState.ScissorsCount = (uint)count; 1067 SignalStateChange(); 1068 } 1069 1070 public void SetStencilTest(StencilTestDescriptor stencilTest) 1071 { 1072 DynamicState.SetStencilMasks( 1073 (uint)stencilTest.BackFuncMask, 1074 (uint)stencilTest.BackMask, 1075 (uint)stencilTest.BackFuncRef, 1076 (uint)stencilTest.FrontFuncMask, 1077 (uint)stencilTest.FrontMask, 1078 (uint)stencilTest.FrontFuncRef); 1079 1080 _newState.StencilTestEnable = stencilTest.TestEnable; 1081 _newState.StencilBackFailOp = stencilTest.BackSFail.Convert(); 1082 _newState.StencilBackPassOp = stencilTest.BackDpPass.Convert(); 1083 _newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert(); 1084 _newState.StencilBackCompareOp = stencilTest.BackFunc.Convert(); 1085 _newState.StencilFrontFailOp = stencilTest.FrontSFail.Convert(); 1086 _newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert(); 1087 _newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert(); 1088 _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert(); 1089 1090 UpdatePassDepthStencil(); 1091 SignalStateChange(); 1092 } 1093 1094 public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers) 1095 { 1096 _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, buffers); 1097 } 1098 1099 public void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers) 1100 { 1101 _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers); 1102 } 1103 1104 public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) 1105 { 1106 _descriptorSetUpdater.SetTextureAndSampler(Cbs, stage, binding, texture, sampler); 1107 } 1108 1109 public void SetTextureAndSamplerIdentitySwizzle(ShaderStage stage, int binding, ITexture texture, ISampler sampler) 1110 { 1111 _descriptorSetUpdater.SetTextureAndSamplerIdentitySwizzle(Cbs, stage, binding, texture, sampler); 1112 } 1113 1114 public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) 1115 { 1116 _descriptorSetUpdater.SetTextureArray(Cbs, stage, binding, array); 1117 } 1118 1119 public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) 1120 { 1121 _descriptorSetUpdater.SetTextureArraySeparate(Cbs, stage, setIndex, array); 1122 } 1123 1124 public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers) 1125 { 1126 PauseTransformFeedbackInternal(); 1127 1128 int count = Math.Min(Constants.MaxTransformFeedbackBuffers, buffers.Length); 1129 1130 for (int i = 0; i < count; i++) 1131 { 1132 var range = buffers[i]; 1133 1134 _transformFeedbackBuffers[i].Dispose(); 1135 1136 if (range.Handle != BufferHandle.Null) 1137 { 1138 _transformFeedbackBuffers[i] = 1139 new BufferState(Gd.BufferManager.GetBuffer(CommandBuffer, range.Handle, range.Offset, range.Size, true), range.Offset, range.Size); 1140 _transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i); 1141 } 1142 else 1143 { 1144 _transformFeedbackBuffers[i] = BufferState.Null; 1145 } 1146 } 1147 } 1148 1149 public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers) 1150 { 1151 _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers); 1152 } 1153 1154 public void SetUserClipDistance(int index, bool enableClip) 1155 { 1156 // TODO. 1157 } 1158 1159 public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) 1160 { 1161 var formatCapabilities = Gd.FormatCapabilities; 1162 1163 Span<int> newVbScalarSizes = stackalloc int[Constants.MaxVertexBuffers]; 1164 1165 int count = Math.Min(Constants.MaxVertexAttributes, vertexAttribs.Length); 1166 uint dirtyVbSizes = 0; 1167 1168 for (int i = 0; i < count; i++) 1169 { 1170 var attribute = vertexAttribs[i]; 1171 var rawIndex = attribute.BufferIndex; 1172 var bufferIndex = attribute.IsZero ? 0 : rawIndex + 1; 1173 1174 if (!attribute.IsZero) 1175 { 1176 newVbScalarSizes[rawIndex] = Math.Max(newVbScalarSizes[rawIndex], attribute.Format.GetScalarSize()); 1177 dirtyVbSizes |= 1u << rawIndex; 1178 } 1179 1180 _newState.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription( 1181 (uint)i, 1182 (uint)bufferIndex, 1183 formatCapabilities.ConvertToVertexVkFormat(attribute.Format), 1184 (uint)attribute.Offset); 1185 } 1186 1187 while (dirtyVbSizes != 0) 1188 { 1189 int dirtyBit = BitOperations.TrailingZeroCount(dirtyVbSizes); 1190 1191 ref var buffer = ref _vertexBuffers[dirtyBit + 1]; 1192 1193 if (buffer.AttributeScalarAlignment != newVbScalarSizes[dirtyBit]) 1194 { 1195 _vertexBuffersDirty |= 1UL << (dirtyBit + 1); 1196 buffer.AttributeScalarAlignment = newVbScalarSizes[dirtyBit]; 1197 } 1198 1199 dirtyVbSizes &= ~(1u << dirtyBit); 1200 } 1201 1202 _newState.VertexAttributeDescriptionsCount = (uint)count; 1203 SignalStateChange(); 1204 } 1205 1206 public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers) 1207 { 1208 int count = Math.Min(Constants.MaxVertexBuffers, vertexBuffers.Length); 1209 1210 _newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex); 1211 1212 int validCount = 1; 1213 1214 BufferHandle lastHandle = default; 1215 Auto<DisposableBuffer> lastBuffer = default; 1216 1217 for (int i = 0; i < count; i++) 1218 { 1219 var vertexBuffer = vertexBuffers[i]; 1220 1221 // TODO: Support divisor > 1 1222 var inputRate = vertexBuffer.Divisor != 0 ? VertexInputRate.Instance : VertexInputRate.Vertex; 1223 1224 if (vertexBuffer.Buffer.Handle != BufferHandle.Null) 1225 { 1226 Auto<DisposableBuffer> vb = (vertexBuffer.Buffer.Handle == lastHandle) ? lastBuffer : 1227 Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false); 1228 1229 lastHandle = vertexBuffer.Buffer.Handle; 1230 lastBuffer = vb; 1231 1232 if (vb != null) 1233 { 1234 int binding = i + 1; 1235 int descriptorIndex = validCount++; 1236 1237 _newState.Internal.VertexBindingDescriptions[descriptorIndex] = new VertexInputBindingDescription( 1238 (uint)binding, 1239 (uint)vertexBuffer.Stride, 1240 inputRate); 1241 1242 int vbSize = vertexBuffer.Buffer.Size; 1243 1244 if (Gd.Vendor == Vendor.Amd && !Gd.IsMoltenVk && vertexBuffer.Stride > 0) 1245 { 1246 // AMD has a bug where if offset + stride * count is greater than 1247 // the size, then the last attribute will have the wrong value. 1248 // As a workaround, simply use the full buffer size. 1249 int remainder = vbSize % vertexBuffer.Stride; 1250 if (remainder != 0) 1251 { 1252 vbSize += vertexBuffer.Stride - remainder; 1253 } 1254 } 1255 1256 ref var buffer = ref _vertexBuffers[binding]; 1257 int oldScalarAlign = buffer.AttributeScalarAlignment; 1258 1259 if (Gd.Capabilities.VertexBufferAlignment < 2 && 1260 (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) 1261 { 1262 if (!buffer.Matches(vb, descriptorIndex, vertexBuffer.Buffer.Offset, vbSize, vertexBuffer.Stride)) 1263 { 1264 buffer.Dispose(); 1265 1266 buffer = new VertexBufferState( 1267 vb, 1268 descriptorIndex, 1269 vertexBuffer.Buffer.Offset, 1270 vbSize, 1271 vertexBuffer.Stride); 1272 1273 buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState, _vertexBufferUpdater); 1274 } 1275 } 1276 else 1277 { 1278 // May need to be rewritten. Bind this buffer before draw. 1279 1280 buffer.Dispose(); 1281 1282 buffer = new VertexBufferState( 1283 vertexBuffer.Buffer.Handle, 1284 descriptorIndex, 1285 vertexBuffer.Buffer.Offset, 1286 vbSize, 1287 vertexBuffer.Stride); 1288 1289 _vertexBuffersDirty |= 1UL << binding; 1290 } 1291 1292 buffer.AttributeScalarAlignment = oldScalarAlign; 1293 } 1294 } 1295 } 1296 1297 _vertexBufferUpdater.Commit(Cbs); 1298 1299 _newState.VertexBindingDescriptionsCount = (uint)validCount; 1300 SignalStateChange(); 1301 } 1302 1303 public void SetViewports(ReadOnlySpan<Viewport> viewports) 1304 { 1305 int maxViewports = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; 1306 int count = Math.Min(maxViewports, viewports.Length); 1307 1308 static float Clamp(float value) 1309 { 1310 return Math.Clamp(value, 0f, 1f); 1311 } 1312 1313 DynamicState.ViewportsCount = (uint)count; 1314 1315 for (int i = 0; i < count; i++) 1316 { 1317 var viewport = viewports[i]; 1318 1319 DynamicState.SetViewport(i, new Silk.NET.Vulkan.Viewport( 1320 viewport.Region.X, 1321 viewport.Region.Y, 1322 viewport.Region.Width == 0f ? 1f : viewport.Region.Width, 1323 viewport.Region.Height == 0f ? 1f : viewport.Region.Height, 1324 Clamp(viewport.DepthNear), 1325 Clamp(viewport.DepthFar))); 1326 } 1327 1328 _newState.ViewportsCount = (uint)count; 1329 SignalStateChange(); 1330 } 1331 1332 public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) 1333 { 1334 _indexBuffer.Swap(from, to); 1335 1336 for (int i = 0; i < _vertexBuffers.Length; i++) 1337 { 1338 _vertexBuffers[i].Swap(from, to); 1339 } 1340 1341 for (int i = 0; i < _transformFeedbackBuffers.Length; i++) 1342 { 1343 _transformFeedbackBuffers[i].Swap(from, to); 1344 } 1345 1346 _descriptorSetUpdater.SwapBuffer(from, to); 1347 1348 SignalCommandBufferChange(); 1349 } 1350 1351 public void ForceTextureDirty() 1352 { 1353 _descriptorSetUpdater.ForceTextureDirty(); 1354 } 1355 1356 public void ForceImageDirty() 1357 { 1358 _descriptorSetUpdater.ForceImageDirty(); 1359 } 1360 1361 public unsafe void TextureBarrier() 1362 { 1363 Gd.Barriers.QueueTextureBarrier(); 1364 } 1365 1366 public void TextureBarrierTiled() 1367 { 1368 TextureBarrier(); 1369 } 1370 1371 protected void SignalCommandBufferChange() 1372 { 1373 _needsIndexBufferRebind = true; 1374 _needsTransformFeedbackBuffersRebind = true; 1375 _vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length); 1376 1377 _descriptorSetUpdater.SignalCommandBufferChange(); 1378 DynamicState.ForceAllDirty(); 1379 _currentPipelineHandle = 0; 1380 } 1381 1382 private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) 1383 { 1384 if (filterWriteMasked) 1385 { 1386 // TBDR GPUs don't work properly if the same attachment is bound to multiple targets, 1387 // due to each attachment being a copy of the real attachment, rather than a direct write. 1388 1389 // Just try to remove duplicate attachments. 1390 // Save a copy of the array to rebind when mask changes. 1391 1392 void MaskOut() 1393 { 1394 if (!_framebufferUsingColorWriteMask) 1395 { 1396 _preMaskColors = colors.ToArray(); 1397 _preMaskDepthStencil = depthStencil; 1398 } 1399 1400 // If true, then the framebuffer must be recreated when the mask changes. 1401 _framebufferUsingColorWriteMask = true; 1402 } 1403 1404 // Look for textures that are masked out. 1405 1406 for (int i = 0; i < colors.Length; i++) 1407 { 1408 if (colors[i] == null) 1409 { 1410 continue; 1411 } 1412 1413 ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i]; 1414 1415 for (int j = 0; j < i; j++) 1416 { 1417 // Check each binding for a duplicate binding before it. 1418 1419 if (colors[i] == colors[j]) 1420 { 1421 // Prefer the binding with no write mask. 1422 ref var vkBlend2 = ref _newState.Internal.ColorBlendAttachmentState[j]; 1423 if (vkBlend.ColorWriteMask == 0) 1424 { 1425 colors[i] = null; 1426 MaskOut(); 1427 } 1428 else if (vkBlend2.ColorWriteMask == 0) 1429 { 1430 colors[j] = null; 1431 MaskOut(); 1432 } 1433 } 1434 } 1435 } 1436 } 1437 1438 if (IsMainPipeline) 1439 { 1440 FramebufferParams?.ClearBindings(); 1441 } 1442 1443 FramebufferParams = new FramebufferParams(Device, colors, depthStencil); 1444 1445 if (IsMainPipeline) 1446 { 1447 FramebufferParams.AddBindings(); 1448 1449 _newState.FeedbackLoopAspects = FeedbackLoopAspects.None; 1450 _bindingBarriersDirty = true; 1451 } 1452 1453 _passWritesDepthStencil = false; 1454 UpdatePassDepthStencil(); 1455 UpdatePipelineAttachmentFormats(); 1456 } 1457 1458 protected void UpdatePipelineAttachmentFormats() 1459 { 1460 var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); 1461 FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); 1462 _newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask; 1463 _newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed; 1464 1465 for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) 1466 { 1467 dstAttachmentFormats[i] = 0; 1468 } 1469 1470 _newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1); 1471 _newState.HasDepthStencil = FramebufferParams.HasDepthStencil; 1472 _newState.SamplesCount = FramebufferParams.AttachmentSamples.Length != 0 ? FramebufferParams.AttachmentSamples[0] : 1; 1473 } 1474 1475 protected unsafe void CreateRenderPass() 1476 { 1477 var hasFramebuffer = FramebufferParams != null; 1478 1479 EndRenderPass(); 1480 1481 if (!hasFramebuffer || FramebufferParams.AttachmentsCount == 0) 1482 { 1483 // Use the null framebuffer. 1484 _nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams); 1485 1486 _rpHolder = _nullRenderPass; 1487 _renderPass = _nullRenderPass.GetRenderPass(); 1488 _framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams); 1489 } 1490 else 1491 { 1492 (_rpHolder, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs); 1493 1494 _renderPass = _rpHolder.GetRenderPass(); 1495 } 1496 } 1497 1498 protected void SignalStateChange() 1499 { 1500 _graphicsStateDirty = true; 1501 _computeStateDirty = true; 1502 } 1503 1504 private void RecreateComputePipelineIfNeeded() 1505 { 1506 if (_computeStateDirty || Pbp != PipelineBindPoint.Compute) 1507 { 1508 CreatePipeline(PipelineBindPoint.Compute); 1509 _computeStateDirty = false; 1510 Pbp = PipelineBindPoint.Compute; 1511 1512 if (_bindingBarriersDirty) 1513 { 1514 // Stale barriers may have been activated by switching program. Emit any that are relevant. 1515 _descriptorSetUpdater.InsertBindingBarriers(Cbs); 1516 1517 _bindingBarriersDirty = false; 1518 } 1519 } 1520 1521 Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate); 1522 1523 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute); 1524 } 1525 1526 private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects) 1527 { 1528 if (_feedbackLoop != aspects) 1529 { 1530 if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) 1531 { 1532 DynamicState.SetFeedbackLoop(aspects); 1533 } 1534 else 1535 { 1536 _newState.FeedbackLoopAspects = aspects; 1537 } 1538 1539 _feedbackLoop = aspects; 1540 1541 return true; 1542 } 1543 1544 return false; 1545 } 1546 1547 [MethodImpl(MethodImplOptions.AggressiveInlining)] 1548 private bool UpdateFeedbackLoop() 1549 { 1550 List<TextureView> hazards = _descriptorSetUpdater.FeedbackLoopHazards; 1551 1552 if ((hazards?.Count ?? 0) > 0) 1553 { 1554 FeedbackLoopAspects aspects = 0; 1555 1556 foreach (TextureView view in hazards) 1557 { 1558 // May need to enforce feedback loop layout here in the future. 1559 // Though technically, it should always work with the general layout. 1560 1561 if (view.Info.Format.IsDepthOrStencil()) 1562 { 1563 if (_passWritesDepthStencil) 1564 { 1565 // If depth/stencil isn't written in the pass, it doesn't count as a feedback loop. 1566 1567 aspects |= FeedbackLoopAspects.Depth; 1568 } 1569 } 1570 else 1571 { 1572 aspects |= FeedbackLoopAspects.Color; 1573 } 1574 } 1575 1576 return ChangeFeedbackLoop(aspects); 1577 } 1578 else if (_feedbackLoop != 0) 1579 { 1580 return ChangeFeedbackLoop(FeedbackLoopAspects.None); 1581 } 1582 1583 return false; 1584 } 1585 1586 private void UpdatePassDepthStencil() 1587 { 1588 if (!RenderPassActive) 1589 { 1590 _passWritesDepthStencil = false; 1591 } 1592 1593 // Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check. 1594 _passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable; 1595 } 1596 1597 private bool RecreateGraphicsPipelineIfNeeded() 1598 { 1599 if (AutoFlush.ShouldFlushDraw(DrawCount)) 1600 { 1601 Gd.FlushAllCommands(); 1602 } 1603 1604 DynamicState.ReplayIfDirty(Gd, CommandBuffer); 1605 1606 if (_needsIndexBufferRebind && _indexBufferPattern == null) 1607 { 1608 _indexBuffer.BindIndexBuffer(Gd, Cbs); 1609 _needsIndexBufferRebind = false; 1610 } 1611 1612 if (_needsTransformFeedbackBuffersRebind) 1613 { 1614 PauseTransformFeedbackInternal(); 1615 1616 for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++) 1617 { 1618 _transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i); 1619 } 1620 1621 _needsTransformFeedbackBuffersRebind = false; 1622 } 1623 1624 if (_vertexBuffersDirty != 0) 1625 { 1626 while (_vertexBuffersDirty != 0) 1627 { 1628 int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty); 1629 1630 _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater); 1631 1632 _vertexBuffersDirty &= ~(1UL << i); 1633 } 1634 1635 _vertexBufferUpdater.Commit(Cbs); 1636 } 1637 1638 if (_bindingBarriersDirty) 1639 { 1640 // Stale barriers may have been activated by switching program. Emit any that are relevant. 1641 _descriptorSetUpdater.InsertBindingBarriers(Cbs); 1642 1643 _bindingBarriersDirty = false; 1644 } 1645 1646 if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics) 1647 { 1648 if (!CreatePipeline(PipelineBindPoint.Graphics)) 1649 { 1650 return false; 1651 } 1652 1653 _graphicsStateDirty = false; 1654 Pbp = PipelineBindPoint.Graphics; 1655 } 1656 1657 Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate); 1658 1659 _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics); 1660 1661 return true; 1662 } 1663 1664 private bool CreatePipeline(PipelineBindPoint pbp) 1665 { 1666 // We can only create a pipeline if the have the shader stages set. 1667 if (_newState.Stages != null) 1668 { 1669 if (pbp == PipelineBindPoint.Graphics && _renderPass == null) 1670 { 1671 CreateRenderPass(); 1672 } 1673 1674 if (!_program.IsLinked) 1675 { 1676 // Background compile failed, we likely can't create the pipeline because the shader is broken 1677 // or the driver failed to compile it. 1678 1679 return false; 1680 } 1681 1682 var pipeline = pbp == PipelineBindPoint.Compute 1683 ? _newState.CreateComputePipeline(Gd, Device, _program, PipelineCache) 1684 : _newState.CreateGraphicsPipeline(Gd, Device, _program, PipelineCache, _renderPass.Get(Cbs).Value); 1685 1686 if (pipeline == null) 1687 { 1688 // Host failed to create the pipeline, likely due to driver bugs. 1689 1690 return false; 1691 } 1692 1693 ulong pipelineHandle = pipeline.GetUnsafe().Value.Handle; 1694 1695 if (_currentPipelineHandle != pipelineHandle) 1696 { 1697 _currentPipelineHandle = pipelineHandle; 1698 Pipeline = pipeline; 1699 1700 PauseTransformFeedbackInternal(); 1701 Gd.Api.CmdBindPipeline(CommandBuffer, pbp, Pipeline.Get(Cbs).Value); 1702 } 1703 } 1704 1705 return true; 1706 } 1707 1708 private unsafe void BeginRenderPass() 1709 { 1710 if (!RenderPassActive) 1711 { 1712 FramebufferParams.InsertLoadOpBarriers(Gd, Cbs); 1713 1714 var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height)); 1715 var clearValue = new ClearValue(); 1716 1717 var renderPassBeginInfo = new RenderPassBeginInfo 1718 { 1719 SType = StructureType.RenderPassBeginInfo, 1720 RenderPass = _renderPass.Get(Cbs).Value, 1721 Framebuffer = _framebuffer.Get(Cbs).Value, 1722 RenderArea = renderArea, 1723 PClearValues = &clearValue, 1724 ClearValueCount = 1, 1725 }; 1726 1727 Gd.Api.CmdBeginRenderPass(CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline); 1728 RenderPassActive = true; 1729 } 1730 } 1731 1732 public void EndRenderPass() 1733 { 1734 if (RenderPassActive) 1735 { 1736 FramebufferParams.AddStoreOpUsage(); 1737 1738 PauseTransformFeedbackInternal(); 1739 Gd.Api.CmdEndRenderPass(CommandBuffer); 1740 SignalRenderPassEnd(); 1741 RenderPassActive = false; 1742 } 1743 } 1744 1745 protected virtual void SignalRenderPassEnd() 1746 { 1747 } 1748 1749 private void PauseTransformFeedbackInternal() 1750 { 1751 if (_tfEnabled && _tfActive) 1752 { 1753 EndTransformFeedbackInternal(); 1754 _tfActive = false; 1755 } 1756 } 1757 1758 private void ResumeTransformFeedbackInternal() 1759 { 1760 if (_tfEnabled && !_tfActive) 1761 { 1762 BeginTransformFeedbackInternal(); 1763 _tfActive = true; 1764 } 1765 } 1766 1767 private unsafe void BeginTransformFeedbackInternal() 1768 { 1769 Gd.TransformFeedbackApi.CmdBeginTransformFeedback(CommandBuffer, 0, 0, null, null); 1770 } 1771 1772 private unsafe void EndTransformFeedbackInternal() 1773 { 1774 Gd.TransformFeedbackApi.CmdEndTransformFeedback(CommandBuffer, 0, 0, null, null); 1775 } 1776 1777 protected virtual void Dispose(bool disposing) 1778 { 1779 if (disposing) 1780 { 1781 _nullRenderPass?.Dispose(); 1782 _newState.Dispose(); 1783 _descriptorSetUpdater.Dispose(); 1784 _vertexBufferUpdater.Dispose(); 1785 1786 for (int i = 0; i < _vertexBuffers.Length; i++) 1787 { 1788 _vertexBuffers[i].Dispose(); 1789 } 1790 1791 for (int i = 0; i < _transformFeedbackBuffers.Length; i++) 1792 { 1793 _transformFeedbackBuffers[i].Dispose(); 1794 } 1795 1796 Pipeline?.Dispose(); 1797 1798 unsafe 1799 { 1800 Gd.Api.DestroyPipelineCache(Device, PipelineCache, null); 1801 } 1802 } 1803 } 1804 1805 public void Dispose() 1806 { 1807 Dispose(true); 1808 } 1809 } 1810 }