/ src / Ryujinx.Graphics.Gpu / Memory / BufferManager.cs
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  }