BufferManager.cs 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. using Ryujinx.Common;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Gpu.Image;
  4. using Ryujinx.Graphics.Gpu.State;
  5. using Ryujinx.Graphics.Shader;
  6. using Ryujinx.Memory.Range;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Collections.ObjectModel;
  10. using System.Linq;
  11. namespace Ryujinx.Graphics.Gpu.Memory
  12. {
  13. /// <summary>
  14. /// Buffer manager.
  15. /// </summary>
  16. class BufferManager
  17. {
  18. private const int StackToHeapThreshold = 16;
  19. private const int OverlapsBufferInitialCapacity = 10;
  20. private const int OverlapsBufferMaxCapacity = 10000;
  21. private const ulong BufferAlignmentSize = 0x1000;
  22. private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
  23. private GpuContext _context;
  24. private RangeList<Buffer> _buffers;
  25. private Buffer[] _bufferOverlaps;
  26. private IndexBuffer _indexBuffer;
  27. private VertexBuffer[] _vertexBuffers;
  28. private BufferBounds[] _transformFeedbackBuffers;
  29. private List<BufferTextureBinding> _bufferTextures;
  30. /// <summary>
  31. /// Holds shader stage buffer state and binding information.
  32. /// </summary>
  33. private class BuffersPerStage
  34. {
  35. /// <summary>
  36. /// Shader buffer binding information.
  37. /// </summary>
  38. public BufferDescriptor[] Bindings { get; }
  39. /// <summary>
  40. /// Buffer regions.
  41. /// </summary>
  42. public BufferBounds[] Buffers { get; }
  43. /// <summary>
  44. /// Total amount of buffers used on the shader.
  45. /// </summary>
  46. public int Count { get; private set; }
  47. /// <summary>
  48. /// Creates a new instance of the shader stage buffer information.
  49. /// </summary>
  50. /// <param name="count">Maximum amount of buffers that the shader stage can use</param>
  51. public BuffersPerStage(int count)
  52. {
  53. Bindings = new BufferDescriptor[count];
  54. Buffers = new BufferBounds[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(ReadOnlyCollection<BufferDescriptor> descriptors)
  72. {
  73. if (descriptors == null)
  74. {
  75. Count = 0;
  76. return;
  77. }
  78. descriptors.CopyTo(Bindings, 0);
  79. Count = descriptors.Count;
  80. }
  81. }
  82. private BuffersPerStage _cpStorageBuffers;
  83. private BuffersPerStage _cpUniformBuffers;
  84. private BuffersPerStage[] _gpStorageBuffers;
  85. private BuffersPerStage[] _gpUniformBuffers;
  86. private int _cpStorageBufferBindings;
  87. private int _cpUniformBufferBindings;
  88. private int _gpStorageBufferBindings;
  89. private int _gpUniformBufferBindings;
  90. private bool _gpStorageBuffersDirty;
  91. private bool _gpUniformBuffersDirty;
  92. private bool _indexBufferDirty;
  93. private bool _vertexBuffersDirty;
  94. private uint _vertexBuffersEnableMask;
  95. private bool _transformFeedbackBuffersDirty;
  96. private bool _rebind;
  97. private Dictionary<ulong, BufferCacheEntry> _dirtyCache;
  98. /// <summary>
  99. /// Creates a new instance of the buffer manager.
  100. /// </summary>
  101. /// <param name="context">The GPU context that the buffer manager belongs to</param>
  102. public BufferManager(GpuContext context)
  103. {
  104. _context = context;
  105. _buffers = new RangeList<Buffer>();
  106. _bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
  107. _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
  108. _transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers];
  109. _cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
  110. _cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers);
  111. _gpStorageBuffers = new BuffersPerStage[Constants.ShaderStages];
  112. _gpUniformBuffers = new BuffersPerStage[Constants.ShaderStages];
  113. for (int index = 0; index < Constants.ShaderStages; index++)
  114. {
  115. _gpStorageBuffers[index] = new BuffersPerStage(Constants.TotalGpStorageBuffers);
  116. _gpUniformBuffers[index] = new BuffersPerStage(Constants.TotalGpUniformBuffers);
  117. }
  118. _bufferTextures = new List<BufferTextureBinding>();
  119. _dirtyCache = new Dictionary<ulong, BufferCacheEntry>();
  120. }
  121. /// <summary>
  122. /// Sets the memory range with the index buffer data, to be used for subsequent draw calls.
  123. /// </summary>
  124. /// <param name="gpuVa">Start GPU virtual address of the index buffer</param>
  125. /// <param name="size">Size, in bytes, of the index buffer</param>
  126. /// <param name="type">Type of each index buffer element</param>
  127. public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
  128. {
  129. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  130. _indexBuffer.Address = address;
  131. _indexBuffer.Size = size;
  132. _indexBuffer.Type = type;
  133. _indexBufferDirty = true;
  134. }
  135. /// <summary>
  136. /// Sets a new index buffer that overrides the one set on the call to <see cref="CommitGraphicsBindings"/>.
  137. /// </summary>
  138. /// <param name="buffer">Buffer to be used as index buffer</param>
  139. /// <param name="type">Type of each index buffer element</param>
  140. public void SetIndexBuffer(BufferRange buffer, IndexType type)
  141. {
  142. _context.Renderer.Pipeline.SetIndexBuffer(buffer, type);
  143. _indexBufferDirty = true;
  144. }
  145. /// <summary>
  146. /// Sets the memory range with vertex buffer data, to be used for subsequent draw calls.
  147. /// </summary>
  148. /// <param name="index">Index of the vertex buffer (up to 16)</param>
  149. /// <param name="gpuVa">GPU virtual address of the buffer</param>
  150. /// <param name="size">Size in bytes of the buffer</param>
  151. /// <param name="stride">Stride of the buffer, defined as the number of bytes of each vertex</param>
  152. /// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
  153. public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
  154. {
  155. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  156. _vertexBuffers[index].Address = address;
  157. _vertexBuffers[index].Size = size;
  158. _vertexBuffers[index].Stride = stride;
  159. _vertexBuffers[index].Divisor = divisor;
  160. _vertexBuffersDirty = true;
  161. if (address != 0)
  162. {
  163. _vertexBuffersEnableMask |= 1u << index;
  164. }
  165. else
  166. {
  167. _vertexBuffersEnableMask &= ~(1u << index);
  168. }
  169. }
  170. /// <summary>
  171. /// Sets a transform feedback buffer on the graphics pipeline.
  172. /// The output from the vertex transformation stages are written into the feedback buffer.
  173. /// </summary>
  174. /// <param name="index">Index of the transform feedback buffer</param>
  175. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  176. /// <param name="size">Size in bytes of the transform feedback buffer</param>
  177. public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
  178. {
  179. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  180. _transformFeedbackBuffers[index] = new BufferBounds(address, size);
  181. _transformFeedbackBuffersDirty = true;
  182. }
  183. /// <summary>
  184. /// Sets a storage buffer on the compute pipeline.
  185. /// Storage buffers can be read and written to on shaders.
  186. /// </summary>
  187. /// <param name="index">Index of the storage buffer</param>
  188. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  189. /// <param name="size">Size in bytes of the storage buffer</param>
  190. /// <param name="flags">Buffer usage flags</param>
  191. public void SetComputeStorageBuffer(int index, ulong gpuVa, ulong size, BufferUsageFlags flags)
  192. {
  193. size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
  194. gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
  195. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  196. _cpStorageBuffers.SetBounds(index, address, size, flags);
  197. }
  198. /// <summary>
  199. /// Sets a storage buffer on the graphics pipeline.
  200. /// Storage buffers can be read and written to on shaders.
  201. /// </summary>
  202. /// <param name="stage">Index of the shader stage</param>
  203. /// <param name="index">Index of the storage buffer</param>
  204. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  205. /// <param name="size">Size in bytes of the storage buffer</param>
  206. /// <param name="flags">Buffer usage flags</param>
  207. public void SetGraphicsStorageBuffer(int stage, int index, ulong gpuVa, ulong size, BufferUsageFlags flags)
  208. {
  209. size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
  210. gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
  211. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  212. if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
  213. _gpStorageBuffers[stage].Buffers[index].Size != size)
  214. {
  215. _gpStorageBuffersDirty = true;
  216. }
  217. _gpStorageBuffers[stage].SetBounds(index, address, size, flags);
  218. }
  219. /// <summary>
  220. /// Sets a uniform buffer on the compute pipeline.
  221. /// Uniform buffers are read-only from shaders, and have a small capacity.
  222. /// </summary>
  223. /// <param name="index">Index of the uniform buffer</param>
  224. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  225. /// <param name="size">Size in bytes of the storage buffer</param>
  226. public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
  227. {
  228. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  229. _cpUniformBuffers.SetBounds(index, address, size);
  230. }
  231. /// <summary>
  232. /// Sets a uniform buffer on the graphics pipeline.
  233. /// Uniform buffers are read-only from shaders, and have a small capacity.
  234. /// </summary>
  235. /// <param name="stage">Index of the shader stage</param>
  236. /// <param name="index">Index of the uniform buffer</param>
  237. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  238. /// <param name="size">Size in bytes of the storage buffer</param>
  239. public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
  240. {
  241. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  242. _gpUniformBuffers[stage].SetBounds(index, address, size);
  243. _gpUniformBuffersDirty = true;
  244. }
  245. /// <summary>
  246. /// Sets the binding points for the storage buffers bound on the compute pipeline.
  247. /// </summary>
  248. /// <param name="descriptors">Buffer descriptors with the binding point values</param>
  249. public void SetComputeStorageBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
  250. {
  251. _cpStorageBuffers.SetBindings(descriptors);
  252. _cpStorageBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
  253. }
  254. /// <summary>
  255. /// Sets the binding points for the storage buffers bound on the graphics pipeline.
  256. /// </summary>
  257. /// <param name="stage">Index of the shader stage</param>
  258. /// <param name="descriptors">Buffer descriptors with the binding point values</param>
  259. public void SetGraphicsStorageBufferBindings(int stage, ReadOnlyCollection<BufferDescriptor> descriptors)
  260. {
  261. _gpStorageBuffers[stage].SetBindings(descriptors);
  262. _gpStorageBuffersDirty = true;
  263. }
  264. /// <summary>
  265. /// Sets the total number of storage buffer bindings used.
  266. /// </summary>
  267. /// <param name="count">Number of storage buffer bindings used</param>
  268. public void SetGraphicsStorageBufferBindingsCount(int count)
  269. {
  270. _gpStorageBufferBindings = count;
  271. }
  272. /// <summary>
  273. /// Sets the binding points for the uniform buffers bound on the compute pipeline.
  274. /// </summary>
  275. /// <param name="descriptors">Buffer descriptors with the binding point values</param>
  276. public void SetComputeUniformBufferBindings(ReadOnlyCollection<BufferDescriptor> descriptors)
  277. {
  278. _cpUniformBuffers.SetBindings(descriptors);
  279. _cpUniformBufferBindings = descriptors.Count != 0 ? descriptors.Max(x => x.Binding) + 1 : 0;
  280. }
  281. /// <summary>
  282. /// Sets the enabled uniform buffers mask on the graphics pipeline.
  283. /// Each bit set on the mask indicates that the respective buffer index is enabled.
  284. /// </summary>
  285. /// <param name="stage">Index of the shader stage</param>
  286. /// <param name="descriptors">Buffer descriptors with the binding point values</param>
  287. public void SetGraphicsUniformBufferBindings(int stage, ReadOnlyCollection<BufferDescriptor> descriptors)
  288. {
  289. _gpUniformBuffers[stage].SetBindings(descriptors);
  290. _gpUniformBuffersDirty = true;
  291. }
  292. /// <summary>
  293. /// Sets the total number of uniform buffer bindings used.
  294. /// </summary>
  295. /// <param name="count">Number of uniform buffer bindings used</param>
  296. public void SetGraphicsUniformBufferBindingsCount(int count)
  297. {
  298. _gpUniformBufferBindings = count;
  299. }
  300. /// <summary>
  301. /// Gets a bit mask indicating which compute uniform buffers are currently bound.
  302. /// </summary>
  303. /// <returns>Mask where each bit set indicates a bound constant buffer</returns>
  304. public uint GetComputeUniformBufferUseMask()
  305. {
  306. uint mask = 0;
  307. for (int i = 0; i < _cpUniformBuffers.Buffers.Length; i++)
  308. {
  309. if (_cpUniformBuffers.Buffers[i].Address != 0)
  310. {
  311. mask |= 1u << i;
  312. }
  313. }
  314. return mask;
  315. }
  316. /// <summary>
  317. /// Gets a bit mask indicating which graphics uniform buffers are currently bound.
  318. /// </summary>
  319. /// <param name="stage">Index of the shader stage</param>
  320. /// <returns>Mask where each bit set indicates a bound constant buffer</returns>
  321. public uint GetGraphicsUniformBufferUseMask(int stage)
  322. {
  323. uint mask = 0;
  324. for (int i = 0; i < _gpUniformBuffers[stage].Buffers.Length; i++)
  325. {
  326. if (_gpUniformBuffers[stage].Buffers[i].Address != 0)
  327. {
  328. mask |= 1u << i;
  329. }
  330. }
  331. return mask;
  332. }
  333. /// <summary>
  334. /// Handles removal of buffers written to a memory region being unmapped.
  335. /// </summary>
  336. /// <param name="sender">Sender object</param>
  337. /// <param name="e">Event arguments</param>
  338. public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
  339. {
  340. Buffer[] overlaps = new Buffer[10];
  341. int overlapCount;
  342. ulong address = _context.MemoryManager.Translate(e.Address);
  343. ulong size = e.Size;
  344. lock (_buffers)
  345. {
  346. overlapCount = _buffers.FindOverlaps(address, size, ref overlaps);
  347. }
  348. for (int i = 0; i < overlapCount; i++)
  349. {
  350. overlaps[i].Unmapped(address, size);
  351. }
  352. }
  353. /// <summary>
  354. /// Performs address translation of the GPU virtual address, and creates a
  355. /// new buffer, if needed, for the specified range.
  356. /// </summary>
  357. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  358. /// <param name="size">Size in bytes of the buffer</param>
  359. /// <returns>CPU virtual address of the buffer, after address translation</returns>
  360. private ulong TranslateAndCreateBuffer(ulong gpuVa, ulong size)
  361. {
  362. if (gpuVa == 0)
  363. {
  364. return 0;
  365. }
  366. ulong address = _context.MemoryManager.Translate(gpuVa);
  367. if (address == MemoryManager.PteUnmapped)
  368. {
  369. return 0;
  370. }
  371. CreateBuffer(address, size);
  372. return address;
  373. }
  374. /// <summary>
  375. /// Creates a new buffer for the specified range, if it does not yet exist.
  376. /// This can be used to ensure the existance of a buffer.
  377. /// </summary>
  378. /// <param name="address">Address of the buffer in memory</param>
  379. /// <param name="size">Size of the buffer in bytes</param>
  380. public void CreateBuffer(ulong address, ulong size)
  381. {
  382. ulong endAddress = address + size;
  383. ulong alignedAddress = address & ~BufferAlignmentMask;
  384. ulong alignedEndAddress = (endAddress + BufferAlignmentMask) & ~BufferAlignmentMask;
  385. // The buffer must have the size of at least one page.
  386. if (alignedEndAddress == alignedAddress)
  387. {
  388. alignedEndAddress += BufferAlignmentSize;
  389. }
  390. CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress);
  391. }
  392. /// <summary>
  393. /// Performs address translation of the GPU virtual address, and attempts to force
  394. /// the buffer in the region as dirty.
  395. /// The buffer lookup for this function is cached in a dictionary for quick access, which
  396. /// accelerates common UBO updates.
  397. /// </summary>
  398. /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
  399. /// <param name="size">Size in bytes of the buffer</param>
  400. public void ForceDirty(ulong gpuVa, ulong size)
  401. {
  402. BufferCacheEntry result;
  403. if (!_dirtyCache.TryGetValue(gpuVa, out result) || result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence)
  404. {
  405. ulong address = TranslateAndCreateBuffer(gpuVa, size);
  406. result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
  407. _dirtyCache[gpuVa] = result;
  408. }
  409. result.Buffer.ForceDirty(result.Address, size);
  410. }
  411. /// <summary>
  412. /// Creates a new buffer for the specified range, if needed.
  413. /// If a buffer where this range can be fully contained already exists,
  414. /// then the creation of a new buffer is not necessary.
  415. /// </summary>
  416. /// <param name="address">Address of the buffer in guest memory</param>
  417. /// <param name="size">Size in bytes of the buffer</param>
  418. private void CreateBufferAligned(ulong address, ulong size)
  419. {
  420. int overlapsCount;
  421. lock (_buffers)
  422. {
  423. overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
  424. }
  425. if (overlapsCount != 0)
  426. {
  427. // The buffer already exists. We can just return the existing buffer
  428. // if the buffer we need is fully contained inside the overlapping buffer.
  429. // Otherwise, we must delete the overlapping buffers and create a bigger buffer
  430. // that fits all the data we need. We also need to copy the contents from the
  431. // old buffer(s) to the new buffer.
  432. ulong endAddress = address + size;
  433. if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
  434. {
  435. for (int index = 0; index < overlapsCount; index++)
  436. {
  437. Buffer buffer = _bufferOverlaps[index];
  438. address = Math.Min(address, buffer.Address);
  439. endAddress = Math.Max(endAddress, buffer.EndAddress);
  440. lock (_buffers)
  441. {
  442. _buffers.Remove(buffer);
  443. }
  444. }
  445. Buffer newBuffer = new Buffer(_context, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
  446. lock (_buffers)
  447. {
  448. _buffers.Add(newBuffer);
  449. }
  450. for (int index = 0; index < overlapsCount; index++)
  451. {
  452. Buffer buffer = _bufferOverlaps[index];
  453. int dstOffset = (int)(buffer.Address - newBuffer.Address);
  454. buffer.CopyTo(newBuffer, dstOffset);
  455. newBuffer.InheritModifiedRanges(buffer);
  456. buffer.DisposeData();
  457. }
  458. newBuffer.SynchronizeMemory(address, endAddress - address);
  459. // Existing buffers were modified, we need to rebind everything.
  460. _rebind = true;
  461. }
  462. }
  463. else
  464. {
  465. // No overlap, just create a new buffer.
  466. Buffer buffer = new Buffer(_context, address, size);
  467. lock (_buffers)
  468. {
  469. _buffers.Add(buffer);
  470. }
  471. }
  472. ShrinkOverlapsBufferIfNeeded();
  473. }
  474. /// <summary>
  475. /// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
  476. /// </summary>
  477. private void ShrinkOverlapsBufferIfNeeded()
  478. {
  479. if (_bufferOverlaps.Length > OverlapsBufferMaxCapacity)
  480. {
  481. Array.Resize(ref _bufferOverlaps, OverlapsBufferMaxCapacity);
  482. }
  483. }
  484. /// <summary>
  485. /// Gets the address of the compute uniform buffer currently bound at the given index.
  486. /// </summary>
  487. /// <param name="index">Index of the uniform buffer binding</param>
  488. /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
  489. public ulong GetComputeUniformBufferAddress(int index)
  490. {
  491. return _cpUniformBuffers.Buffers[index].Address;
  492. }
  493. /// <summary>
  494. /// Gets the address of the graphics uniform buffer currently bound at the given index.
  495. /// </summary>
  496. /// <param name="stage">Index of the shader stage</param>
  497. /// <param name="index">Index of the uniform buffer binding</param>
  498. /// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
  499. public ulong GetGraphicsUniformBufferAddress(int stage, int index)
  500. {
  501. return _gpUniformBuffers[stage].Buffers[index].Address;
  502. }
  503. /// <summary>
  504. /// Ensures that the compute engine bindings are visible to the host GPU.
  505. /// Note: this actually performs the binding using the host graphics API.
  506. /// </summary>
  507. public void CommitComputeBindings()
  508. {
  509. int sCount = _cpStorageBufferBindings;
  510. Span<BufferRange> sRanges = sCount < StackToHeapThreshold ? stackalloc BufferRange[sCount] : new BufferRange[sCount];
  511. for (int index = 0; index < _cpStorageBuffers.Count; index++)
  512. {
  513. ref var bindingInfo = ref _cpStorageBuffers.Bindings[index];
  514. BufferBounds bounds = _cpStorageBuffers.Buffers[bindingInfo.Slot];
  515. if (bounds.Address != 0)
  516. {
  517. // The storage buffer size is not reliable (it might be lower than the actual size),
  518. // so we bind the entire buffer to allow otherwise out of range accesses to work.
  519. sRanges[bindingInfo.Binding] = GetBufferRangeTillEnd(
  520. bounds.Address,
  521. bounds.Size,
  522. bounds.Flags.HasFlag(BufferUsageFlags.Write));
  523. }
  524. }
  525. _context.Renderer.Pipeline.SetStorageBuffers(sRanges);
  526. int uCount = _cpUniformBufferBindings;
  527. Span<BufferRange> uRanges = uCount < StackToHeapThreshold ? stackalloc BufferRange[uCount] : new BufferRange[uCount];
  528. for (int index = 0; index < _cpUniformBuffers.Count; index++)
  529. {
  530. ref var bindingInfo = ref _cpUniformBuffers.Bindings[index];
  531. BufferBounds bounds = _cpUniformBuffers.Buffers[bindingInfo.Slot];
  532. if (bounds.Address != 0)
  533. {
  534. uRanges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size);
  535. }
  536. }
  537. _context.Renderer.Pipeline.SetUniformBuffers(uRanges);
  538. CommitBufferTextureBindings();
  539. // Force rebind after doing compute work.
  540. _rebind = true;
  541. }
  542. /// <summary>
  543. /// Commit any queued buffer texture bindings.
  544. /// </summary>
  545. private void CommitBufferTextureBindings()
  546. {
  547. if (_bufferTextures.Count > 0)
  548. {
  549. foreach (var binding in _bufferTextures)
  550. {
  551. binding.Texture.SetStorage(GetBufferRange(binding.Address, binding.Size, binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore)));
  552. // The texture must be rebound to use the new storage if it was updated.
  553. if (binding.IsImage)
  554. {
  555. _context.Renderer.Pipeline.SetImage(binding.BindingInfo.Binding, binding.Texture, binding.Format);
  556. }
  557. else
  558. {
  559. _context.Renderer.Pipeline.SetTexture(binding.BindingInfo.Binding, binding.Texture);
  560. }
  561. }
  562. _bufferTextures.Clear();
  563. }
  564. }
  565. /// <summary>
  566. /// Ensures that the graphics engine bindings are visible to the host GPU.
  567. /// Note: this actually performs the binding using the host graphics API.
  568. /// </summary>
  569. public void CommitGraphicsBindings()
  570. {
  571. if (_indexBufferDirty || _rebind)
  572. {
  573. _indexBufferDirty = false;
  574. if (_indexBuffer.Address != 0)
  575. {
  576. BufferRange buffer = GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
  577. _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
  578. }
  579. }
  580. else if (_indexBuffer.Address != 0)
  581. {
  582. SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
  583. }
  584. uint vbEnableMask = _vertexBuffersEnableMask;
  585. if (_vertexBuffersDirty || _rebind)
  586. {
  587. _vertexBuffersDirty = false;
  588. Span<VertexBufferDescriptor> vertexBuffers = stackalloc VertexBufferDescriptor[Constants.TotalVertexBuffers];
  589. for (int index = 0; (vbEnableMask >> index) != 0; index++)
  590. {
  591. VertexBuffer vb = _vertexBuffers[index];
  592. if (vb.Address == 0)
  593. {
  594. continue;
  595. }
  596. BufferRange buffer = GetBufferRange(vb.Address, vb.Size);
  597. vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
  598. }
  599. _context.Renderer.Pipeline.SetVertexBuffers(vertexBuffers);
  600. }
  601. else
  602. {
  603. for (int index = 0; (vbEnableMask >> index) != 0; index++)
  604. {
  605. VertexBuffer vb = _vertexBuffers[index];
  606. if (vb.Address == 0)
  607. {
  608. continue;
  609. }
  610. SynchronizeBufferRange(vb.Address, vb.Size);
  611. }
  612. }
  613. if (_transformFeedbackBuffersDirty || _rebind)
  614. {
  615. _transformFeedbackBuffersDirty = false;
  616. Span<BufferRange> tfbs = stackalloc BufferRange[Constants.TotalTransformFeedbackBuffers];
  617. for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
  618. {
  619. BufferBounds tfb = _transformFeedbackBuffers[index];
  620. if (tfb.Address == 0)
  621. {
  622. tfbs[index] = BufferRange.Empty;
  623. continue;
  624. }
  625. tfbs[index] = GetBufferRange(tfb.Address, tfb.Size);
  626. }
  627. _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
  628. }
  629. else
  630. {
  631. for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
  632. {
  633. BufferBounds tfb = _transformFeedbackBuffers[index];
  634. if (tfb.Address == 0)
  635. {
  636. continue;
  637. }
  638. SynchronizeBufferRange(tfb.Address, tfb.Size);
  639. }
  640. }
  641. if (_gpStorageBuffersDirty || _rebind)
  642. {
  643. _gpStorageBuffersDirty = false;
  644. BindBuffers(_gpStorageBuffers, isStorage: true);
  645. }
  646. else
  647. {
  648. UpdateBuffers(_gpStorageBuffers);
  649. }
  650. if (_gpUniformBuffersDirty || _rebind)
  651. {
  652. _gpUniformBuffersDirty = false;
  653. BindBuffers(_gpUniformBuffers, isStorage: false);
  654. }
  655. else
  656. {
  657. UpdateBuffers(_gpUniformBuffers);
  658. }
  659. CommitBufferTextureBindings();
  660. _rebind = false;
  661. }
  662. /// <summary>
  663. /// Bind respective buffer bindings on the host API.
  664. /// </summary>
  665. /// <param name="bindings">Bindings to bind</param>
  666. /// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffers</param>
  667. private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
  668. {
  669. int count = isStorage ? _gpStorageBufferBindings : _gpUniformBufferBindings;
  670. Span<BufferRange> ranges = count < StackToHeapThreshold ? stackalloc BufferRange[count] : new BufferRange[count];
  671. for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
  672. {
  673. ref var buffers = ref bindings[(int)stage - 1];
  674. for (int index = 0; index < buffers.Count; index++)
  675. {
  676. ref var bindingInfo = ref buffers.Bindings[index];
  677. BufferBounds bounds = buffers.Buffers[bindingInfo.Slot];
  678. if (bounds.Address != 0)
  679. {
  680. ranges[bindingInfo.Binding] = isStorage
  681. ? GetBufferRangeTillEnd(bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write))
  682. : GetBufferRange(bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write));
  683. }
  684. }
  685. }
  686. if (isStorage)
  687. {
  688. _context.Renderer.Pipeline.SetStorageBuffers(ranges);
  689. }
  690. else
  691. {
  692. _context.Renderer.Pipeline.SetUniformBuffers(ranges);
  693. }
  694. }
  695. /// <summary>
  696. /// Updates data for the already bound buffer bindings.
  697. /// </summary>
  698. /// <param name="bindings">Bindings to update</param>
  699. private void UpdateBuffers(BuffersPerStage[] bindings)
  700. {
  701. for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
  702. {
  703. ref var buffers = ref bindings[(int)stage - 1];
  704. for (int index = 0; index < buffers.Count; index++)
  705. {
  706. ref var binding = ref buffers.Bindings[index];
  707. BufferBounds bounds = buffers.Buffers[binding.Slot];
  708. if (bounds.Address == 0)
  709. {
  710. continue;
  711. }
  712. SynchronizeBufferRange(bounds.Address, bounds.Size);
  713. }
  714. }
  715. }
  716. /// <summary>
  717. /// Sets the buffer storage of a buffer texture. This will be bound when the buffer manager commits bindings.
  718. /// </summary>
  719. /// <param name="texture">Buffer texture</param>
  720. /// <param name="address">Address of the buffer in memory</param>
  721. /// <param name="size">Size of the buffer in bytes</param>
  722. /// <param name="bindingInfo">Binding info for the buffer texture</param>
  723. /// <param name="format">Format of the buffer texture</param>
  724. /// <param name="isImage">Whether the binding is for an image or a sampler</param>
  725. public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, TextureBindingInfo bindingInfo, Format format, bool isImage)
  726. {
  727. CreateBuffer(address, size);
  728. _bufferTextures.Add(new BufferTextureBinding(texture, address, size, bindingInfo, format, isImage));
  729. }
  730. /// <summary>
  731. /// Copy a buffer data from a given address to another.
  732. /// </summary>
  733. /// <remarks>
  734. /// This does a GPU side copy.
  735. /// </remarks>
  736. /// <param name="srcVa">GPU virtual address of the copy source</param>
  737. /// <param name="dstVa">GPU virtual address of the copy destination</param>
  738. /// <param name="size">Size in bytes of the copy</param>
  739. public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size)
  740. {
  741. ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size);
  742. ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size);
  743. Buffer srcBuffer = GetBuffer(srcAddress, size);
  744. Buffer dstBuffer = GetBuffer(dstAddress, size);
  745. int srcOffset = (int)(srcAddress - srcBuffer.Address);
  746. int dstOffset = (int)(dstAddress - dstBuffer.Address);
  747. _context.Renderer.Pipeline.CopyBuffer(
  748. srcBuffer.Handle,
  749. dstBuffer.Handle,
  750. srcOffset,
  751. dstOffset,
  752. (int)size);
  753. if (srcBuffer.IsModified(srcAddress, size))
  754. {
  755. dstBuffer.SignalModified(dstAddress, size);
  756. }
  757. else
  758. {
  759. // Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
  760. dstBuffer.ClearModified(dstAddress, size);
  761. _context.PhysicalMemory.WriteUntracked(dstAddress, _context.PhysicalMemory.GetSpan(srcAddress, (int)size));
  762. }
  763. }
  764. /// <summary>
  765. /// Clears a buffer at a given address with the specified value.
  766. /// </summary>
  767. /// <remarks>
  768. /// Both the address and size must be aligned to 4 bytes.
  769. /// </remarks>
  770. /// <param name="gpuVa">GPU virtual address of the region to clear</param>
  771. /// <param name="size">Number of bytes to clear</param>
  772. /// <param name="value">Value to be written into the buffer</param>
  773. public void ClearBuffer(GpuVa gpuVa, ulong size, uint value)
  774. {
  775. ulong address = TranslateAndCreateBuffer(gpuVa.Pack(), size);
  776. Buffer buffer = GetBuffer(address, size);
  777. int offset = (int)(address - buffer.Address);
  778. _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value);
  779. buffer.SignalModified(address, size);
  780. }
  781. /// <summary>
  782. /// Gets a buffer sub-range starting at a given memory address.
  783. /// </summary>
  784. /// <param name="address">Start address of the memory range</param>
  785. /// <param name="size">Size in bytes of the memory range</param>
  786. /// <param name="write">Whether the buffer will be written to by this use</param>
  787. /// <returns>The buffer sub-range starting at the given memory address</returns>
  788. private BufferRange GetBufferRangeTillEnd(ulong address, ulong size, bool write = false)
  789. {
  790. return GetBuffer(address, size, write).GetRange(address);
  791. }
  792. /// <summary>
  793. /// Gets a buffer sub-range for a given memory range.
  794. /// </summary>
  795. /// <param name="address">Start address of the memory range</param>
  796. /// <param name="size">Size in bytes of the memory range</param>
  797. /// <param name="write">Whether the buffer will be written to by this use</param>
  798. /// <returns>The buffer sub-range for the given range</returns>
  799. private BufferRange GetBufferRange(ulong address, ulong size, bool write = false)
  800. {
  801. return GetBuffer(address, size, write).GetRange(address, size);
  802. }
  803. /// <summary>
  804. /// Gets a buffer for a given memory range.
  805. /// A buffer overlapping with the specified range is assumed to already exist on the cache.
  806. /// </summary>
  807. /// <param name="address">Start address of the memory range</param>
  808. /// <param name="size">Size in bytes of the memory range</param>
  809. /// <param name="write">Whether the buffer will be written to by this use</param>
  810. /// <returns>The buffer where the range is fully contained</returns>
  811. private Buffer GetBuffer(ulong address, ulong size, bool write = false)
  812. {
  813. Buffer buffer;
  814. if (size != 0)
  815. {
  816. lock (_buffers)
  817. {
  818. buffer = _buffers.FindFirstOverlap(address, size);
  819. }
  820. buffer.SynchronizeMemory(address, size);
  821. if (write)
  822. {
  823. buffer.SignalModified(address, size);
  824. }
  825. }
  826. else
  827. {
  828. lock (_buffers)
  829. {
  830. buffer = _buffers.FindFirstOverlap(address, 1);
  831. }
  832. }
  833. return buffer;
  834. }
  835. /// <summary>
  836. /// Performs guest to host memory synchronization of a given memory range.
  837. /// </summary>
  838. /// <param name="address">Start address of the memory range</param>
  839. /// <param name="size">Size in bytes of the memory range</param>
  840. private void SynchronizeBufferRange(ulong address, ulong size)
  841. {
  842. if (size != 0)
  843. {
  844. Buffer buffer;
  845. lock (_buffers)
  846. {
  847. buffer = _buffers.FindFirstOverlap(address, size);
  848. }
  849. buffer.SynchronizeMemory(address, size);
  850. }
  851. }
  852. /// <summary>
  853. /// Disposes all buffers in the cache.
  854. /// It's an error to use the buffer manager after disposal.
  855. /// </summary>
  856. public void Dispose()
  857. {
  858. lock (_buffers)
  859. {
  860. foreach (Buffer buffer in _buffers)
  861. {
  862. buffer.Dispose();
  863. }
  864. }
  865. }
  866. }
  867. }