BufferManager.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  1. using Ryujinx.Common;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Gpu.State;
  4. using Ryujinx.Graphics.Shader;
  5. using Ryujinx.Memory.Range;
  6. using System;
  7. namespace Ryujinx.Graphics.Gpu.Memory
  8. {
  9. /// <summary>
  10. /// Buffer manager.
  11. /// </summary>
  12. class BufferManager
  13. {
  14. private const int OverlapsBufferInitialCapacity = 10;
  15. private const int OverlapsBufferMaxCapacity = 10000;
  16. private const ulong BufferAlignmentSize = 0x1000;
  17. private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
  18. private GpuContext _context;
  19. private RangeList<Buffer> _buffers;
  20. private Buffer[] _bufferOverlaps;
  21. private IndexBuffer _indexBuffer;
  22. private VertexBuffer[] _vertexBuffers;
  23. private BufferBounds[] _transformFeedbackBuffers;
  24. private class BuffersPerStage
  25. {
  26. public uint EnableMask { get; set; }
  27. public BufferBounds[] Buffers { get; }
  28. public BuffersPerStage(int count)
  29. {
  30. Buffers = new BufferBounds[count];
  31. }
  32. public void Bind(int index, ulong address, ulong size)
  33. {
  34. Buffers[index].Address = address;
  35. Buffers[index].Size = size;
  36. }
  37. }
  38. private BuffersPerStage _cpStorageBuffers;
  39. private BuffersPerStage _cpUniformBuffers;
  40. private BuffersPerStage[] _gpStorageBuffers;
  41. private BuffersPerStage[] _gpUniformBuffers;
  42. private bool _gpStorageBuffersDirty;
  43. private bool _gpUniformBuffersDirty;
  44. private bool _indexBufferDirty;
  45. private bool _vertexBuffersDirty;
  46. private uint _vertexBuffersEnableMask;
  47. private bool _transformFeedbackBuffersDirty;
  48. private bool _rebind;
  49. /// <summary>
  50. /// Creates a new instance of the buffer manager.
  51. /// </summary>
  52. /// <param name="context">The GPU context that the buffer manager belongs to</param>
  53. public BufferManager(GpuContext context)
  54. {
  55. _context = context;
  56. _buffers = new RangeList<Buffer>();
  57. _bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
  58. _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
  59. _transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers];
  60. _cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
  61. _cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers);
  62. _gpStorageBuffers = new BuffersPerStage[Constants.ShaderStages];
  63. _gpUniformBuffers = new BuffersPerStage[Constants.ShaderStages];
  64. for (int index = 0; index < Constants.ShaderStages; index++)
  65. {
  66. _gpStorageBuffers[index] = new BuffersPerStage(Constants.TotalGpStorageBuffers);
  67. _gpUniformBuffers[index] = new BuffersPerStage(Constants.TotalGpUniformBuffers);
  68. }
  69. }
  70. /// <summary>
  71. /// Sets the memory range with the index buffer data, to be used for subsequent draw calls.
  72. /// </summary>
  73. /// <param name="gpuVa">Start GPU virtual address of the index buffer</param>
  74. /// <param name="size">Size, in bytes, of the index buffer</param>
  75. /// <param name="type">Type of each index buffer element</param>
  76. public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
  77. {
  78. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  79. _indexBuffer.Address = address;
  80. _indexBuffer.Size = size;
  81. _indexBuffer.Type = type;
  82. _indexBufferDirty = true;
  83. }
  84. /// <summary>
  85. /// Sets a new index buffer that overrides the one set on the call to <see cref="CommitGraphicsBindings"/>.
  86. /// </summary>
  87. /// <param name="buffer">Buffer to be used as index buffer</param>
  88. /// <param name="type">Type of each index buffer element</param>
  89. public void SetIndexBuffer(BufferRange buffer, IndexType type)
  90. {
  91. _context.Renderer.Pipeline.SetIndexBuffer(buffer, type);
  92. _indexBufferDirty = true;
  93. }
  94. /// <summary>
  95. /// Sets the memory range with vertex buffer data, to be used for subsequent draw calls.
  96. /// </summary>
  97. /// <param name="index">Index of the vertex buffer (up to 16)</param>
  98. /// <param name="gpuVa">GPU virtual address of the buffer</param>
  99. /// <param name="size">Size in bytes of the buffer</param>
  100. /// <param name="stride">Stride of the buffer, defined as the number of bytes of each vertex</param>
  101. /// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
  102. public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
  103. {
  104. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  105. _vertexBuffers[index].Address = address;
  106. _vertexBuffers[index].Size = size;
  107. _vertexBuffers[index].Stride = stride;
  108. _vertexBuffers[index].Divisor = divisor;
  109. _vertexBuffersDirty = true;
  110. if (address != 0)
  111. {
  112. _vertexBuffersEnableMask |= 1u << index;
  113. }
  114. else
  115. {
  116. _vertexBuffersEnableMask &= ~(1u << index);
  117. }
  118. }
  119. /// <summary>
  120. /// Sets a transform feedback buffer on the graphics pipeline.
  121. /// The output from the vertex transformation stages are written into the feedback buffer.
  122. /// </summary>
  123. /// <param name="index">Index of the transform feedback buffer</param>
  124. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  125. /// <param name="size">Size in bytes of the transform feedback buffer</param>
  126. public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
  127. {
  128. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  129. _transformFeedbackBuffers[index].Address = address;
  130. _transformFeedbackBuffers[index].Size = size;
  131. _transformFeedbackBuffersDirty = true;
  132. }
  133. /// <summary>
  134. /// Sets a storage buffer on the compute pipeline.
  135. /// Storage buffers can be read and written to on shaders.
  136. /// </summary>
  137. /// <param name="index">Index of the storage buffer</param>
  138. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  139. /// <param name="size">Size in bytes of the storage buffer</param>
  140. public void SetComputeStorageBuffer(int index, ulong gpuVa, ulong size)
  141. {
  142. size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
  143. gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
  144. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  145. _cpStorageBuffers.Bind(index, address, size);
  146. }
  147. /// <summary>
  148. /// Sets a storage buffer on the graphics pipeline.
  149. /// Storage buffers can be read and written to on shaders.
  150. /// </summary>
  151. /// <param name="stage">Index of the shader stage</param>
  152. /// <param name="index">Index of the storage buffer</param>
  153. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  154. /// <param name="size">Size in bytes of the storage buffer</param>
  155. public void SetGraphicsStorageBuffer(int stage, int index, ulong gpuVa, ulong size)
  156. {
  157. size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
  158. gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
  159. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  160. if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
  161. _gpStorageBuffers[stage].Buffers[index].Size != size)
  162. {
  163. _gpStorageBuffersDirty = true;
  164. }
  165. _gpStorageBuffers[stage].Bind(index, address, size);
  166. }
  167. /// <summary>
  168. /// Sets a uniform buffer on the compute pipeline.
  169. /// Uniform buffers are read-only from shaders, and have a small capacity.
  170. /// </summary>
  171. /// <param name="index">Index of the uniform buffer</param>
  172. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  173. /// <param name="size">Size in bytes of the storage buffer</param>
  174. public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
  175. {
  176. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  177. _cpUniformBuffers.Bind(index, address, size);
  178. }
  179. /// <summary>
  180. /// Sets a uniform buffer on the graphics pipeline.
  181. /// Uniform buffers are read-only from shaders, and have a small capacity.
  182. /// </summary>
  183. /// <param name="stage">Index of the shader stage</param>
  184. /// <param name="index">Index of the uniform buffer</param>
  185. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  186. /// <param name="size">Size in bytes of the storage buffer</param>
  187. public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
  188. {
  189. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  190. _gpUniformBuffers[stage].Bind(index, address, size);
  191. _gpUniformBuffersDirty = true;
  192. }
  193. /// <summary>
  194. /// Sets the enabled storage buffers mask on the compute pipeline.
  195. /// Each bit set on the mask indicates that the respective buffer index is enabled.
  196. /// </summary>
  197. /// <param name="mask">Buffer enable mask</param>
  198. public void SetComputeStorageBufferEnableMask(uint mask)
  199. {
  200. _cpStorageBuffers.EnableMask = mask;
  201. }
  202. /// <summary>
  203. /// Sets the enabled storage buffers mask on the graphics pipeline.
  204. /// Each bit set on the mask indicates that the respective buffer index is enabled.
  205. /// </summary>
  206. /// <param name="stage">Index of the shader stage</param>
  207. /// <param name="mask">Buffer enable mask</param>
  208. public void SetGraphicsStorageBufferEnableMask(int stage, uint mask)
  209. {
  210. _gpStorageBuffers[stage].EnableMask = mask;
  211. _gpStorageBuffersDirty = true;
  212. }
  213. /// <summary>
  214. /// Sets the enabled uniform buffers mask on the compute pipeline.
  215. /// Each bit set on the mask indicates that the respective buffer index is enabled.
  216. /// </summary>
  217. /// <param name="mask">Buffer enable mask</param>
  218. public void SetComputeUniformBufferEnableMask(uint mask)
  219. {
  220. _cpUniformBuffers.EnableMask = mask;
  221. }
  222. /// <summary>
  223. /// Gets a bit mask indicating which compute uniform buffers are currently bound.
  224. /// </summary>
  225. /// <returns>Mask where each bit set indicates a bound constant buffer</returns>
  226. public uint GetComputeUniformBufferUseMask()
  227. {
  228. uint mask = 0;
  229. for (int i = 0; i < _cpUniformBuffers.Buffers.Length; i++)
  230. {
  231. if (_cpUniformBuffers.Buffers[i].Address != 0)
  232. {
  233. mask |= 1u << i;
  234. }
  235. }
  236. return mask;
  237. }
  238. /// <summary>
  239. /// Sets the enabled uniform buffers mask on the graphics pipeline.
  240. /// Each bit set on the mask indicates that the respective buffer index is enabled.
  241. /// </summary>
  242. /// <param name="stage">Index of the shader stage</param>
  243. /// <param name="mask">Buffer enable mask</param>
  244. public void SetGraphicsUniformBufferEnableMask(int stage, uint mask)
  245. {
  246. _gpUniformBuffers[stage].EnableMask = mask;
  247. _gpUniformBuffersDirty = true;
  248. }
  249. /// <summary>
  250. /// Gets a bit mask indicating which graphics uniform buffers are currently bound.
  251. /// </summary>
  252. /// <param name="stage">Index of the shader stage</param>
  253. /// <returns>Mask where each bit set indicates a bound constant buffer</returns>
  254. public uint GetGraphicsUniformBufferUseMask(int stage)
  255. {
  256. uint mask = 0;
  257. for (int i = 0; i < _gpUniformBuffers[stage].Buffers.Length; i++)
  258. {
  259. if (_gpUniformBuffers[stage].Buffers[i].Address != 0)
  260. {
  261. mask |= 1u << i;
  262. }
  263. }
  264. return mask;
  265. }
  266. /// <summary>
  267. /// Performs address translation of the GPU virtual address, and creates a
  268. /// new buffer, if needed, for the specified range.
  269. /// </summary>
  270. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  271. /// <param name="size">Size in bytes of the buffer</param>
  272. /// <returns>CPU virtual address of the buffer, after address translation</returns>
  273. private ulong TranslateAndCreateBuffer(ulong gpuVa, ulong size)
  274. {
  275. if (gpuVa == 0)
  276. {
  277. return 0;
  278. }
  279. ulong address = _context.MemoryManager.Translate(gpuVa);
  280. if (address == MemoryManager.BadAddress)
  281. {
  282. return 0;
  283. }
  284. CreateBuffer(address, size);
  285. return address;
  286. }
  287. /// <summary>
  288. /// Creates a new buffer for the specified range, if it does not yet exist.
  289. /// This can be used to ensure the existance of a buffer.
  290. /// </summary>
  291. /// <param name="address">Address of the buffer in memory</param>
  292. /// <param name="size">Size of the buffer in bytes</param>
  293. public void CreateBuffer(ulong address, ulong size)
  294. {
  295. ulong endAddress = address + size;
  296. ulong alignedAddress = address & ~BufferAlignmentMask;
  297. ulong alignedEndAddress = (endAddress + BufferAlignmentMask) & ~BufferAlignmentMask;
  298. // The buffer must have the size of at least one page.
  299. if (alignedEndAddress == alignedAddress)
  300. {
  301. alignedEndAddress += BufferAlignmentSize;
  302. }
  303. CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress);
  304. }
  305. /// <summary>
  306. /// Creates a new buffer for the specified range, if needed.
  307. /// If a buffer where this range can be fully contained already exists,
  308. /// then the creation of a new buffer is not necessary.
  309. /// </summary>
  310. /// <param name="address">Address of the buffer in guest memory</param>
  311. /// <param name="size">Size in bytes of the buffer</param>
  312. private void CreateBufferAligned(ulong address, ulong size)
  313. {
  314. int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
  315. if (overlapsCount != 0)
  316. {
  317. // The buffer already exists. We can just return the existing buffer
  318. // if the buffer we need is fully contained inside the overlapping buffer.
  319. // Otherwise, we must delete the overlapping buffers and create a bigger buffer
  320. // that fits all the data we need. We also need to copy the contents from the
  321. // old buffer(s) to the new buffer.
  322. ulong endAddress = address + size;
  323. if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
  324. {
  325. for (int index = 0; index < overlapsCount; index++)
  326. {
  327. Buffer buffer = _bufferOverlaps[index];
  328. address = Math.Min(address, buffer.Address);
  329. endAddress = Math.Max(endAddress, buffer.EndAddress);
  330. buffer.SynchronizeMemory(buffer.Address, buffer.Size);
  331. _buffers.Remove(buffer);
  332. }
  333. Buffer newBuffer = new Buffer(_context, address, endAddress - address);
  334. newBuffer.SynchronizeMemory(address, endAddress - address);
  335. _buffers.Add(newBuffer);
  336. for (int index = 0; index < overlapsCount; index++)
  337. {
  338. Buffer buffer = _bufferOverlaps[index];
  339. int dstOffset = (int)(buffer.Address - newBuffer.Address);
  340. buffer.CopyTo(newBuffer, dstOffset);
  341. buffer.Dispose();
  342. }
  343. // Existing buffers were modified, we need to rebind everything.
  344. _rebind = true;
  345. }
  346. }
  347. else
  348. {
  349. // No overlap, just create a new buffer.
  350. Buffer buffer = new Buffer(_context, address, size);
  351. _buffers.Add(buffer);
  352. }
  353. ShrinkOverlapsBufferIfNeeded();
  354. }
  355. /// <summary>
  356. /// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
  357. /// </summary>
  358. private void ShrinkOverlapsBufferIfNeeded()
  359. {
  360. if (_bufferOverlaps.Length > OverlapsBufferMaxCapacity)
  361. {
  362. Array.Resize(ref _bufferOverlaps, OverlapsBufferMaxCapacity);
  363. }
  364. }
  365. /// <summary>
  366. /// Gets the address of the compute uniform buffer currently bound at the given index.
  367. /// </summary>
  368. /// <param name="index">Index of the uniform buffer binding</param>
  369. /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
  370. public ulong GetComputeUniformBufferAddress(int index)
  371. {
  372. return _cpUniformBuffers.Buffers[index].Address;
  373. }
  374. /// <summary>
  375. /// Gets the address of the graphics uniform buffer currently bound at the given index.
  376. /// </summary>
  377. /// <param name="stage">Index of the shader stage</param>
  378. /// <param name="index">Index of the uniform buffer binding</param>
  379. /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
  380. public ulong GetGraphicsUniformBufferAddress(int stage, int index)
  381. {
  382. return _gpUniformBuffers[stage].Buffers[index].Address;
  383. }
  384. /// <summary>
  385. /// Ensures that the compute engine bindings are visible to the host GPU.
  386. /// Note: this actually performs the binding using the host graphics API.
  387. /// </summary>
  388. public void CommitComputeBindings()
  389. {
  390. uint enableMask = _cpStorageBuffers.EnableMask;
  391. for (int index = 0; (enableMask >> index) != 0; index++)
  392. {
  393. if ((enableMask & (1u << index)) == 0)
  394. {
  395. continue;
  396. }
  397. BufferBounds bounds = _cpStorageBuffers.Buffers[index];
  398. if (bounds.Address == 0)
  399. {
  400. continue;
  401. }
  402. BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
  403. _context.Renderer.Pipeline.SetStorageBuffer(index, ShaderStage.Compute, buffer);
  404. }
  405. enableMask = _cpUniformBuffers.EnableMask;
  406. for (int index = 0; (enableMask >> index) != 0; index++)
  407. {
  408. if ((enableMask & (1u << index)) == 0)
  409. {
  410. continue;
  411. }
  412. BufferBounds bounds = _cpUniformBuffers.Buffers[index];
  413. if (bounds.Address == 0)
  414. {
  415. continue;
  416. }
  417. BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
  418. _context.Renderer.Pipeline.SetUniformBuffer(index, ShaderStage.Compute, buffer);
  419. }
  420. // Force rebind after doing compute work.
  421. _rebind = true;
  422. }
  423. /// <summary>
  424. /// Ensures that the graphics engine bindings are visible to the host GPU.
  425. /// Note: this actually performs the binding using the host graphics API.
  426. /// </summary>
  427. public void CommitGraphicsBindings()
  428. {
  429. if (_indexBufferDirty || _rebind)
  430. {
  431. _indexBufferDirty = false;
  432. if (_indexBuffer.Address != 0)
  433. {
  434. BufferRange buffer = GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
  435. _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
  436. }
  437. }
  438. else if (_indexBuffer.Address != 0)
  439. {
  440. SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
  441. }
  442. uint vbEnableMask = _vertexBuffersEnableMask;
  443. if (_vertexBuffersDirty || _rebind)
  444. {
  445. _vertexBuffersDirty = false;
  446. Span<VertexBufferDescriptor> vertexBuffers = stackalloc VertexBufferDescriptor[Constants.TotalVertexBuffers];
  447. for (int index = 0; (vbEnableMask >> index) != 0; index++)
  448. {
  449. VertexBuffer vb = _vertexBuffers[index];
  450. if (vb.Address == 0)
  451. {
  452. continue;
  453. }
  454. BufferRange buffer = GetBufferRange(vb.Address, vb.Size);
  455. vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
  456. }
  457. _context.Renderer.Pipeline.SetVertexBuffers(vertexBuffers);
  458. }
  459. else
  460. {
  461. for (int index = 0; (vbEnableMask >> index) != 0; index++)
  462. {
  463. VertexBuffer vb = _vertexBuffers[index];
  464. if (vb.Address == 0)
  465. {
  466. continue;
  467. }
  468. SynchronizeBufferRange(vb.Address, vb.Size);
  469. }
  470. }
  471. if (_transformFeedbackBuffersDirty || _rebind)
  472. {
  473. _transformFeedbackBuffersDirty = false;
  474. Span<BufferRange> tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers];
  475. for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
  476. {
  477. BufferBounds tfb = _transformFeedbackBuffers[index];
  478. if (tfb.Address == 0)
  479. {
  480. tfbs[index] = BufferRange.Empty;
  481. continue;
  482. }
  483. tfbs[index] = GetBufferRange(tfb.Address, tfb.Size);
  484. }
  485. _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
  486. }
  487. else
  488. {
  489. for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
  490. {
  491. BufferBounds tfb = _transformFeedbackBuffers[index];
  492. if (tfb.Address == 0)
  493. {
  494. continue;
  495. }
  496. SynchronizeBufferRange(tfb.Address, tfb.Size);
  497. }
  498. }
  499. if (_gpStorageBuffersDirty || _rebind)
  500. {
  501. _gpStorageBuffersDirty = false;
  502. BindBuffers(_gpStorageBuffers, isStorage: true);
  503. }
  504. else
  505. {
  506. UpdateBuffers(_gpStorageBuffers);
  507. }
  508. if (_gpUniformBuffersDirty || _rebind)
  509. {
  510. _gpUniformBuffersDirty = false;
  511. BindBuffers(_gpUniformBuffers, isStorage: false);
  512. }
  513. else
  514. {
  515. UpdateBuffers(_gpUniformBuffers);
  516. }
  517. _rebind = false;
  518. }
  519. /// <summary>
  520. /// Bind respective buffer bindings on the host API.
  521. /// </summary>
  522. /// <param name="bindings">Bindings to bind</param>
  523. /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffers</param>
  524. private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
  525. {
  526. BindOrUpdateBuffers(bindings, bind: true, isStorage);
  527. }
  528. /// <summary>
  529. /// Updates data for the already bound buffer bindings.
  530. /// </summary>
  531. /// <param name="bindings">Bindings to update</param>
  532. private void UpdateBuffers(BuffersPerStage[] bindings)
  533. {
  534. BindOrUpdateBuffers(bindings, bind: false);
  535. }
  536. /// <summary>
  537. /// This binds buffers into the host API, or updates data for already bound buffers.
  538. /// </summary>
  539. /// <param name="bindings">Bindings to bind or update</param>
  540. /// <param name="bind">True to bind, false to update</param>
  541. /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
  542. private void BindOrUpdateBuffers(BuffersPerStage[] bindings, bool bind, bool isStorage = false)
  543. {
  544. for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
  545. {
  546. uint enableMask = bindings[(int)stage - 1].EnableMask;
  547. if (enableMask == 0)
  548. {
  549. continue;
  550. }
  551. for (int index = 0; (enableMask >> index) != 0; index++)
  552. {
  553. if ((enableMask & (1u << index)) == 0)
  554. {
  555. continue;
  556. }
  557. BufferBounds bounds = bindings[(int)stage - 1].Buffers[index];
  558. if (bounds.Address == 0)
  559. {
  560. continue;
  561. }
  562. if (bind)
  563. {
  564. BindBuffer(index, stage, bounds, isStorage);
  565. }
  566. else
  567. {
  568. SynchronizeBufferRange(bounds.Address, bounds.Size);
  569. }
  570. }
  571. }
  572. }
  573. /// <summary>
  574. /// Binds a buffer on the host API.
  575. /// </summary>
  576. /// <param name="index">Index to bind the buffer into</param>
  577. /// <param name="stage">Shader stage to bind the buffer into</param>
  578. /// <param name="bounds">Buffer address and size</param>
  579. /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
  580. private void BindBuffer(int index, ShaderStage stage, BufferBounds bounds, bool isStorage)
  581. {
  582. BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
  583. if (isStorage)
  584. {
  585. _context.Renderer.Pipeline.SetStorageBuffer(index, stage, buffer);
  586. }
  587. else
  588. {
  589. _context.Renderer.Pipeline.SetUniformBuffer(index, stage, buffer);
  590. }
  591. }
  592. /// <summary>
  593. /// Sets the buffer storage of a buffer texture.
  594. /// </summary>
  595. /// <param name="texture">Buffer texture</param>
  596. /// <param name="address">Address of the buffer in memory</param>
  597. /// <param name="size">Size of the buffer in bytes</param>
  598. /// <param name="compute">Indicates if the buffer texture belongs to the compute or graphics pipeline</param>
  599. public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, bool compute)
  600. {
  601. CreateBuffer(address, size);
  602. if (_rebind)
  603. {
  604. // We probably had to modify existing buffers to create the texture buffer,
  605. // so rebind everything to ensure we're using the new buffers for all bound resources.
  606. if (compute)
  607. {
  608. CommitComputeBindings();
  609. }
  610. else
  611. {
  612. CommitGraphicsBindings();
  613. }
  614. }
  615. texture.SetStorage(GetBufferRange(address, size));
  616. }
  617. /// <summary>
  618. /// Copy a buffer data from a given address to another.
  619. /// </summary>
  620. /// <remarks>
  621. /// This does a GPU side copy.
  622. /// </remarks>
  623. /// <param name="srcVa">GPU virtual address of the copy source</param>
  624. /// <param name="dstVa">GPU virtual address of the copy destination</param>
  625. /// <param name="size">Size in bytes of the copy</param>
  626. public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size)
  627. {
  628. ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size);
  629. ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size);
  630. Buffer srcBuffer = GetBuffer(srcAddress, size);
  631. Buffer dstBuffer = GetBuffer(dstAddress, size);
  632. int srcOffset = (int)(srcAddress - srcBuffer.Address);
  633. int dstOffset = (int)(dstAddress - dstBuffer.Address);
  634. _context.Renderer.Pipeline.CopyBuffer(
  635. srcBuffer.Handle,
  636. dstBuffer.Handle,
  637. srcOffset,
  638. dstOffset,
  639. (int)size);
  640. dstBuffer.Flush(dstAddress, size);
  641. }
  642. /// <summary>
  643. /// Gets a buffer sub-range for a given memory range.
  644. /// </summary>
  645. /// <param name="address">Start address of the memory range</param>
  646. /// <param name="size">Size in bytes of the memory range</param>
  647. /// <returns>The buffer sub-range for the given range</returns>
  648. private BufferRange GetBufferRange(ulong address, ulong size)
  649. {
  650. return GetBuffer(address, size).GetRange(address, size);
  651. }
  652. /// <summary>
  653. /// Gets a buffer for a given memory range.
  654. /// A buffer overlapping with the specified range is assumed to already exist on the cache.
  655. /// </summary>
  656. /// <param name="address">Start address of the memory range</param>
  657. /// <param name="size">Size in bytes of the memory range</param>
  658. /// <returns>The buffer where the range is fully contained</returns>
  659. private Buffer GetBuffer(ulong address, ulong size)
  660. {
  661. Buffer buffer;
  662. if (size != 0)
  663. {
  664. buffer = _buffers.FindFirstOverlap(address, size);
  665. buffer.SynchronizeMemory(address, size);
  666. }
  667. else
  668. {
  669. buffer = _buffers.FindFirstOverlap(address, 1);
  670. }
  671. return buffer;
  672. }
  673. /// <summary>
  674. /// Performs guest to host memory synchronization of a given memory range.
  675. /// </summary>
  676. /// <param name="address">Start address of the memory range</param>
  677. /// <param name="size">Size in bytes of the memory range</param>
  678. private void SynchronizeBufferRange(ulong address, ulong size)
  679. {
  680. if (size != 0)
  681. {
  682. Buffer buffer = _buffers.FindFirstOverlap(address, size);
  683. buffer.SynchronizeMemory(address, size);
  684. }
  685. }
  686. /// <summary>
  687. /// Disposes all buffers in the cache.
  688. /// It's an error to use the buffer manager after disposal.
  689. /// </summary>
  690. public void Dispose()
  691. {
  692. foreach (Buffer buffer in _buffers)
  693. {
  694. buffer.Dispose();
  695. }
  696. }
  697. }
  698. }