BufferManager.cs
1 using Ryujinx.Common; 2 using Ryujinx.Graphics.GAL; 3 using Ryujinx.Graphics.Gpu.Image; 4 using Ryujinx.Graphics.Gpu.Shader; 5 using Ryujinx.Graphics.Shader; 6 using Ryujinx.Memory.Range; 7 using System; 8 using System.Collections.Generic; 9 using System.Runtime.CompilerServices; 10 11 namespace Ryujinx.Graphics.Gpu.Memory 12 { 13 /// <summary> 14 /// Buffer manager. 15 /// </summary> 16 class BufferManager 17 { 18 private readonly GpuContext _context; 19 private readonly GpuChannel _channel; 20 21 private int _unalignedStorageBuffers; 22 public bool HasUnalignedStorageBuffers => _unalignedStorageBuffers > 0; 23 24 public bool HasTransformFeedbackOutputs { get; set; } 25 26 private IndexBuffer _indexBuffer; 27 private readonly VertexBuffer[] _vertexBuffers; 28 private readonly BufferBounds[] _transformFeedbackBuffers; 29 private readonly List<BufferTextureBinding> _bufferTextures; 30 private readonly List<BufferTextureArrayBinding<ITextureArray>> _bufferTextureArrays; 31 private readonly List<BufferTextureArrayBinding<IImageArray>> _bufferImageArrays; 32 private readonly BufferAssignment[] _ranges; 33 34 /// <summary> 35 /// Holds shader stage buffer state and binding information. 36 /// </summary> 37 private class BuffersPerStage 38 { 39 /// <summary> 40 /// Shader buffer binding information. 41 /// </summary> 42 public BufferDescriptor[] Bindings { get; private set; } 43 44 /// <summary> 45 /// Buffer regions. 46 /// </summary> 47 public BufferBounds[] Buffers { get; } 48 49 /// <summary> 50 /// Flag indicating if this binding is unaligned. 51 /// </summary> 52 public bool[] Unaligned { get; } 53 54 /// <summary> 55 /// Total amount of buffers used on the shader. 56 /// </summary> 57 public int Count { get; private set; } 58 59 /// <summary> 60 /// Creates a new instance of the shader stage buffer information. 61 /// </summary> 62 /// <param name="count">Maximum amount of buffers that the shader stage can use</param> 63 public BuffersPerStage(int count) 64 { 65 Bindings = new BufferDescriptor[count]; 66 Buffers = new BufferBounds[count]; 67 Unaligned = new bool[count]; 68 69 Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL))); 70 } 71 72 /// <summary> 73 /// Sets the region of a buffer at a given slot. 74 /// </summary> 75 /// <param name="index">Buffer slot</param> 76 /// <param name="range">Physical memory regions where the buffer is mapped</param> 77 /// <param name="flags">Buffer usage flags</param> 78 public void SetBounds(int index, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None) 79 { 80 Buffers[index] = new BufferBounds(range, flags); 81 } 82 83 /// <summary> 84 /// Sets shader buffer binding information. 85 /// </summary> 86 /// <param name="descriptors">Buffer binding information</param> 87 public void SetBindings(BufferDescriptor[] descriptors) 88 { 89 if (descriptors == null) 90 { 91 Count = 0; 92 return; 93 } 94 95 if ((Count = descriptors.Length) != 0) 96 { 97 Bindings = descriptors; 98 } 99 } 100 } 101 102 private readonly BuffersPerStage _cpStorageBuffers; 103 private readonly BuffersPerStage _cpUniformBuffers; 104 private readonly BuffersPerStage[] _gpStorageBuffers; 105 private readonly BuffersPerStage[] _gpUniformBuffers; 106 107 private bool _gpStorageBuffersDirty; 108 private bool _gpUniformBuffersDirty; 109 110 private bool _indexBufferDirty; 111 private bool _vertexBuffersDirty; 112 private uint _vertexBuffersEnableMask; 113 private bool _transformFeedbackBuffersDirty; 114 115 private bool _rebind; 116 117 /// <summary> 118 /// Creates a new instance of the buffer manager. 119 /// </summary> 120 /// <param name="context">GPU context that the buffer manager belongs to</param> 121 /// <param name="channel">GPU channel that the buffer manager belongs to</param> 122 public BufferManager(GpuContext context, GpuChannel channel) 123 { 124 _context = context; 125 _channel = channel; 126 127 _indexBuffer.Range = new MultiRange(MemoryManager.PteUnmapped, 0UL); 128 _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers]; 129 130 _transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers]; 131 132 _cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers); 133 _cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers); 134 135 _gpStorageBuffers = new BuffersPerStage[Constants.ShaderStages]; 136 _gpUniformBuffers = new BuffersPerStage[Constants.ShaderStages]; 137 138 for (int index = 0; index < Constants.ShaderStages; index++) 139 { 140 _gpStorageBuffers[index] = new BuffersPerStage(Constants.TotalGpStorageBuffers); 141 _gpUniformBuffers[index] = new BuffersPerStage(Constants.TotalGpUniformBuffers); 142 } 143 144 _bufferTextures = new List<BufferTextureBinding>(); 145 _bufferTextureArrays = new List<BufferTextureArrayBinding<ITextureArray>>(); 146 _bufferImageArrays = new List<BufferTextureArrayBinding<IImageArray>>(); 147 148 _ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages]; 149 } 150 151 /// <summary> 152 /// Sets the memory range with the index buffer data, to be used for subsequent draw calls. 153 /// </summary> 154 /// <param name="gpuVa">Start GPU virtual address of the index buffer</param> 155 /// <param name="size">Size, in bytes, of the index buffer</param> 156 /// <param name="type">Type of each index buffer element</param> 157 public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type) 158 { 159 MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer); 160 161 _indexBuffer.Range = range; 162 _indexBuffer.Type = type; 163 164 _indexBufferDirty = true; 165 } 166 167 /// <summary> 168 /// Sets a new index buffer that overrides the one set on the call to <see cref="CommitGraphicsBindings"/>. 169 /// </summary> 170 /// <param name="buffer">Buffer to be used as index buffer</param> 171 /// <param name="type">Type of each index buffer element</param> 172 public void SetIndexBuffer(BufferRange buffer, IndexType type) 173 { 174 _context.Renderer.Pipeline.SetIndexBuffer(buffer, type); 175 176 _indexBufferDirty = true; 177 } 178 179 /// <summary> 180 /// Sets the memory range with vertex buffer data, to be used for subsequent draw calls. 181 /// </summary> 182 /// <param name="index">Index of the vertex buffer (up to 16)</param> 183 /// <param name="gpuVa">GPU virtual address of the buffer</param> 184 /// <param name="size">Size in bytes of the buffer</param> 185 /// <param name="stride">Stride of the buffer, defined as the number of bytes of each vertex</param> 186 /// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param> 187 public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor) 188 { 189 MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer); 190 191 _vertexBuffers[index].Range = range; 192 _vertexBuffers[index].Stride = stride; 193 _vertexBuffers[index].Divisor = divisor; 194 195 _vertexBuffersDirty = true; 196 197 if (!range.IsUnmapped) 198 { 199 _vertexBuffersEnableMask |= 1u << index; 200 } 201 else 202 { 203 _vertexBuffersEnableMask &= ~(1u << index); 204 } 205 } 206 207 /// <summary> 208 /// Sets a transform feedback buffer on the graphics pipeline. 209 /// The output from the vertex transformation stages are written into the feedback buffer. 210 /// </summary> 211 /// <param name="index">Index of the transform feedback buffer</param> 212 /// <param name="gpuVa">Start GPU virtual address of the buffer</param> 213 /// <param name="size">Size in bytes of the transform feedback buffer</param> 214 public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) 215 { 216 MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback); 217 218 _transformFeedbackBuffers[index] = new BufferBounds(range); 219 _transformFeedbackBuffersDirty = true; 220 } 221 222 /// <summary> 223 /// Records the alignment of a storage buffer. 224 /// Unaligned storage buffers disable some optimizations on the shader. 225 /// </summary> 226 /// <param name="buffers">The binding list to modify</param> 227 /// <param name="index">Index of the storage buffer</param> 228 /// <param name="gpuVa">Start GPU virtual address of the buffer</param> 229 private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa) 230 { 231 bool unaligned = (gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1)) != 0; 232 233 if (unaligned || HasUnalignedStorageBuffers) 234 { 235 // Check if the alignment changed for this binding. 236 237 ref bool currentUnaligned = ref buffers.Unaligned[index]; 238 239 if (currentUnaligned != unaligned) 240 { 241 currentUnaligned = unaligned; 242 _unalignedStorageBuffers += unaligned ? 1 : -1; 243 } 244 } 245 } 246 247 /// <summary> 248 /// Sets a storage buffer on the compute pipeline. 249 /// Storage buffers can be read and written to on shaders. 250 /// </summary> 251 /// <param name="index">Index of the storage buffer</param> 252 /// <param name="gpuVa">Start GPU virtual address of the buffer</param> 253 /// <param name="size">Size in bytes of the storage buffer</param> 254 /// <param name="flags">Buffer usage flags</param> 255 public void SetComputeStorageBuffer(int index, ulong gpuVa, ulong size, BufferUsageFlags flags) 256 { 257 size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1); 258 259 RecordStorageAlignment(_cpStorageBuffers, index, gpuVa); 260 261 gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); 262 263 MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags)); 264 265 _cpStorageBuffers.SetBounds(index, range, flags); 266 } 267 268 /// <summary> 269 /// Sets a storage buffer on the graphics pipeline. 270 /// Storage buffers can be read and written to on shaders. 271 /// </summary> 272 /// <param name="stage">Index of the shader stage</param> 273 /// <param name="index">Index of the storage buffer</param> 274 /// <param name="gpuVa">Start GPU virtual address of the buffer</param> 275 /// <param name="size">Size in bytes of the storage buffer</param> 276 /// <param name="flags">Buffer usage flags</param> 277 public void SetGraphicsStorageBuffer(int stage, int index, ulong gpuVa, ulong size, BufferUsageFlags flags) 278 { 279 size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1); 280 281 BuffersPerStage buffers = _gpStorageBuffers[stage]; 282 283 RecordStorageAlignment(buffers, index, gpuVa); 284 285 gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); 286 287 MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags)); 288 289 if (!buffers.Buffers[index].Range.Equals(range)) 290 { 291 _gpStorageBuffersDirty = true; 292 } 293 294 buffers.SetBounds(index, range, flags); 295 } 296 297 /// <summary> 298 /// Sets a uniform buffer on the compute pipeline. 299 /// Uniform buffers are read-only from shaders, and have a small capacity. 300 /// </summary> 301 /// <param name="index">Index of the uniform buffer</param> 302 /// <param name="gpuVa">Start GPU virtual address of the buffer</param> 303 /// <param name="size">Size in bytes of the storage buffer</param> 304 public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size) 305 { 306 MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute); 307 308 _cpUniformBuffers.SetBounds(index, range); 309 } 310 311 /// <summary> 312 /// Sets a uniform buffer on the graphics pipeline. 313 /// Uniform buffers are read-only from shaders, and have a small capacity. 314 /// </summary> 315 /// <param name="stage">Index of the shader stage</param> 316 /// <param name="index">Index of the uniform buffer</param> 317 /// <param name="gpuVa">Start GPU virtual address of the buffer</param> 318 /// <param name="size">Size in bytes of the storage buffer</param> 319 public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size) 320 { 321 MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage)); 322 323 _gpUniformBuffers[stage].SetBounds(index, range); 324 _gpUniformBuffersDirty = true; 325 } 326 327 /// <summary> 328 /// Sets the number of vertices per instance on a instanced draw. Used for transform feedback emulation. 329 /// </summary> 330 /// <param name="vertexCount">Vertex count per instance</param> 331 public void SetInstancedDrawVertexCount(int vertexCount) 332 { 333 if (!_context.Capabilities.SupportsTransformFeedback && HasTransformFeedbackOutputs) 334 { 335 _context.SupportBufferUpdater.SetTfeVertexCount(vertexCount); 336 _context.SupportBufferUpdater.Commit(); 337 } 338 } 339 340 /// <summary> 341 /// Forces transform feedback and storage buffers to be updated on the next draw. 342 /// </summary> 343 public void ForceTransformFeedbackAndStorageBuffersDirty() 344 { 345 _transformFeedbackBuffersDirty = true; 346 _gpStorageBuffersDirty = true; 347 } 348 349 /// <summary> 350 /// Sets the binding points for the storage buffers bound on the compute pipeline. 351 /// </summary> 352 /// <param name="bindings">Bindings for the active shader</param> 353 public void SetComputeBufferBindings(CachedShaderBindings bindings) 354 { 355 _cpStorageBuffers.SetBindings(bindings.StorageBufferBindings[0]); 356 _cpUniformBuffers.SetBindings(bindings.ConstantBufferBindings[0]); 357 } 358 359 /// <summary> 360 /// Sets the binding points for the storage buffers bound on the graphics pipeline. 361 /// </summary> 362 /// <param name="bindings">Bindings for the active shader</param> 363 public void SetGraphicsBufferBindings(CachedShaderBindings bindings) 364 { 365 for (int i = 0; i < Constants.ShaderStages; i++) 366 { 367 _gpStorageBuffers[i].SetBindings(bindings.StorageBufferBindings[i]); 368 _gpUniformBuffers[i].SetBindings(bindings.ConstantBufferBindings[i]); 369 } 370 371 _gpStorageBuffersDirty = true; 372 _gpUniformBuffersDirty = true; 373 } 374 375 /// <summary> 376 /// Gets a bit mask indicating which compute uniform buffers are currently bound. 377 /// </summary> 378 /// <returns>Mask where each bit set indicates a bound constant buffer</returns> 379 public uint GetComputeUniformBufferUseMask() 380 { 381 uint mask = 0; 382 383 for (int i = 0; i < _cpUniformBuffers.Buffers.Length; i++) 384 { 385 if (!_cpUniformBuffers.Buffers[i].IsUnmapped) 386 { 387 mask |= 1u << i; 388 } 389 } 390 391 return mask; 392 } 393 394 /// <summary> 395 /// Gets a bit mask indicating which graphics uniform buffers are currently bound. 396 /// </summary> 397 /// <param name="stage">Index of the shader stage</param> 398 /// <returns>Mask where each bit set indicates a bound constant buffer</returns> 399 public uint GetGraphicsUniformBufferUseMask(int stage) 400 { 401 uint mask = 0; 402 403 for (int i = 0; i < _gpUniformBuffers[stage].Buffers.Length; i++) 404 { 405 if (!_gpUniformBuffers[stage].Buffers[i].IsUnmapped) 406 { 407 mask |= 1u << i; 408 } 409 } 410 411 return mask; 412 } 413 414 /// <summary> 415 /// Gets the address of the compute uniform buffer currently bound at the given index. 416 /// </summary> 417 /// <param name="index">Index of the uniform buffer binding</param> 418 /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns> 419 public ulong GetComputeUniformBufferAddress(int index) 420 { 421 return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address; 422 } 423 424 /// <summary> 425 /// Gets the size of the compute uniform buffer currently bound at the given index. 426 /// </summary> 427 /// <param name="index">Index of the uniform buffer binding</param> 428 /// <returns>The uniform buffer size, or an undefined value if the buffer is not currently bound</returns> 429 public int GetComputeUniformBufferSize(int index) 430 { 431 return (int)_cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Size; 432 } 433 434 /// <summary> 435 /// Gets the address of the graphics uniform buffer currently bound at the given index. 436 /// </summary> 437 /// <param name="stage">Index of the shader stage</param> 438 /// <param name="index">Index of the uniform buffer binding</param> 439 /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns> 440 public ulong GetGraphicsUniformBufferAddress(int stage, int index) 441 { 442 return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address; 443 } 444 445 /// <summary> 446 /// Gets the size of the graphics uniform buffer currently bound at the given index. 447 /// </summary> 448 /// <param name="stage">Index of the shader stage</param> 449 /// <param name="index">Index of the uniform buffer binding</param> 450 /// <returns>The uniform buffer size, or an undefined value if the buffer is not currently bound</returns> 451 public int GetGraphicsUniformBufferSize(int stage, int index) 452 { 453 return (int)_gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Size; 454 } 455 456 /// <summary> 457 /// Gets the bounds of the uniform buffer currently bound at the given index. 458 /// </summary> 459 /// <param name="isCompute">Indicates whenever the uniform is requested by the 3D or compute engine</param> 460 /// <param name="stage">Index of the shader stage, if the uniform is for the 3D engine</param> 461 /// <param name="index">Index of the uniform buffer binding</param> 462 /// <returns>The uniform buffer bounds, or an undefined value if the buffer is not currently bound</returns> 463 public ref BufferBounds GetUniformBufferBounds(bool isCompute, int stage, int index) 464 { 465 if (isCompute) 466 { 467 return ref _cpUniformBuffers.Buffers[index]; 468 } 469 else 470 { 471 return ref _gpUniformBuffers[stage].Buffers[index]; 472 } 473 } 474 475 /// <summary> 476 /// Ensures that the compute engine bindings are visible to the host GPU. 477 /// Note: this actually performs the binding using the host graphics API. 478 /// </summary> 479 public void CommitComputeBindings() 480 { 481 var bufferCache = _channel.MemoryManager.Physical.BufferCache; 482 483 BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true); 484 BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false); 485 486 CommitBufferTextureBindings(bufferCache); 487 488 // Force rebind after doing compute work. 489 Rebind(); 490 491 _context.SupportBufferUpdater.Commit(); 492 } 493 494 /// <summary> 495 /// Commit any queued buffer texture bindings. 496 /// </summary> 497 /// <param name="bufferCache">Buffer cache</param> 498 private void CommitBufferTextureBindings(BufferCache bufferCache) 499 { 500 if (_bufferTextures.Count > 0) 501 { 502 foreach (var binding in _bufferTextures) 503 { 504 var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); 505 var range = bufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore); 506 binding.Texture.SetStorage(range); 507 508 // The texture must be rebound to use the new storage if it was updated. 509 510 if (binding.IsImage) 511 { 512 _context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture); 513 } 514 else 515 { 516 _context.Renderer.Pipeline.SetTextureAndSampler(binding.Stage, binding.BindingInfo.Binding, binding.Texture, null); 517 } 518 } 519 520 _bufferTextures.Clear(); 521 } 522 523 if (_bufferTextureArrays.Count > 0 || _bufferImageArrays.Count > 0) 524 { 525 ITexture[] textureArray = new ITexture[1]; 526 527 foreach (var binding in _bufferTextureArrays) 528 { 529 var range = bufferCache.GetBufferRange(binding.Range, BufferStage.None); 530 binding.Texture.SetStorage(range); 531 532 textureArray[0] = binding.Texture; 533 binding.Array.SetTextures(binding.Index, textureArray); 534 } 535 536 foreach (var binding in _bufferImageArrays) 537 { 538 var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); 539 var range = bufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore); 540 binding.Texture.SetStorage(range); 541 542 textureArray[0] = binding.Texture; 543 binding.Array.SetImages(binding.Index, textureArray); 544 } 545 546 _bufferTextureArrays.Clear(); 547 _bufferImageArrays.Clear(); 548 } 549 } 550 551 /// <summary> 552 /// Ensures that the graphics engine bindings are visible to the host GPU. 553 /// Note: this actually performs the binding using the host graphics API. 554 /// </summary> 555 /// <param name="indexed">True if the index buffer is in use</param> 556 public void CommitGraphicsBindings(bool indexed) 557 { 558 var bufferCache = _channel.MemoryManager.Physical.BufferCache; 559 560 if (indexed) 561 { 562 if (_indexBufferDirty || _rebind) 563 { 564 _indexBufferDirty = false; 565 566 if (!_indexBuffer.Range.IsUnmapped) 567 { 568 BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer); 569 570 _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); 571 } 572 } 573 else if (!_indexBuffer.Range.IsUnmapped) 574 { 575 bufferCache.SynchronizeBufferRange(_indexBuffer.Range); 576 } 577 } 578 else if (_rebind) 579 { 580 _indexBufferDirty = true; 581 } 582 583 uint vbEnableMask = _vertexBuffersEnableMask; 584 585 if (_vertexBuffersDirty || _rebind) 586 { 587 _vertexBuffersDirty = false; 588 589 Span<VertexBufferDescriptor> vertexBuffers = stackalloc VertexBufferDescriptor[Constants.TotalVertexBuffers]; 590 591 for (int index = 0; (vbEnableMask >> index) != 0; index++) 592 { 593 VertexBuffer vb = _vertexBuffers[index]; 594 595 if (vb.Range.IsUnmapped) 596 { 597 continue; 598 } 599 600 BufferRange buffer = bufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer); 601 602 vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor); 603 } 604 605 _context.Renderer.Pipeline.SetVertexBuffers(vertexBuffers); 606 } 607 else 608 { 609 for (int index = 0; (vbEnableMask >> index) != 0; index++) 610 { 611 VertexBuffer vb = _vertexBuffers[index]; 612 613 if (vb.Range.IsUnmapped) 614 { 615 continue; 616 } 617 618 bufferCache.SynchronizeBufferRange(vb.Range); 619 } 620 } 621 622 if (_transformFeedbackBuffersDirty || _rebind) 623 { 624 _transformFeedbackBuffersDirty = false; 625 626 if (_context.Capabilities.SupportsTransformFeedback) 627 { 628 Span<BufferRange> tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers]; 629 630 for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) 631 { 632 BufferBounds tfb = _transformFeedbackBuffers[index]; 633 634 if (tfb.IsUnmapped) 635 { 636 tfbs[index] = BufferRange.Empty; 637 continue; 638 } 639 640 tfbs[index] = bufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true); 641 } 642 643 _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); 644 } 645 else if (HasTransformFeedbackOutputs) 646 { 647 Span<BufferAssignment> buffers = stackalloc BufferAssignment[Constants.TotalTransformFeedbackBuffers]; 648 649 int alignment = _context.Capabilities.StorageBufferOffsetAlignment; 650 651 for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) 652 { 653 BufferBounds tfb = _transformFeedbackBuffers[index]; 654 655 if (tfb.IsUnmapped) 656 { 657 buffers[index] = new BufferAssignment(index, BufferRange.Empty); 658 } 659 else 660 { 661 MultiRange range = tfb.Range; 662 ulong address0 = range.GetSubRange(0).Address; 663 ulong address = BitUtils.AlignDown(address0, (ulong)alignment); 664 665 if (range.Count == 1) 666 { 667 range = new MultiRange(address, range.GetSubRange(0).Size + (address0 - address)); 668 } 669 else 670 { 671 MemoryRange[] subRanges = new MemoryRange[range.Count]; 672 673 subRanges[0] = new MemoryRange(address, range.GetSubRange(0).Size + (address0 - address)); 674 675 for (int i = 1; i < range.Count; i++) 676 { 677 subRanges[i] = range.GetSubRange(i); 678 } 679 680 range = new MultiRange(subRanges); 681 } 682 683 int tfeOffset = ((int)address0 & (alignment - 1)) / 4; 684 685 _context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset); 686 687 buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true)); 688 } 689 } 690 691 _context.Renderer.Pipeline.SetStorageBuffers(buffers); 692 } 693 } 694 else 695 { 696 for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) 697 { 698 BufferBounds tfb = _transformFeedbackBuffers[index]; 699 700 if (tfb.IsUnmapped) 701 { 702 continue; 703 } 704 705 bufferCache.SynchronizeBufferRange(tfb.Range); 706 } 707 } 708 709 if (_gpStorageBuffersDirty || _rebind) 710 { 711 _gpStorageBuffersDirty = false; 712 713 BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true); 714 } 715 else 716 { 717 UpdateBuffers(_gpStorageBuffers); 718 } 719 720 if (_gpUniformBuffersDirty || _rebind) 721 { 722 _gpUniformBuffersDirty = false; 723 724 BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false); 725 } 726 else 727 { 728 UpdateBuffers(_gpUniformBuffers); 729 } 730 731 CommitBufferTextureBindings(bufferCache); 732 733 _rebind = false; 734 735 _context.SupportBufferUpdater.Commit(); 736 } 737 738 /// <summary> 739 /// Bind respective buffer bindings on the host API. 740 /// </summary> 741 /// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param> 742 /// <param name="bindings">Buffer memory ranges to bind</param> 743 /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param> 744 [MethodImpl(MethodImplOptions.AggressiveInlining)] 745 private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage) 746 { 747 int rangesCount = 0; 748 749 Span<BufferAssignment> ranges = _ranges; 750 751 for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) 752 { 753 ref var buffers = ref bindings[(int)stage - 1]; 754 BufferStage bufferStage = BufferStageUtils.FromShaderStage(stage); 755 756 for (int index = 0; index < buffers.Count; index++) 757 { 758 ref var bindingInfo = ref buffers.Bindings[index]; 759 760 BufferBounds bounds = buffers.Buffers[bindingInfo.Slot]; 761 762 if (!bounds.IsUnmapped) 763 { 764 var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); 765 var range = isStorage 766 ? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite) 767 : bufferCache.GetBufferRange(bounds.Range, bufferStage); 768 769 ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); 770 } 771 } 772 } 773 774 if (rangesCount != 0) 775 { 776 SetHostBuffers(ranges, rangesCount, isStorage); 777 } 778 } 779 780 /// <summary> 781 /// Bind respective buffer bindings on the host API. 782 /// </summary> 783 /// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param> 784 /// <param name="buffers">Buffer memory ranges to bind</param> 785 /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param> 786 [MethodImpl(MethodImplOptions.AggressiveInlining)] 787 private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage) 788 { 789 int rangesCount = 0; 790 791 Span<BufferAssignment> ranges = _ranges; 792 793 for (int index = 0; index < buffers.Count; index++) 794 { 795 ref var bindingInfo = ref buffers.Bindings[index]; 796 797 BufferBounds bounds = buffers.Buffers[bindingInfo.Slot]; 798 799 if (!bounds.IsUnmapped) 800 { 801 var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); 802 var range = isStorage 803 ? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite) 804 : bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute); 805 806 ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); 807 } 808 } 809 810 if (rangesCount != 0) 811 { 812 SetHostBuffers(ranges, rangesCount, isStorage); 813 } 814 } 815 816 /// <summary> 817 /// Bind respective buffer bindings on the host API. 818 /// </summary> 819 /// <param name="ranges">Host buffers to bind, with their offsets and sizes</param> 820 /// <param name="first">First binding point</param> 821 /// <param name="count">Number of bindings</param> 822 /// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param> 823 [MethodImpl(MethodImplOptions.AggressiveInlining)] 824 private void SetHostBuffers(ReadOnlySpan<BufferAssignment> ranges, int count, bool isStorage) 825 { 826 if (isStorage) 827 { 828 _context.Renderer.Pipeline.SetStorageBuffers(ranges[..count]); 829 } 830 else 831 { 832 _context.Renderer.Pipeline.SetUniformBuffers(ranges[..count]); 833 } 834 } 835 836 /// <summary> 837 /// Updates data for the already bound buffer bindings. 838 /// </summary> 839 /// <param name="bindings">Bindings to update</param> 840 private void UpdateBuffers(BuffersPerStage[] bindings) 841 { 842 for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) 843 { 844 ref var buffers = ref bindings[(int)stage - 1]; 845 846 for (int index = 0; index < buffers.Count; index++) 847 { 848 ref var binding = ref buffers.Bindings[index]; 849 850 BufferBounds bounds = buffers.Buffers[binding.Slot]; 851 852 if (bounds.IsUnmapped) 853 { 854 continue; 855 } 856 857 _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Range); 858 } 859 } 860 } 861 862 /// <summary> 863 /// Sets the buffer storage of a buffer texture. This will be bound when the buffer manager commits bindings. 864 /// </summary> 865 /// <param name="stage">Shader stage accessing the texture</param> 866 /// <param name="texture">Buffer texture</param> 867 /// <param name="range">Physical ranges of memory where the buffer texture data is located</param> 868 /// <param name="bindingInfo">Binding info for the buffer texture</param> 869 /// <param name="format">Format of the buffer texture</param> 870 /// <param name="isImage">Whether the binding is for an image or a sampler</param> 871 public void SetBufferTextureStorage( 872 ShaderStage stage, 873 ITexture texture, 874 MultiRange range, 875 TextureBindingInfo bindingInfo, 876 bool isImage) 877 { 878 _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); 879 880 _bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage)); 881 } 882 883 /// <summary> 884 /// Sets the buffer storage of a buffer texture array element. This will be bound when the buffer manager commits bindings. 885 /// </summary> 886 /// <param name="stage">Shader stage accessing the texture</param> 887 /// <param name="array">Texture array where the element will be inserted</param> 888 /// <param name="texture">Buffer texture</param> 889 /// <param name="range">Physical ranges of memory where the buffer texture data is located</param> 890 /// <param name="bindingInfo">Binding info for the buffer texture</param> 891 /// <param name="index">Index of the binding on the array</param> 892 /// <param name="format">Format of the buffer texture</param> 893 public void SetBufferTextureStorage( 894 ShaderStage stage, 895 ITextureArray array, 896 ITexture texture, 897 MultiRange range, 898 TextureBindingInfo bindingInfo, 899 int index) 900 { 901 _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); 902 903 _bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index)); 904 } 905 906 /// <summary> 907 /// Sets the buffer storage of a buffer image array element. This will be bound when the buffer manager commits bindings. 908 /// </summary> 909 /// <param name="stage">Shader stage accessing the texture</param> 910 /// <param name="array">Image array where the element will be inserted</param> 911 /// <param name="texture">Buffer texture</param> 912 /// <param name="range">Physical ranges of memory where the buffer texture data is located</param> 913 /// <param name="bindingInfo">Binding info for the buffer texture</param> 914 /// <param name="index">Index of the binding on the array</param> 915 /// <param name="format">Format of the buffer texture</param> 916 public void SetBufferTextureStorage( 917 ShaderStage stage, 918 IImageArray array, 919 ITexture texture, 920 MultiRange range, 921 TextureBindingInfo bindingInfo, 922 int index) 923 { 924 _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); 925 926 _bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index)); 927 } 928 929 /// <summary> 930 /// Force all bound textures and images to be rebound the next time CommitBindings is called. 931 /// </summary> 932 public void Rebind() 933 { 934 _rebind = true; 935 } 936 } 937 }