BufferManager.cs 19 KB

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