BufferManager.cs 17 KB

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