BufferManager.cs 19 KB

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