BufferManager.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  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 System;
  7. using System.Collections.Generic;
  8. using System.Runtime.CompilerServices;
  9. namespace Ryujinx.Graphics.Gpu.Memory
  10. {
  11. /// <summary>
  12. /// Buffer manager.
  13. /// </summary>
  14. class BufferManager
  15. {
  16. private readonly GpuContext _context;
  17. private readonly GpuChannel _channel;
  18. private int _unalignedStorageBuffers;
  19. public bool HasUnalignedStorageBuffers => _unalignedStorageBuffers > 0;
  20. private IndexBuffer _indexBuffer;
  21. private readonly VertexBuffer[] _vertexBuffers;
  22. private readonly BufferBounds[] _transformFeedbackBuffers;
  23. private readonly List<BufferTextureBinding> _bufferTextures;
  24. private readonly BufferAssignment[] _ranges;
  25. /// <summary>
  26. /// Holds shader stage buffer state and binding information.
  27. /// </summary>
  28. private class BuffersPerStage
  29. {
  30. /// <summary>
  31. /// Shader buffer binding information.
  32. /// </summary>
  33. public BufferDescriptor[] Bindings { get; private set; }
  34. /// <summary>
  35. /// Buffer regions.
  36. /// </summary>
  37. public BufferBounds[] Buffers { get; }
  38. /// <summary>
  39. /// Flag indicating if this binding is unaligned.
  40. /// </summary>
  41. public bool[] Unaligned { get; }
  42. /// <summary>
  43. /// Total amount of buffers used on the shader.
  44. /// </summary>
  45. public int Count { get; private set; }
  46. /// <summary>
  47. /// Creates a new instance of the shader stage buffer information.
  48. /// </summary>
  49. /// <param name="count">Maximum amount of buffers that the shader stage can use</param>
  50. public BuffersPerStage(int count)
  51. {
  52. Bindings = new BufferDescriptor[count];
  53. Buffers = new BufferBounds[count];
  54. Unaligned = new bool[count];
  55. }
  56. /// <summary>
  57. /// Sets the region of a buffer at a given slot.
  58. /// </summary>
  59. /// <param name="index">Buffer slot</param>
  60. /// <param name="address">Region virtual address</param>
  61. /// <param name="size">Region size in bytes</param>
  62. /// <param name="flags">Buffer usage flags</param>
  63. public void SetBounds(int index, ulong address, ulong size, BufferUsageFlags flags = BufferUsageFlags.None)
  64. {
  65. Buffers[index] = new BufferBounds(address, size, flags);
  66. }
  67. /// <summary>
  68. /// Sets shader buffer binding information.
  69. /// </summary>
  70. /// <param name="descriptors">Buffer binding information</param>
  71. public void SetBindings(BufferDescriptor[] descriptors)
  72. {
  73. if (descriptors == null)
  74. {
  75. Count = 0;
  76. return;
  77. }
  78. if ((Count = descriptors.Length) != 0)
  79. {
  80. Bindings = descriptors;
  81. }
  82. }
  83. }
  84. private readonly BuffersPerStage _cpStorageBuffers;
  85. private readonly BuffersPerStage _cpUniformBuffers;
  86. private readonly BuffersPerStage[] _gpStorageBuffers;
  87. private readonly BuffersPerStage[] _gpUniformBuffers;
  88. private bool _gpStorageBuffersDirty;
  89. private bool _gpUniformBuffersDirty;
  90. private bool _indexBufferDirty;
  91. private bool _vertexBuffersDirty;
  92. private uint _vertexBuffersEnableMask;
  93. private bool _transformFeedbackBuffersDirty;
  94. private bool _rebind;
  95. /// <summary>
  96. /// Creates a new instance of the buffer manager.
  97. /// </summary>
  98. /// <param name="context">GPU context that the buffer manager belongs to</param>
  99. /// <param name="channel">GPU channel that the buffer manager belongs to</param>
  100. public BufferManager(GpuContext context, GpuChannel channel)
  101. {
  102. _context = context;
  103. _channel = channel;
  104. _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
  105. _transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers];
  106. _cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
  107. _cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers);
  108. _gpStorageBuffers = new BuffersPerStage[Constants.ShaderStages];
  109. _gpUniformBuffers = new BuffersPerStage[Constants.ShaderStages];
  110. for (int index = 0; index < Constants.ShaderStages; index++)
  111. {
  112. _gpStorageBuffers[index] = new BuffersPerStage(Constants.TotalGpStorageBuffers);
  113. _gpUniformBuffers[index] = new BuffersPerStage(Constants.TotalGpUniformBuffers);
  114. }
  115. _bufferTextures = new List<BufferTextureBinding>();
  116. _ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
  117. }
  118. /// <summary>
  119. /// Sets the memory range with the index buffer data, to be used for subsequent draw calls.
  120. /// </summary>
  121. /// <param name="gpuVa">Start GPU virtual address of the index buffer</param>
  122. /// <param name="size">Size, in bytes, of the index buffer</param>
  123. /// <param name="type">Type of each index buffer element</param>
  124. public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
  125. {
  126. ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
  127. _indexBuffer.Address = address;
  128. _indexBuffer.Size = size;
  129. _indexBuffer.Type = type;
  130. _indexBufferDirty = true;
  131. }
  132. /// <summary>
  133. /// Sets a new index buffer that overrides the one set on the call to <see cref="CommitGraphicsBindings"/>.
  134. /// </summary>
  135. /// <param name="buffer">Buffer to be used as index buffer</param>
  136. /// <param name="type">Type of each index buffer element</param>
  137. public void SetIndexBuffer(BufferRange buffer, IndexType type)
  138. {
  139. _context.Renderer.Pipeline.SetIndexBuffer(buffer, type);
  140. _indexBufferDirty = true;
  141. }
  142. /// <summary>
  143. /// Sets the memory range with vertex buffer data, to be used for subsequent draw calls.
  144. /// </summary>
  145. /// <param name="index">Index of the vertex buffer (up to 16)</param>
  146. /// <param name="gpuVa">GPU virtual address of the buffer</param>
  147. /// <param name="size">Size in bytes of the buffer</param>
  148. /// <param name="stride">Stride of the buffer, defined as the number of bytes of each vertex</param>
  149. /// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
  150. public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
  151. {
  152. ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
  153. _vertexBuffers[index].Address = address;
  154. _vertexBuffers[index].Size = size;
  155. _vertexBuffers[index].Stride = stride;
  156. _vertexBuffers[index].Divisor = divisor;
  157. _vertexBuffersDirty = true;
  158. if (address != 0)
  159. {
  160. _vertexBuffersEnableMask |= 1u << index;
  161. }
  162. else
  163. {
  164. _vertexBuffersEnableMask &= ~(1u << index);
  165. }
  166. }
  167. /// <summary>
  168. /// Sets a transform feedback buffer on the graphics pipeline.
  169. /// The output from the vertex transformation stages are written into the feedback buffer.
  170. /// </summary>
  171. /// <param name="index">Index of the transform feedback buffer</param>
  172. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  173. /// <param name="size">Size in bytes of the transform feedback buffer</param>
  174. public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
  175. {
  176. ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
  177. _transformFeedbackBuffers[index] = new BufferBounds(address, size);
  178. _transformFeedbackBuffersDirty = true;
  179. }
  180. /// <summary>
  181. /// Records the alignment of a storage buffer.
  182. /// Unaligned storage buffers disable some optimizations on the shader.
  183. /// </summary>
  184. /// <param name="buffers">The binding list to modify</param>
  185. /// <param name="index">Index of the storage buffer</param>
  186. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  187. private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa)
  188. {
  189. bool unaligned = (gpuVa & (Constants.StorageAlignment - 1)) != 0;
  190. if (unaligned || HasUnalignedStorageBuffers)
  191. {
  192. // Check if the alignment changed for this binding.
  193. ref bool currentUnaligned = ref buffers.Unaligned[index];
  194. if (currentUnaligned != unaligned)
  195. {
  196. currentUnaligned = unaligned;
  197. _unalignedStorageBuffers += unaligned ? 1 : -1;
  198. }
  199. }
  200. }
  201. /// <summary>
  202. /// Sets a storage buffer on the compute pipeline.
  203. /// Storage buffers can be read and written to on shaders.
  204. /// </summary>
  205. /// <param name="index">Index of the storage buffer</param>
  206. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  207. /// <param name="size">Size in bytes of the storage buffer</param>
  208. /// <param name="flags">Buffer usage flags</param>
  209. public void SetComputeStorageBuffer(int index, ulong gpuVa, ulong size, BufferUsageFlags flags)
  210. {
  211. size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
  212. RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
  213. gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
  214. ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
  215. _cpStorageBuffers.SetBounds(index, address, size, flags);
  216. }
  217. /// <summary>
  218. /// Sets a storage buffer on the graphics pipeline.
  219. /// Storage buffers can be read and written to on shaders.
  220. /// </summary>
  221. /// <param name="stage">Index of the shader stage</param>
  222. /// <param name="index">Index of the storage buffer</param>
  223. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  224. /// <param name="size">Size in bytes of the storage buffer</param>
  225. /// <param name="flags">Buffer usage flags</param>
  226. public void SetGraphicsStorageBuffer(int stage, int index, ulong gpuVa, ulong size, BufferUsageFlags flags)
  227. {
  228. size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
  229. BuffersPerStage buffers = _gpStorageBuffers[stage];
  230. RecordStorageAlignment(buffers, index, gpuVa);
  231. gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
  232. ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
  233. if (buffers.Buffers[index].Address != address ||
  234. buffers.Buffers[index].Size != size)
  235. {
  236. _gpStorageBuffersDirty = true;
  237. }
  238. buffers.SetBounds(index, address, size, flags);
  239. }
  240. /// <summary>
  241. /// Sets a uniform buffer on the compute pipeline.
  242. /// Uniform buffers are read-only from shaders, and have a small capacity.
  243. /// </summary>
  244. /// <param name="index">Index of the uniform buffer</param>
  245. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  246. /// <param name="size">Size in bytes of the storage buffer</param>
  247. public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
  248. {
  249. ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
  250. _cpUniformBuffers.SetBounds(index, address, size);
  251. }
  252. /// <summary>
  253. /// Sets a uniform buffer on the graphics pipeline.
  254. /// Uniform buffers are read-only from shaders, and have a small capacity.
  255. /// </summary>
  256. /// <param name="stage">Index of the shader stage</param>
  257. /// <param name="index">Index of the uniform buffer</param>
  258. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  259. /// <param name="size">Size in bytes of the storage buffer</param>
  260. public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
  261. {
  262. ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
  263. _gpUniformBuffers[stage].SetBounds(index, address, size);
  264. _gpUniformBuffersDirty = true;
  265. }
  266. /// <summary>
  267. /// Sets the binding points for the storage buffers bound on the compute pipeline.
  268. /// </summary>
  269. /// <param name="bindings">Bindings for the active shader</param>
  270. public void SetComputeBufferBindings(CachedShaderBindings bindings)
  271. {
  272. _cpStorageBuffers.SetBindings(bindings.StorageBufferBindings[0]);
  273. _cpUniformBuffers.SetBindings(bindings.ConstantBufferBindings[0]);
  274. }
  275. /// <summary>
  276. /// Sets the binding points for the storage buffers bound on the graphics pipeline.
  277. /// </summary>
  278. /// <param name="bindings">Bindings for the active shader</param>
  279. public void SetGraphicsBufferBindings(CachedShaderBindings bindings)
  280. {
  281. for (int i = 0; i < Constants.ShaderStages; i++)
  282. {
  283. _gpStorageBuffers[i].SetBindings(bindings.StorageBufferBindings[i]);
  284. _gpUniformBuffers[i].SetBindings(bindings.ConstantBufferBindings[i]);
  285. }
  286. _gpStorageBuffersDirty = true;
  287. _gpUniformBuffersDirty = true;
  288. }
  289. /// <summary>
  290. /// Gets a bit mask indicating which compute uniform buffers are currently bound.
  291. /// </summary>
  292. /// <returns>Mask where each bit set indicates a bound constant buffer</returns>
  293. public uint GetComputeUniformBufferUseMask()
  294. {
  295. uint mask = 0;
  296. for (int i = 0; i < _cpUniformBuffers.Buffers.Length; i++)
  297. {
  298. if (_cpUniformBuffers.Buffers[i].Address != 0)
  299. {
  300. mask |= 1u << i;
  301. }
  302. }
  303. return mask;
  304. }
  305. /// <summary>
  306. /// Gets a bit mask indicating which graphics uniform buffers are currently bound.
  307. /// </summary>
  308. /// <param name="stage">Index of the shader stage</param>
  309. /// <returns>Mask where each bit set indicates a bound constant buffer</returns>
  310. public uint GetGraphicsUniformBufferUseMask(int stage)
  311. {
  312. uint mask = 0;
  313. for (int i = 0; i < _gpUniformBuffers[stage].Buffers.Length; i++)
  314. {
  315. if (_gpUniformBuffers[stage].Buffers[i].Address != 0)
  316. {
  317. mask |= 1u << i;
  318. }
  319. }
  320. return mask;
  321. }
  322. /// <summary>
  323. /// Gets the address of the compute uniform buffer currently bound at the given index.
  324. /// </summary>
  325. /// <param name="index">Index of the uniform buffer binding</param>
  326. /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
  327. public ulong GetComputeUniformBufferAddress(int index)
  328. {
  329. return _cpUniformBuffers.Buffers[index].Address;
  330. }
  331. /// <summary>
  332. /// Gets the address of the graphics uniform buffer currently bound at the given index.
  333. /// </summary>
  334. /// <param name="stage">Index of the shader stage</param>
  335. /// <param name="index">Index of the uniform buffer binding</param>
  336. /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
  337. public ulong GetGraphicsUniformBufferAddress(int stage, int index)
  338. {
  339. return _gpUniformBuffers[stage].Buffers[index].Address;
  340. }
  341. /// <summary>
  342. /// Gets the bounds of the uniform buffer currently bound at the given index.
  343. /// </summary>
  344. /// <param name="isCompute">Indicates whenever the uniform is requested by the 3D or compute engine</param>
  345. /// <param name="stage">Index of the shader stage, if the uniform is for the 3D engine</param>
  346. /// <param name="index">Index of the uniform buffer binding</param>
  347. /// <returns>The uniform buffer bounds, or an undefined value if the buffer is not currently bound</returns>
  348. public ref BufferBounds GetUniformBufferBounds(bool isCompute, int stage, int index)
  349. {
  350. if (isCompute)
  351. {
  352. return ref _cpUniformBuffers.Buffers[index];
  353. }
  354. else
  355. {
  356. return ref _gpUniformBuffers[stage].Buffers[index];
  357. }
  358. }
  359. /// <summary>
  360. /// Ensures that the compute engine bindings are visible to the host GPU.
  361. /// Note: this actually performs the binding using the host graphics API.
  362. /// </summary>
  363. public void CommitComputeBindings()
  364. {
  365. var bufferCache = _channel.MemoryManager.Physical.BufferCache;
  366. BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
  367. BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
  368. CommitBufferTextureBindings();
  369. // Force rebind after doing compute work.
  370. Rebind();
  371. }
  372. /// <summary>
  373. /// Commit any queued buffer texture bindings.
  374. /// </summary>
  375. private void CommitBufferTextureBindings()
  376. {
  377. if (_bufferTextures.Count > 0)
  378. {
  379. foreach (var binding in _bufferTextures)
  380. {
  381. var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
  382. var range = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore);
  383. binding.Texture.SetStorage(range);
  384. // The texture must be rebound to use the new storage if it was updated.
  385. if (binding.IsImage)
  386. {
  387. _context.Renderer.Pipeline.SetImage(binding.BindingInfo.Binding, binding.Texture, binding.Format);
  388. }
  389. else
  390. {
  391. _context.Renderer.Pipeline.SetTextureAndSampler(binding.Stage, binding.BindingInfo.Binding, binding.Texture, null);
  392. }
  393. }
  394. _bufferTextures.Clear();
  395. }
  396. }
  397. /// <summary>
  398. /// Ensures that the graphics engine bindings are visible to the host GPU.
  399. /// Note: this actually performs the binding using the host graphics API.
  400. /// </summary>
  401. public void CommitGraphicsBindings()
  402. {
  403. var bufferCache = _channel.MemoryManager.Physical.BufferCache;
  404. if (_indexBufferDirty || _rebind)
  405. {
  406. _indexBufferDirty = false;
  407. if (_indexBuffer.Address != 0)
  408. {
  409. BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
  410. _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
  411. }
  412. }
  413. else if (_indexBuffer.Address != 0)
  414. {
  415. bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
  416. }
  417. uint vbEnableMask = _vertexBuffersEnableMask;
  418. if (_vertexBuffersDirty || _rebind)
  419. {
  420. _vertexBuffersDirty = false;
  421. Span<VertexBufferDescriptor> vertexBuffers = stackalloc VertexBufferDescriptor[Constants.TotalVertexBuffers];
  422. for (int index = 0; (vbEnableMask >> index) != 0; index++)
  423. {
  424. VertexBuffer vb = _vertexBuffers[index];
  425. if (vb.Address == 0)
  426. {
  427. continue;
  428. }
  429. BufferRange buffer = bufferCache.GetBufferRange(vb.Address, vb.Size);
  430. vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
  431. }
  432. _context.Renderer.Pipeline.SetVertexBuffers(vertexBuffers);
  433. }
  434. else
  435. {
  436. for (int index = 0; (vbEnableMask >> index) != 0; index++)
  437. {
  438. VertexBuffer vb = _vertexBuffers[index];
  439. if (vb.Address == 0)
  440. {
  441. continue;
  442. }
  443. bufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
  444. }
  445. }
  446. if (_transformFeedbackBuffersDirty || _rebind)
  447. {
  448. _transformFeedbackBuffersDirty = false;
  449. Span<BufferRange> tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers];
  450. for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
  451. {
  452. BufferBounds tfb = _transformFeedbackBuffers[index];
  453. if (tfb.Address == 0)
  454. {
  455. tfbs[index] = BufferRange.Empty;
  456. continue;
  457. }
  458. tfbs[index] = bufferCache.GetBufferRange(tfb.Address, tfb.Size, write: true);
  459. }
  460. _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
  461. }
  462. else
  463. {
  464. for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
  465. {
  466. BufferBounds tfb = _transformFeedbackBuffers[index];
  467. if (tfb.Address == 0)
  468. {
  469. continue;
  470. }
  471. bufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
  472. }
  473. }
  474. if (_gpStorageBuffersDirty || _rebind)
  475. {
  476. _gpStorageBuffersDirty = false;
  477. BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
  478. }
  479. else
  480. {
  481. UpdateBuffers(_gpStorageBuffers);
  482. }
  483. if (_gpUniformBuffersDirty || _rebind)
  484. {
  485. _gpUniformBuffersDirty = false;
  486. BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
  487. }
  488. else
  489. {
  490. UpdateBuffers(_gpUniformBuffers);
  491. }
  492. CommitBufferTextureBindings();
  493. _rebind = false;
  494. }
  495. /// <summary>
  496. /// Bind respective buffer bindings on the host API.
  497. /// </summary>
  498. /// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
  499. /// <param name="bindings">Buffer memory ranges to bind</param>
  500. /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
  501. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  502. private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
  503. {
  504. int rangesCount = 0;
  505. Span<BufferAssignment> ranges = _ranges;
  506. for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
  507. {
  508. ref var buffers = ref bindings[(int)stage - 1];
  509. for (int index = 0; index < buffers.Count; index++)
  510. {
  511. ref var bindingInfo = ref buffers.Bindings[index];
  512. BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
  513. if (bounds.Address != 0)
  514. {
  515. var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
  516. var range = isStorage
  517. ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
  518. : bufferCache.GetBufferRange(bounds.Address, bounds.Size);
  519. ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
  520. }
  521. }
  522. }
  523. if (rangesCount != 0)
  524. {
  525. SetHostBuffers(ranges, rangesCount, isStorage);
  526. }
  527. }
  528. /// <summary>
  529. /// Bind respective buffer bindings on the host API.
  530. /// </summary>
  531. /// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
  532. /// <param name="buffers">Buffer memory ranges to bind</param>
  533. /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
  534. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  535. private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
  536. {
  537. int rangesCount = 0;
  538. Span<BufferAssignment> ranges = _ranges;
  539. for (int index = 0; index < buffers.Count; index++)
  540. {
  541. ref var bindingInfo = ref buffers.Bindings[index];
  542. BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
  543. if (bounds.Address != 0)
  544. {
  545. var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
  546. var range = isStorage
  547. ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
  548. : bufferCache.GetBufferRange(bounds.Address, bounds.Size);
  549. ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
  550. }
  551. }
  552. if (rangesCount != 0)
  553. {
  554. SetHostBuffers(ranges, rangesCount, isStorage);
  555. }
  556. }
  557. /// <summary>
  558. /// Bind respective buffer bindings on the host API.
  559. /// </summary>
  560. /// <param name="ranges">Host buffers to bind, with their offsets and sizes</param>
  561. /// <param name="first">First binding point</param>
  562. /// <param name="count">Number of bindings</param>
  563. /// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param>
  564. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  565. private void SetHostBuffers(ReadOnlySpan<BufferAssignment> ranges, int count, bool isStorage)
  566. {
  567. if (isStorage)
  568. {
  569. _context.Renderer.Pipeline.SetStorageBuffers(ranges.Slice(0, count));
  570. }
  571. else
  572. {
  573. _context.Renderer.Pipeline.SetUniformBuffers(ranges.Slice(0, count));
  574. }
  575. }
  576. /// <summary>
  577. /// Updates data for the already bound buffer bindings.
  578. /// </summary>
  579. /// <param name="bindings">Bindings to update</param>
  580. private void UpdateBuffers(BuffersPerStage[] bindings)
  581. {
  582. for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
  583. {
  584. ref var buffers = ref bindings[(int)stage - 1];
  585. for (int index = 0; index < buffers.Count; index++)
  586. {
  587. ref var binding = ref buffers.Bindings[index];
  588. BufferBounds bounds = buffers.Buffers[binding.Slot];
  589. if (bounds.Address == 0)
  590. {
  591. continue;
  592. }
  593. _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size);
  594. }
  595. }
  596. }
  597. /// <summary>
  598. /// Sets the buffer storage of a buffer texture. This will be bound when the buffer manager commits bindings.
  599. /// </summary>
  600. /// <param name="stage">Shader stage accessing the texture</param>
  601. /// <param name="texture">Buffer texture</param>
  602. /// <param name="address">Address of the buffer in memory</param>
  603. /// <param name="size">Size of the buffer in bytes</param>
  604. /// <param name="bindingInfo">Binding info for the buffer texture</param>
  605. /// <param name="format">Format of the buffer texture</param>
  606. /// <param name="isImage">Whether the binding is for an image or a sampler</param>
  607. public void SetBufferTextureStorage(
  608. ShaderStage stage,
  609. ITexture texture,
  610. ulong address,
  611. ulong size,
  612. TextureBindingInfo bindingInfo,
  613. Format format,
  614. bool isImage)
  615. {
  616. _channel.MemoryManager.Physical.BufferCache.CreateBuffer(address, size);
  617. _bufferTextures.Add(new BufferTextureBinding(stage, texture, address, size, bindingInfo, format, isImage));
  618. }
  619. /// <summary>
  620. /// Force all bound textures and images to be rebound the next time CommitBindings is called.
  621. /// </summary>
  622. public void Rebind()
  623. {
  624. _rebind = true;
  625. }
  626. }
  627. }