BsVulkanHardwareBuffer.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsVulkanHardwareBuffer.h"
  4. #include "BsVulkanRenderAPI.h"
  5. #include "BsVulkanDevice.h"
  6. #include "BsVulkanUtility.h"
  7. #include "Managers/BsVulkanCommandBufferManager.h"
  8. #include "BsVulkanCommandBuffer.h"
  9. #include "BsVulkanTexture.h"
  10. namespace bs { namespace ct
  11. {
  12. VulkanBuffer::VulkanBuffer(VulkanResourceManager* owner, VkBuffer buffer, VkBufferView view, VmaAllocation allocation,
  13. UINT32 rowPitch, UINT32 slicePitch)
  14. : VulkanResource(owner, false), mBuffer(buffer), mView(view), mAllocation(allocation), mRowPitch(rowPitch)
  15. {
  16. if (rowPitch != 0)
  17. mSliceHeight = slicePitch / rowPitch;
  18. else
  19. mSliceHeight = 0;
  20. }
  21. VulkanBuffer::~VulkanBuffer()
  22. {
  23. VulkanDevice& device = mOwner->getDevice();
  24. if (mView != VK_NULL_HANDLE)
  25. vkDestroyBufferView(device.getLogical(), mView, gVulkanAllocator);
  26. vkDestroyBuffer(device.getLogical(), mBuffer, gVulkanAllocator);
  27. device.freeMemory(mAllocation);
  28. }
  29. UINT8* VulkanBuffer::map(VkDeviceSize offset, VkDeviceSize length) const
  30. {
  31. VulkanDevice& device = mOwner->getDevice();
  32. VkDeviceMemory memory;
  33. VkDeviceSize memoryOffset;
  34. device.getAllocationInfo(mAllocation, memory, memoryOffset);
  35. UINT8* data;
  36. VkResult result = vkMapMemory(device.getLogical(), memory, memoryOffset + offset, length, 0, (void**)&data);
  37. assert(result == VK_SUCCESS);
  38. return data;
  39. }
  40. void VulkanBuffer::unmap()
  41. {
  42. VulkanDevice& device = mOwner->getDevice();
  43. VkDeviceMemory memory;
  44. VkDeviceSize memoryOffset;
  45. device.getAllocationInfo(mAllocation, memory, memoryOffset);
  46. vkUnmapMemory(device.getLogical(), memory);
  47. }
  48. void VulkanBuffer::copy(VulkanCmdBuffer* cb, VulkanBuffer* destination, VkDeviceSize srcOffset,
  49. VkDeviceSize dstOffset, VkDeviceSize length)
  50. {
  51. VkBufferCopy region;
  52. region.size = length;
  53. region.srcOffset = srcOffset;
  54. region.dstOffset = dstOffset;
  55. vkCmdCopyBuffer(cb->getHandle(), mBuffer, destination->getHandle(), 1, &region);
  56. }
  57. void VulkanBuffer::copy(VulkanCmdBuffer* cb, VulkanImage* destination, const VkExtent3D& extent,
  58. const VkImageSubresourceLayers& range, VkImageLayout layout)
  59. {
  60. VkBufferImageCopy region;
  61. region.bufferRowLength = mRowPitch;
  62. region.bufferImageHeight = mSliceHeight;
  63. region.bufferOffset = 0;
  64. region.imageOffset.x = 0;
  65. region.imageOffset.y = 0;
  66. region.imageOffset.z = 0;
  67. region.imageExtent = extent;
  68. region.imageSubresource = range;
  69. vkCmdCopyBufferToImage(cb->getHandle(), mBuffer, destination->getHandle(), layout, 1, &region);
  70. }
  71. void VulkanBuffer::update(VulkanCmdBuffer* cb, UINT8* data, VkDeviceSize offset, VkDeviceSize length)
  72. {
  73. vkCmdUpdateBuffer(cb->getHandle(), mBuffer, offset, length, (uint32_t*)data);
  74. }
  75. VulkanHardwareBuffer::VulkanHardwareBuffer(BufferType type, GpuBufferFormat format, GpuBufferUsage usage,
  76. UINT32 size, GpuDeviceFlags deviceMask)
  77. : HardwareBuffer(size), mBuffers(), mStagingBuffer(nullptr), mStagingMemory(nullptr), mMappedDeviceIdx(-1)
  78. , mMappedGlobalQueueIdx(-1), mMappedOffset(0), mMappedSize(0), mMappedLockOptions(GBL_WRITE_ONLY)
  79. , mDirectlyMappable((usage & GBU_DYNAMIC) != 0), mSupportsGPUWrites(type == BT_STORAGE), mRequiresView(false)
  80. , mIsMapped(false)
  81. {
  82. VkBufferUsageFlags usageFlags = 0;
  83. switch(type)
  84. {
  85. case BT_VERTEX:
  86. usageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
  87. break;
  88. case BT_INDEX:
  89. usageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
  90. break;
  91. case BT_UNIFORM:
  92. usageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
  93. break;
  94. case BT_GENERIC:
  95. usageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
  96. mRequiresView = true;
  97. break;
  98. case BT_STORAGE:
  99. usageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
  100. VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
  101. mRequiresView = true;
  102. break;
  103. case BT_STRUCTURED:
  104. usageFlags = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
  105. break;
  106. }
  107. mBufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
  108. mBufferCI.pNext = nullptr;
  109. mBufferCI.flags = 0;
  110. mBufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  111. mBufferCI.usage = usageFlags;
  112. mBufferCI.queueFamilyIndexCount = 0;
  113. mBufferCI.pQueueFamilyIndices = nullptr;
  114. mViewCI.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
  115. mViewCI.pNext = nullptr;
  116. mViewCI.flags = 0;
  117. mViewCI.format = VulkanUtility::getBufferFormat(format);
  118. mViewCI.offset = 0;
  119. mViewCI.range = VK_WHOLE_SIZE;
  120. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPI::instance());
  121. VulkanDevice* devices[BS_MAX_DEVICES];
  122. VulkanUtility::getDevices(rapi, deviceMask, devices);
  123. // Allocate buffers per-device
  124. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  125. {
  126. if (devices[i] == nullptr)
  127. continue;
  128. mBuffers[i] = createBuffer(*devices[i], size, false, true);
  129. }
  130. }
  131. VulkanHardwareBuffer::~VulkanHardwareBuffer()
  132. {
  133. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  134. {
  135. if (mBuffers[i] == nullptr)
  136. continue;
  137. mBuffers[i]->destroy();
  138. }
  139. assert(mStagingBuffer == nullptr);
  140. }
  141. VulkanBuffer* VulkanHardwareBuffer::createBuffer(VulkanDevice& device, UINT32 size, bool staging, bool readable)
  142. {
  143. VkBufferUsageFlags usage = mBufferCI.usage;
  144. if (staging)
  145. {
  146. mBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
  147. // Staging buffers are used as a destination for reads
  148. if (readable)
  149. mBufferCI.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
  150. }
  151. else if(readable) // Non-staging readable
  152. mBufferCI.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
  153. mBufferCI.size = size;
  154. VkMemoryPropertyFlags flags = (mDirectlyMappable || staging) ?
  155. (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) : // Note: Try using cached memory
  156. VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
  157. VkDevice vkDevice = device.getLogical();
  158. VkBuffer buffer;
  159. VkResult result = vkCreateBuffer(vkDevice, &mBufferCI, gVulkanAllocator, &buffer);
  160. assert(result == VK_SUCCESS);
  161. VmaAllocation allocation = device.allocateMemory(buffer, flags);
  162. VkBufferView view;
  163. if (mRequiresView && !staging)
  164. {
  165. mViewCI.buffer = buffer;
  166. result = vkCreateBufferView(vkDevice, &mViewCI, gVulkanAllocator, &view);
  167. assert(result == VK_SUCCESS);
  168. }
  169. else
  170. view = VK_NULL_HANDLE;
  171. mBufferCI.usage = usage; // Restore original usage
  172. return device.getResourceManager().create<VulkanBuffer>(buffer, view, allocation);
  173. }
  174. void* VulkanHardwareBuffer::map(UINT32 offset, UINT32 length, GpuLockOptions options, UINT32 deviceIdx, UINT32 queueIdx)
  175. {
  176. if ((offset + length) > mSize)
  177. {
  178. LOGERR("Provided offset(" + toString(offset) + ") + length(" + toString(length) + ") "
  179. "is larger than the buffer " + toString(mSize) + ".");
  180. return nullptr;
  181. }
  182. if (length == 0)
  183. return nullptr;
  184. VulkanBuffer* buffer = mBuffers[deviceIdx];
  185. if (buffer == nullptr)
  186. return nullptr;
  187. mIsMapped = true;
  188. mMappedDeviceIdx = deviceIdx;
  189. mMappedGlobalQueueIdx = queueIdx;
  190. mMappedOffset = offset;
  191. mMappedSize = length;
  192. mMappedLockOptions = options;
  193. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPI::instance());
  194. VulkanDevice& device = *rapi._getDevice(deviceIdx);
  195. VulkanCommandBufferManager& cbManager = gVulkanCBManager();
  196. GpuQueueType queueType;
  197. UINT32 localQueueIdx = CommandSyncMask::getQueueIdxAndType(queueIdx, queueType);
  198. VkAccessFlags accessFlags;
  199. if (options == GBL_READ_ONLY)
  200. accessFlags = VK_ACCESS_HOST_READ_BIT;
  201. else if (options == GBL_READ_WRITE)
  202. accessFlags = VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT;
  203. else
  204. accessFlags = VK_ACCESS_HOST_WRITE_BIT;
  205. // If memory is host visible try mapping it directly
  206. if(mDirectlyMappable)
  207. {
  208. // Check is the GPU currently reading or writing from the buffer
  209. UINT32 useMask = buffer->getUseInfo(VulkanUseFlag::Read | VulkanUseFlag::Write);
  210. // Note: Even if GPU isn't currently using the buffer, but the buffer supports GPU writes, we consider it as
  211. // being used because the write could have completed yet still not visible, so we need to issue a pipeline
  212. // barrier below.
  213. bool isUsedOnGPU = useMask != 0 || mSupportsGPUWrites;
  214. // We're safe to map directly since GPU isn't using the buffer
  215. if (!isUsedOnGPU)
  216. {
  217. // If some CB has an operation queued that will be using the current contents of the buffer, create a new
  218. // buffer so we don't modify the previous use of the buffer
  219. if(buffer->isBound())
  220. {
  221. VulkanBuffer* newBuffer = createBuffer(device, mSize, false, true);
  222. // Copy contents of the current buffer to the new one, unless caller explicitly specifies he doesn't
  223. // care about the current contents
  224. if (options != GBL_WRITE_ONLY_DISCARD)
  225. {
  226. UINT8* src = buffer->map(offset, length);
  227. UINT8* dst = newBuffer->map(offset, length);
  228. memcpy(dst, src, length);
  229. buffer->unmap();
  230. newBuffer->unmap();
  231. }
  232. buffer->destroy();
  233. buffer = newBuffer;
  234. mBuffers[deviceIdx] = buffer;
  235. }
  236. return buffer->map(offset, length);
  237. }
  238. // Caller guarantees he won't touch the same data as the GPU, so just map even though the GPU is using the buffer
  239. if (options == GBL_WRITE_ONLY_NO_OVERWRITE)
  240. return buffer->map(offset, length);
  241. // Caller doesn't care about buffer contents, so just discard the existing buffer and create a new one
  242. if (options == GBL_WRITE_ONLY_DISCARD)
  243. {
  244. buffer->destroy();
  245. buffer = createBuffer(device, mSize, false, true);
  246. mBuffers[deviceIdx] = buffer;
  247. return buffer->map(offset, length);
  248. }
  249. // We need to read the buffer contents
  250. if(options == GBL_READ_ONLY || options == GBL_READ_WRITE)
  251. {
  252. // We need to wait until (potential) read/write operations complete
  253. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(deviceIdx, queueType, localQueueIdx);
  254. // Ensure flush() will wait for all queues currently using to the buffer (if any) to finish
  255. // If only reading, wait for all writes to complete, otherwise wait on both writes and reads
  256. if (options == GBL_READ_ONLY)
  257. useMask = buffer->getUseInfo(VulkanUseFlag::Write);
  258. else
  259. useMask = buffer->getUseInfo(VulkanUseFlag::Read | VulkanUseFlag::Write);
  260. transferCB->appendMask(useMask);
  261. // Make any writes visible before mapping
  262. if (mSupportsGPUWrites)
  263. {
  264. // Issue a barrier so :
  265. // - If reading: the device makes the written memory available for read (read-after-write hazard)
  266. // - If writing: ensures our writes properly overlap with GPU writes (write-after-write hazard)
  267. transferCB->memoryBarrier(buffer->getHandle(),
  268. VK_ACCESS_SHADER_WRITE_BIT,
  269. accessFlags,
  270. // Last stages that could have written to the buffer:
  271. VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
  272. VK_PIPELINE_STAGE_HOST_BIT
  273. );
  274. }
  275. // Submit the command buffer and wait until it finishes
  276. transferCB->flush(true);
  277. // If writing and some CB has an operation queued that will be using the current contents of the buffer,
  278. // create a new buffer so we don't modify the previous use of the buffer
  279. if (options == GBL_READ_WRITE && buffer->isBound())
  280. {
  281. VulkanBuffer* newBuffer = createBuffer(device, mSize, false, true);
  282. // Copy contents of the current buffer to the new one
  283. UINT8* src = buffer->map(offset, length);
  284. UINT8* dst = newBuffer->map(offset, length);
  285. memcpy(dst, src, length);
  286. buffer->unmap();
  287. newBuffer->unmap();
  288. buffer->destroy();
  289. buffer = newBuffer;
  290. mBuffers[deviceIdx] = buffer;
  291. }
  292. return buffer->map(offset, length);
  293. }
  294. // Otherwise, we're doing write only, in which case it's best to use the staging buffer to avoid waiting
  295. // and blocking, so fall through
  296. }
  297. // Can't use direct mapping, so use a staging buffer or memory
  298. // We might need to copy the current contents of the buffer to the staging buffer. Even if the user doesn't plan on
  299. // reading, it is still required as we will eventually copy all of the contents back to the original buffer,
  300. // and we can't write potentially uninitialized data. The only exception is when the caller specifies the buffer
  301. // contents should be discarded in which he guarantees he will overwrite the entire locked area with his own
  302. // contents.
  303. bool needRead = options != GBL_WRITE_ONLY_DISCARD_RANGE && options != GBL_WRITE_ONLY_DISCARD;
  304. // See if we can use the cheaper staging memory, rather than a staging buffer
  305. if(!needRead && offset % 4 == 0 && length % 4 == 0 && length <= 65536)
  306. {
  307. mStagingMemory = (UINT8*)bs_alloc(length);
  308. return mStagingMemory;
  309. }
  310. // Create a staging buffer
  311. mStagingBuffer = createBuffer(device, length, true, needRead);
  312. if (needRead)
  313. {
  314. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(deviceIdx, queueType, localQueueIdx);
  315. // Similar to above, if buffer supports GPU writes or is currently being written to, we need to wait on any
  316. // potential writes to complete
  317. UINT32 writeUseMask = buffer->getUseInfo(VulkanUseFlag::Write);
  318. if(mSupportsGPUWrites || writeUseMask != 0)
  319. {
  320. // Ensure flush() will wait for all queues currently writing to the buffer (if any) to finish
  321. transferCB->appendMask(writeUseMask);
  322. }
  323. // Queue copy command
  324. buffer->copy(transferCB->getCB(), mStagingBuffer, offset, 0, length);
  325. // Ensure data written to the staging buffer is visible
  326. transferCB->memoryBarrier(mStagingBuffer->getHandle(),
  327. VK_ACCESS_TRANSFER_WRITE_BIT,
  328. accessFlags,
  329. VK_PIPELINE_STAGE_TRANSFER_BIT,
  330. VK_PIPELINE_STAGE_HOST_BIT
  331. );
  332. // Submit the command buffer and wait until it finishes
  333. transferCB->flush(true);
  334. assert(!buffer->isUsed());
  335. }
  336. return mStagingBuffer->map(0, length);
  337. }
  338. void VulkanHardwareBuffer::unmap()
  339. {
  340. // Possibly map() failed with some error
  341. if (!mIsMapped)
  342. return;
  343. // Note: If we did any writes they need to be made visible to the GPU. However there is no need to execute
  344. // a pipeline barrier because (as per spec) host writes are implicitly visible to the device.
  345. if(mStagingMemory == nullptr && mStagingBuffer == nullptr) // We directly mapped the buffer
  346. {
  347. mBuffers[mMappedDeviceIdx]->unmap();
  348. }
  349. else
  350. {
  351. if(mStagingBuffer != nullptr)
  352. mStagingBuffer->unmap();
  353. bool isWrite = mMappedLockOptions != GBL_READ_ONLY;
  354. // We the caller wrote anything to the staging buffer, we need to upload it back to the main buffer
  355. if(isWrite)
  356. {
  357. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPI::instance());
  358. VulkanDevice& device = *rapi._getDevice(mMappedDeviceIdx);
  359. VulkanCommandBufferManager& cbManager = gVulkanCBManager();
  360. GpuQueueType queueType;
  361. UINT32 localQueueIdx = CommandSyncMask::getQueueIdxAndType(mMappedGlobalQueueIdx, queueType);
  362. VulkanBuffer* buffer = mBuffers[mMappedDeviceIdx];
  363. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(mMappedDeviceIdx, queueType, localQueueIdx);
  364. // If the buffer is used in any way on the GPU, we need to wait for that use to finish before
  365. // we issue our copy
  366. UINT32 useMask = buffer->getUseInfo(VulkanUseFlag::Read | VulkanUseFlag::Write);
  367. bool isNormalWrite = false;
  368. if(useMask != 0) // Buffer is currently used on the GPU
  369. {
  370. // Try to avoid the wait by checking for special write conditions
  371. // Caller guarantees he won't touch the same data as the GPU, so just copy
  372. if (mMappedLockOptions == GBL_WRITE_ONLY_NO_OVERWRITE)
  373. {
  374. // Fall through to copy()
  375. }
  376. // Caller doesn't care about buffer contents, so just discard the existing buffer and create a new one
  377. else if (mMappedLockOptions == GBL_WRITE_ONLY_DISCARD)
  378. {
  379. buffer->destroy();
  380. buffer = createBuffer(device, mSize, false, true);
  381. mBuffers[mMappedDeviceIdx] = buffer;
  382. }
  383. else // Otherwise we have no choice but to issue a dependency between the queues
  384. {
  385. transferCB->appendMask(useMask);
  386. isNormalWrite = true;
  387. }
  388. }
  389. else
  390. isNormalWrite = true;
  391. // Check if the buffer will still be bound somewhere after the CBs using it finish
  392. if (isNormalWrite)
  393. {
  394. UINT32 useCount = buffer->getUseCount();
  395. UINT32 boundCount = buffer->getBoundCount();
  396. bool isBoundWithoutUse = boundCount > useCount;
  397. // If buffer is queued for some operation on a CB, then we need to make a copy of the buffer to
  398. // avoid modifying its use in the previous operation
  399. if (isBoundWithoutUse)
  400. {
  401. VulkanBuffer* newBuffer = createBuffer(device, mSize, false, true);
  402. // Avoid copying original contents if the staging buffer completely covers it
  403. if (mMappedOffset > 0 || mMappedSize != mSize)
  404. {
  405. buffer->copy(transferCB->getCB(), newBuffer, 0, 0, mSize);
  406. transferCB->getCB()->registerResource(buffer, VK_ACCESS_TRANSFER_READ_BIT, VulkanUseFlag::Read);
  407. }
  408. buffer->destroy();
  409. buffer = newBuffer;
  410. mBuffers[mMappedDeviceIdx] = buffer;
  411. }
  412. }
  413. // Queue copy/update command
  414. if (mStagingBuffer != nullptr)
  415. {
  416. mStagingBuffer->copy(transferCB->getCB(), buffer, 0, mMappedOffset, mMappedSize);
  417. transferCB->getCB()->registerResource(mStagingBuffer, VK_ACCESS_TRANSFER_READ_BIT, VulkanUseFlag::Read);
  418. }
  419. else // Staging memory
  420. {
  421. buffer->update(transferCB->getCB(), mStagingMemory, mMappedOffset, mMappedSize);
  422. }
  423. transferCB->getCB()->registerResource(buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VulkanUseFlag::Write);
  424. // We don't actually flush the transfer buffer here since it's an expensive operation, but it's instead
  425. // done automatically before next "normal" command buffer submission.
  426. }
  427. if (mStagingBuffer != nullptr)
  428. {
  429. mStagingBuffer->destroy();
  430. mStagingBuffer = nullptr;
  431. }
  432. if(mStagingMemory != nullptr)
  433. {
  434. bs_free(mStagingMemory);
  435. mStagingMemory = nullptr;
  436. }
  437. }
  438. mIsMapped = false;
  439. }
  440. void VulkanHardwareBuffer::copyData(HardwareBuffer& srcBuffer, UINT32 srcOffset,
  441. UINT32 dstOffset, UINT32 length, bool discardWholeBuffer, const SPtr<CommandBuffer>& commandBuffer)
  442. {
  443. if ((dstOffset + length) > mSize)
  444. {
  445. LOGERR("Provided offset(" + toString(dstOffset) + ") + length(" + toString(length) + ") "
  446. "is larger than the destination buffer " + toString(mSize) + ". Copy operation aborted.");
  447. return;
  448. }
  449. if ((srcOffset + length) > srcBuffer.getSize())
  450. {
  451. LOGERR("Provided offset(" + toString(srcOffset) + ") + length(" + toString(length) + ") "
  452. "is larger than the source buffer " + toString(srcBuffer.getSize()) + ". Copy operation aborted.");
  453. return;
  454. }
  455. VulkanHardwareBuffer& vkSource = static_cast<VulkanHardwareBuffer&>(srcBuffer);
  456. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPI::instance());
  457. VulkanCmdBuffer* vkCB;
  458. if (commandBuffer != nullptr)
  459. vkCB = static_cast<VulkanCommandBuffer*>(commandBuffer.get())->getInternal();
  460. else
  461. vkCB = rapi._getMainCommandBuffer()->getInternal();
  462. UINT32 deviceIdx = vkCB->getDeviceIdx();
  463. VulkanBuffer* src = vkSource.mBuffers[deviceIdx];
  464. VulkanBuffer* dst = mBuffers[deviceIdx];
  465. if (src == nullptr || dst == nullptr)
  466. return;
  467. if (vkCB->isInRenderPass())
  468. vkCB->endRenderPass();
  469. src->copy(vkCB, dst, srcOffset, dstOffset, length);
  470. // Notify the command buffer that these resources are being used on it
  471. vkCB->registerResource(src, VK_ACCESS_TRANSFER_READ_BIT, VulkanUseFlag::Read);
  472. vkCB->registerResource(dst, VK_ACCESS_TRANSFER_WRITE_BIT, VulkanUseFlag::Write);
  473. }
  474. void VulkanHardwareBuffer::readData(UINT32 offset, UINT32 length, void* dest, UINT32 deviceIdx, UINT32 queueIdx)
  475. {
  476. void* lockedData = lock(offset, length, GBL_READ_ONLY, deviceIdx, queueIdx);
  477. memcpy(dest, lockedData, length);
  478. unlock();
  479. }
  480. void VulkanHardwareBuffer::writeData(UINT32 offset, UINT32 length, const void* source, BufferWriteType writeFlags,
  481. UINT32 queueIdx)
  482. {
  483. GpuLockOptions lockOptions = GBL_WRITE_ONLY_DISCARD_RANGE;
  484. if (writeFlags == BTW_NO_OVERWRITE)
  485. lockOptions = GBL_WRITE_ONLY_NO_OVERWRITE;
  486. else if (writeFlags == BWT_DISCARD)
  487. lockOptions = GBL_WRITE_ONLY_DISCARD;
  488. // Write to every device
  489. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  490. {
  491. if (mBuffers[i] == nullptr)
  492. continue;
  493. void* lockedData = lock(offset, length, lockOptions, i, queueIdx);
  494. memcpy(lockedData, source, length);
  495. unlock();
  496. }
  497. }
  498. }}