BufferManager.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. using Ryujinx.Graphics.GAL;
  2. using Silk.NET.Vulkan;
  3. using System;
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. using VkFormat = Silk.NET.Vulkan.Format;
  7. using VkBuffer = Silk.NET.Vulkan.Buffer;
  8. namespace Ryujinx.Graphics.Vulkan
  9. {
  10. class BufferManager : IDisposable
  11. {
  12. public const MemoryPropertyFlags DefaultBufferMemoryFlags =
  13. MemoryPropertyFlags.HostVisibleBit |
  14. MemoryPropertyFlags.HostCoherentBit |
  15. MemoryPropertyFlags.HostCachedBit;
  16. // Some drivers don't expose a "HostCached" memory type,
  17. // so we need those alternative flags for the allocation to succeed there.
  18. private const MemoryPropertyFlags DefaultBufferMemoryNoCacheFlags =
  19. MemoryPropertyFlags.HostVisibleBit |
  20. MemoryPropertyFlags.HostCoherentBit;
  21. private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
  22. MemoryPropertyFlags.DeviceLocalBit;
  23. private const MemoryPropertyFlags DeviceLocalMappedBufferMemoryFlags =
  24. MemoryPropertyFlags.DeviceLocalBit |
  25. MemoryPropertyFlags.HostVisibleBit |
  26. MemoryPropertyFlags.HostCoherentBit;
  27. private const BufferUsageFlags DefaultBufferUsageFlags =
  28. BufferUsageFlags.TransferSrcBit |
  29. BufferUsageFlags.TransferDstBit |
  30. BufferUsageFlags.UniformTexelBufferBit |
  31. BufferUsageFlags.StorageTexelBufferBit |
  32. BufferUsageFlags.UniformBufferBit |
  33. BufferUsageFlags.StorageBufferBit |
  34. BufferUsageFlags.IndexBufferBit |
  35. BufferUsageFlags.VertexBufferBit |
  36. BufferUsageFlags.TransformFeedbackBufferBitExt;
  37. private const BufferUsageFlags HostImportedBufferUsageFlags =
  38. BufferUsageFlags.TransferSrcBit |
  39. BufferUsageFlags.TransferDstBit;
  40. private readonly Device _device;
  41. private readonly IdList<BufferHolder> _buffers;
  42. public int BufferCount { get; private set; }
  43. public StagingBuffer StagingBuffer { get; }
  44. public MemoryRequirements HostImportedBufferMemoryRequirements { get; }
  45. public BufferManager(VulkanRenderer gd, Device device)
  46. {
  47. _device = device;
  48. _buffers = new IdList<BufferHolder>();
  49. StagingBuffer = new StagingBuffer(gd, this);
  50. HostImportedBufferMemoryRequirements = GetHostImportedUsageRequirements(gd);
  51. }
  52. public unsafe BufferHandle CreateHostImported(VulkanRenderer gd, nint pointer, int size)
  53. {
  54. var usage = HostImportedBufferUsageFlags;
  55. if (gd.Capabilities.SupportsIndirectParameters)
  56. {
  57. usage |= BufferUsageFlags.IndirectBufferBit;
  58. }
  59. var bufferCreateInfo = new BufferCreateInfo()
  60. {
  61. SType = StructureType.BufferCreateInfo,
  62. Size = (ulong)size,
  63. Usage = usage,
  64. SharingMode = SharingMode.Exclusive
  65. };
  66. gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
  67. (Auto<MemoryAllocation> allocation, ulong offset) = gd.HostMemoryAllocator.GetExistingAllocation(pointer, (ulong)size);
  68. gd.Api.BindBufferMemory(_device, buffer, allocation.GetUnsafe().Memory, allocation.GetUnsafe().Offset + offset);
  69. var holder = new BufferHolder(gd, _device, buffer, allocation, size, BufferAllocationType.HostMapped, BufferAllocationType.HostMapped, (int)offset);
  70. BufferCount++;
  71. ulong handle64 = (uint)_buffers.Add(holder);
  72. return Unsafe.As<ulong, BufferHandle>(ref handle64);
  73. }
  74. public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default)
  75. {
  76. return CreateWithHandle(gd, size, out _, baseType, storageHint);
  77. }
  78. public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, out BufferHolder holder, BufferAllocationType baseType = BufferAllocationType.HostMapped, BufferHandle storageHint = default)
  79. {
  80. holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
  81. if (holder == null)
  82. {
  83. return BufferHandle.Null;
  84. }
  85. BufferCount++;
  86. ulong handle64 = (uint)_buffers.Add(holder);
  87. return Unsafe.As<ulong, BufferHandle>(ref handle64);
  88. }
  89. public unsafe MemoryRequirements GetHostImportedUsageRequirements(VulkanRenderer gd)
  90. {
  91. var usage = HostImportedBufferUsageFlags;
  92. if (gd.Capabilities.SupportsIndirectParameters)
  93. {
  94. usage |= BufferUsageFlags.IndirectBufferBit;
  95. }
  96. var bufferCreateInfo = new BufferCreateInfo()
  97. {
  98. SType = StructureType.BufferCreateInfo,
  99. Size = (ulong)Environment.SystemPageSize,
  100. Usage = usage,
  101. SharingMode = SharingMode.Exclusive
  102. };
  103. gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
  104. gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
  105. gd.Api.DestroyBuffer(_device, buffer, null);
  106. return requirements;
  107. }
  108. public unsafe (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) CreateBacking(
  109. VulkanRenderer gd,
  110. int size,
  111. BufferAllocationType type,
  112. bool forConditionalRendering = false,
  113. BufferAllocationType fallbackType = BufferAllocationType.Auto)
  114. {
  115. var usage = DefaultBufferUsageFlags;
  116. if (forConditionalRendering && gd.Capabilities.SupportsConditionalRendering)
  117. {
  118. usage |= BufferUsageFlags.ConditionalRenderingBitExt;
  119. }
  120. else if (gd.Capabilities.SupportsIndirectParameters)
  121. {
  122. usage |= BufferUsageFlags.IndirectBufferBit;
  123. }
  124. var bufferCreateInfo = new BufferCreateInfo()
  125. {
  126. SType = StructureType.BufferCreateInfo,
  127. Size = (ulong)size,
  128. Usage = usage,
  129. SharingMode = SharingMode.Exclusive
  130. };
  131. gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
  132. gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
  133. MemoryAllocation allocation;
  134. do
  135. {
  136. var allocateFlags = type switch
  137. {
  138. BufferAllocationType.HostMappedNoCache => DefaultBufferMemoryNoCacheFlags,
  139. BufferAllocationType.HostMapped => DefaultBufferMemoryFlags,
  140. BufferAllocationType.DeviceLocal => DeviceLocalBufferMemoryFlags,
  141. BufferAllocationType.DeviceLocalMapped => DeviceLocalMappedBufferMemoryFlags,
  142. _ => DefaultBufferMemoryFlags
  143. };
  144. // If an allocation with this memory type fails, fall back to the previous one.
  145. try
  146. {
  147. allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, true);
  148. }
  149. catch (VulkanException)
  150. {
  151. allocation = default;
  152. }
  153. }
  154. while (allocation.Memory.Handle == 0 && (--type != fallbackType));
  155. if (allocation.Memory.Handle == 0UL)
  156. {
  157. gd.Api.DestroyBuffer(_device, buffer, null);
  158. return default;
  159. }
  160. gd.Api.BindBufferMemory(_device, buffer, allocation.Memory, allocation.Offset);
  161. return (buffer, allocation, type);
  162. }
  163. public unsafe BufferHolder Create(
  164. VulkanRenderer gd,
  165. int size,
  166. bool forConditionalRendering = false,
  167. BufferAllocationType baseType = BufferAllocationType.HostMapped,
  168. BufferHandle storageHint = default)
  169. {
  170. BufferAllocationType type = baseType;
  171. BufferHolder storageHintHolder = null;
  172. if (baseType == BufferAllocationType.Auto)
  173. {
  174. if (gd.IsSharedMemory)
  175. {
  176. baseType = BufferAllocationType.HostMapped;
  177. type = baseType;
  178. }
  179. else
  180. {
  181. type = size >= BufferHolder.DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocal : BufferAllocationType.HostMapped;
  182. }
  183. if (storageHint != BufferHandle.Null)
  184. {
  185. if (TryGetBuffer(storageHint, out storageHintHolder))
  186. {
  187. type = storageHintHolder.DesiredType;
  188. }
  189. }
  190. }
  191. (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) =
  192. CreateBacking(gd, size, type, forConditionalRendering);
  193. if (buffer.Handle != 0)
  194. {
  195. var holder = new BufferHolder(gd, _device, buffer, allocation, size, baseType, resultType);
  196. if (storageHintHolder != null)
  197. {
  198. holder.InheritMetrics(storageHintHolder);
  199. }
  200. return holder;
  201. }
  202. return null;
  203. }
  204. public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size, Action invalidateView)
  205. {
  206. if (TryGetBuffer(handle, out var holder))
  207. {
  208. return holder.CreateView(format, offset, size, invalidateView);
  209. }
  210. return null;
  211. }
  212. public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, bool isSSBO = false)
  213. {
  214. if (TryGetBuffer(handle, out var holder))
  215. {
  216. return holder.GetBuffer(commandBuffer, isWrite, isSSBO);
  217. }
  218. return null;
  219. }
  220. public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, int offset, int size, bool isWrite)
  221. {
  222. if (TryGetBuffer(handle, out var holder))
  223. {
  224. return holder.GetBuffer(commandBuffer, offset, size, isWrite);
  225. }
  226. return null;
  227. }
  228. public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size)
  229. {
  230. if (TryGetBuffer(handle, out var holder))
  231. {
  232. return holder.GetBufferI8ToI16(cbs, offset, size);
  233. }
  234. return null;
  235. }
  236. public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, int stride, int alignment)
  237. {
  238. if (TryGetBuffer(handle, out var holder))
  239. {
  240. return holder.GetAlignedVertexBuffer(cbs, offset, size, stride, alignment);
  241. }
  242. return null;
  243. }
  244. public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
  245. {
  246. if (TryGetBuffer(handle, out var holder))
  247. {
  248. return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
  249. }
  250. return null;
  251. }
  252. public (Auto<DisposableBuffer>, Auto<DisposableBuffer>) GetBufferTopologyConversionIndirect(
  253. VulkanRenderer gd,
  254. CommandBufferScoped cbs,
  255. BufferRange indexBuffer,
  256. BufferRange indirectBuffer,
  257. BufferRange drawCountBuffer,
  258. IndexBufferPattern pattern,
  259. int indexSize,
  260. bool hasDrawCount,
  261. int maxDrawCount,
  262. int indirectDataStride)
  263. {
  264. BufferHolder drawCountBufferHolder = null;
  265. if (!TryGetBuffer(indexBuffer.Handle, out var indexBufferHolder) ||
  266. !TryGetBuffer(indirectBuffer.Handle, out var indirectBufferHolder) ||
  267. (hasDrawCount && !TryGetBuffer(drawCountBuffer.Handle, out drawCountBufferHolder)))
  268. {
  269. return (null, null);
  270. }
  271. var indexBufferKey = new TopologyConversionIndirectCacheKey(
  272. gd,
  273. pattern,
  274. indexSize,
  275. indirectBufferHolder,
  276. indirectBuffer.Offset,
  277. indirectBuffer.Size);
  278. bool hasConvertedIndexBuffer = indexBufferHolder.TryGetCachedConvertedBuffer(
  279. indexBuffer.Offset,
  280. indexBuffer.Size,
  281. indexBufferKey,
  282. out var convertedIndexBuffer);
  283. var indirectBufferKey = new IndirectDataCacheKey(pattern);
  284. bool hasConvertedIndirectBuffer = indirectBufferHolder.TryGetCachedConvertedBuffer(
  285. indirectBuffer.Offset,
  286. indirectBuffer.Size,
  287. indirectBufferKey,
  288. out var convertedIndirectBuffer);
  289. var drawCountBufferKey = new DrawCountCacheKey();
  290. bool hasCachedDrawCount = true;
  291. if (hasDrawCount)
  292. {
  293. hasCachedDrawCount = drawCountBufferHolder.TryGetCachedConvertedBuffer(
  294. drawCountBuffer.Offset,
  295. drawCountBuffer.Size,
  296. drawCountBufferKey,
  297. out _);
  298. }
  299. if (!hasConvertedIndexBuffer || !hasConvertedIndirectBuffer || !hasCachedDrawCount)
  300. {
  301. // The destination index size is always I32.
  302. int indexCount = indexBuffer.Size / indexSize;
  303. int convertedCount = pattern.GetConvertedCount(indexCount);
  304. if (!hasConvertedIndexBuffer)
  305. {
  306. convertedIndexBuffer = Create(gd, convertedCount * 4);
  307. indexBufferKey.SetBuffer(convertedIndexBuffer.GetBuffer());
  308. indexBufferHolder.AddCachedConvertedBuffer(indexBuffer.Offset, indexBuffer.Size, indexBufferKey, convertedIndexBuffer);
  309. }
  310. if (!hasConvertedIndirectBuffer)
  311. {
  312. convertedIndirectBuffer = Create(gd, indirectBuffer.Size);
  313. indirectBufferHolder.AddCachedConvertedBuffer(indirectBuffer.Offset, indirectBuffer.Size, indirectBufferKey, convertedIndirectBuffer);
  314. }
  315. gd.PipelineInternal.EndRenderPass();
  316. gd.HelperShader.ConvertIndexBufferIndirect(
  317. gd,
  318. cbs,
  319. indirectBufferHolder,
  320. convertedIndirectBuffer,
  321. drawCountBuffer,
  322. indexBufferHolder,
  323. convertedIndexBuffer,
  324. pattern,
  325. indexSize,
  326. indexBuffer.Offset,
  327. indexBuffer.Size,
  328. indirectBuffer.Offset,
  329. hasDrawCount,
  330. maxDrawCount,
  331. indirectDataStride);
  332. // Any modification of the indirect buffer should invalidate the index buffers that are associated with it,
  333. // since we used the indirect data to find the range of the index buffer that is used.
  334. var indexBufferDependency = new Dependency(
  335. indexBufferHolder,
  336. indexBuffer.Offset,
  337. indexBuffer.Size,
  338. indexBufferKey);
  339. indirectBufferHolder.AddCachedConvertedBufferDependency(
  340. indirectBuffer.Offset,
  341. indirectBuffer.Size,
  342. indirectBufferKey,
  343. indexBufferDependency);
  344. if (hasDrawCount)
  345. {
  346. if (!hasCachedDrawCount)
  347. {
  348. drawCountBufferHolder.AddCachedConvertedBuffer(drawCountBuffer.Offset, drawCountBuffer.Size, drawCountBufferKey, null);
  349. }
  350. // If we have a draw count, any modification of the draw count should invalidate all indirect buffers
  351. // where we used it to find the range of indirect data that is actually used.
  352. var indirectBufferDependency = new Dependency(
  353. indirectBufferHolder,
  354. indirectBuffer.Offset,
  355. indirectBuffer.Size,
  356. indirectBufferKey);
  357. drawCountBufferHolder.AddCachedConvertedBufferDependency(
  358. drawCountBuffer.Offset,
  359. drawCountBuffer.Size,
  360. drawCountBufferKey,
  361. indirectBufferDependency);
  362. }
  363. }
  364. return (convertedIndexBuffer.GetBuffer(), convertedIndirectBuffer.GetBuffer());
  365. }
  366. public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
  367. {
  368. if (TryGetBuffer(handle, out var holder))
  369. {
  370. size = holder.Size;
  371. return holder.GetBuffer(commandBuffer, isWrite);
  372. }
  373. size = 0;
  374. return null;
  375. }
  376. public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)
  377. {
  378. if (TryGetBuffer(handle, out var holder))
  379. {
  380. return holder.GetData(offset, size);
  381. }
  382. return new PinnedSpan<byte>();
  383. }
  384. public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
  385. {
  386. SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null, null);
  387. }
  388. public void SetData(BufferHandle handle, int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs, Action endRenderPass)
  389. {
  390. if (TryGetBuffer(handle, out var holder))
  391. {
  392. holder.SetData(offset, data, cbs, endRenderPass);
  393. }
  394. }
  395. public void Delete(BufferHandle handle)
  396. {
  397. if (TryGetBuffer(handle, out var holder))
  398. {
  399. holder.Dispose();
  400. _buffers.Remove((int)Unsafe.As<BufferHandle, ulong>(ref handle));
  401. }
  402. }
  403. private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder)
  404. {
  405. return _buffers.TryGetValue((int)Unsafe.As<BufferHandle, ulong>(ref handle), out holder);
  406. }
  407. protected virtual void Dispose(bool disposing)
  408. {
  409. if (disposing)
  410. {
  411. foreach (BufferHolder buffer in _buffers)
  412. {
  413. buffer.Dispose();
  414. }
  415. _buffers.Clear();
  416. StagingBuffer.Dispose();
  417. }
  418. }
  419. public void Dispose()
  420. {
  421. Dispose(true);
  422. }
  423. }
  424. }