BsVulkanTexture.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsVulkanTexture.h"
  4. #include "BsVulkanRenderAPI.h"
  5. #include "BsVulkanDevice.h"
  6. #include "BsVulkanUtility.h"
  7. #include "BsVulkanCommandBufferManager.h"
  8. #include "BsVulkanHardwareBuffer.h"
  9. #include "BsCoreThread.h"
  10. #include "BsRenderStats.h"
  11. #include "BsMath.h"
  12. namespace bs
  13. {
  14. VulkanImage::VulkanImage(VulkanResourceManager* owner, VkImage image, VkDeviceMemory memory, VkImageLayout layout,
  15. const TextureProperties& props)
  16. : VulkanResource(owner, false), mImage(image), mMemory(memory), mLayout(layout)
  17. {
  18. mImageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  19. mImageViewCI.pNext = nullptr;
  20. mImageViewCI.flags = 0;
  21. mImageViewCI.image = image;
  22. mImageViewCI.format = VulkanUtility::getPixelFormat(props.getFormat());
  23. mImageViewCI.components = {
  24. VK_COMPONENT_SWIZZLE_R,
  25. VK_COMPONENT_SWIZZLE_G,
  26. VK_COMPONENT_SWIZZLE_B,
  27. VK_COMPONENT_SWIZZLE_A
  28. };
  29. switch (props.getTextureType())
  30. {
  31. case TEX_TYPE_1D:
  32. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_1D;
  33. break;
  34. default:
  35. case TEX_TYPE_2D:
  36. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
  37. break;
  38. case TEX_TYPE_3D:
  39. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_3D;
  40. break;
  41. case TEX_TYPE_CUBE_MAP:
  42. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
  43. break;
  44. }
  45. if ((props.getUsage() & TU_DEPTHSTENCIL) != 0)
  46. mImageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
  47. else
  48. mImageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  49. TextureSurface completeSurface(0, props.getNumMipmaps() + 1, 0, props.getNumArraySlices());
  50. mMainView = createView(completeSurface);
  51. mNumFaces = props.getNumFaces();
  52. mNumMipLevels = props.getNumMipmaps();
  53. UINT32 numSubresources = mNumFaces * mNumMipLevels;
  54. mSubresources = (VulkanImageSubresource**)bs_alloc<VulkanImageSubresource*>(numSubresources);
  55. for (UINT32 i = 0; i < numSubresources; i++)
  56. mSubresources[i] = owner->create<VulkanImageSubresource>();
  57. }
  58. VulkanImage::~VulkanImage()
  59. {
  60. VulkanDevice& device = mOwner->getDevice();
  61. VkDevice vkDevice = device.getLogical();
  62. UINT32 numSubresources = mNumFaces * mNumMipLevels;
  63. for (UINT32 i = 0; i < numSubresources; i++)
  64. {
  65. assert(!mSubresources[i]->isBound()); // Image beeing freed but its subresources are still bound somewhere
  66. mSubresources[i]->destroy();
  67. }
  68. vkDestroyImageView(vkDevice, mMainView, gVulkanAllocator);
  69. for(auto& entry : mImageInfos)
  70. vkDestroyImageView(vkDevice, entry.view, gVulkanAllocator);
  71. vkDestroyImage(vkDevice, mImage, gVulkanAllocator);
  72. device.freeMemory(mMemory);
  73. }
  74. VkImageView VulkanImage::getView(const TextureSurface& surface) const
  75. {
  76. for(auto& entry : mImageInfos)
  77. {
  78. if (surface.mipLevel == entry.surface.mipLevel &&
  79. surface.numMipLevels == entry.surface.numMipLevels &&
  80. surface.arraySlice == entry.surface.arraySlice &&
  81. surface.numArraySlices == entry.surface.numArraySlices)
  82. {
  83. return entry.view;
  84. }
  85. }
  86. ImageViewInfo info;
  87. info.surface = surface;
  88. info.view = createView(surface);
  89. mImageInfos.push_back(info);
  90. return info.view;
  91. }
  92. VkImageView VulkanImage::createView(const TextureSurface& surface) const
  93. {
  94. VkImageViewType oldViewType = mImageViewCI.viewType;
  95. if(surface.numArraySlices > 1)
  96. {
  97. switch (oldViewType)
  98. {
  99. case VK_IMAGE_VIEW_TYPE_1D:
  100. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
  101. break;
  102. case VK_IMAGE_VIEW_TYPE_2D:
  103. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
  104. break;
  105. case VK_IMAGE_VIEW_TYPE_CUBE:
  106. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
  107. break;
  108. default:
  109. break;
  110. }
  111. }
  112. mImageViewCI.subresourceRange.baseMipLevel = surface.mipLevel;
  113. mImageViewCI.subresourceRange.levelCount = surface.numMipLevels;
  114. mImageViewCI.subresourceRange.baseArrayLayer = surface.arraySlice;
  115. mImageViewCI.subresourceRange.layerCount = surface.numArraySlices;
  116. VkImageView view;
  117. VkResult result = vkCreateImageView(mOwner->getDevice().getLogical(), &mImageViewCI, gVulkanAllocator, &view);
  118. assert(result == VK_SUCCESS);
  119. mImageViewCI.viewType = oldViewType;
  120. return view;
  121. }
  122. VulkanImageSubresource* VulkanImage::getSubresource(UINT32 face, UINT32 mipLevel)
  123. {
  124. return mSubresources[face * mNumMipLevels + mipLevel];
  125. }
  126. void VulkanImage::map(UINT32 face, UINT32 mipLevel, PixelData& output) const
  127. {
  128. VulkanDevice& device = mOwner->getDevice();
  129. VkImageSubresource range;
  130. range.mipLevel = mipLevel;
  131. range.arrayLayer = face;
  132. if (mImageViewCI.subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
  133. range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  134. else // Depth stencil, but we only map depth
  135. range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
  136. VkSubresourceLayout layout;
  137. vkGetImageSubresourceLayout(device.getLogical(), mImage, &range, &layout);
  138. assert(layout.size == output.getSize());
  139. output.setRowPitch(layout.rowPitch);
  140. output.setSlicePitch(layout.depthPitch);
  141. UINT8* data;
  142. VkResult result = vkMapMemory(device.getLogical(), mMemory, layout.offset, layout.size, 0, (void**)&data);
  143. assert(result == VK_SUCCESS);
  144. output.setExternalBuffer(data);
  145. }
  146. void VulkanImage::unmap()
  147. {
  148. VulkanDevice& device = mOwner->getDevice();
  149. vkUnmapMemory(device.getLogical(), mMemory);
  150. }
  151. void VulkanImage::copy(VulkanTransferBuffer* cb, VulkanBuffer* destination, const VkExtent3D& extent,
  152. const VkImageSubresourceLayers& range, VkImageLayout layout)
  153. {
  154. VkBufferImageCopy region;
  155. region.bufferRowLength = destination->getRowPitch();
  156. region.bufferImageHeight = destination->getSliceHeight();
  157. region.bufferOffset = 0;
  158. region.imageOffset.x = 0;
  159. region.imageOffset.y = 0;
  160. region.imageOffset.z = 0;
  161. region.imageExtent = extent;
  162. region.imageSubresource = range;
  163. vkCmdCopyImageToBuffer(cb->getCB()->getHandle(), mImage, layout, destination->getHandle(), 1, &region);
  164. }
  165. VulkanImageSubresource::VulkanImageSubresource(VulkanResourceManager* owner)
  166. :VulkanResource(owner, false)
  167. { }
  168. VulkanTextureCore::VulkanTextureCore(const TEXTURE_DESC& desc, const SPtr<PixelData>& initialData,
  169. GpuDeviceFlags deviceMask)
  170. : TextureCore(desc, initialData, deviceMask), mImages(), mDeviceMask(deviceMask), mAccessFlags(0)
  171. , mStagingBuffer(nullptr), mMappedDeviceIdx(-1), mMappedGlobalQueueIdx(-1), mMappedMip(0), mMappedFace(0)
  172. , mMappedRowPitch(false), mMappedSlicePitch(false), mMappedLockOptions(GBL_WRITE_ONLY), mDirectlyMappable(false)
  173. , mSupportsGPUWrites(false), mIsMapped(false)
  174. {
  175. }
  176. VulkanTextureCore::~VulkanTextureCore()
  177. {
  178. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  179. {
  180. if (mImages[i] == nullptr)
  181. return;
  182. mImages[i]->destroy();
  183. }
  184. assert(mStagingBuffer == nullptr);
  185. BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_Texture);
  186. }
  187. void VulkanTextureCore::initialize()
  188. {
  189. THROW_IF_NOT_CORE_THREAD;
  190. const TextureProperties& props = mProperties;
  191. mImageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  192. mImageCI.pNext = nullptr;
  193. mImageCI.flags = 0;
  194. // Note: If usage is dynamic I might consider creating a VK_IMAGE_TILING_LINEAR (if supported by the device)
  195. TextureType texType = props.getTextureType();
  196. switch(texType)
  197. {
  198. case TEX_TYPE_1D:
  199. mImageCI.imageType = VK_IMAGE_TYPE_1D;
  200. break;
  201. case TEX_TYPE_2D:
  202. mImageCI.imageType = VK_IMAGE_TYPE_2D;
  203. break;
  204. case TEX_TYPE_3D:
  205. mImageCI.imageType = VK_IMAGE_TYPE_3D;
  206. break;
  207. case TEX_TYPE_CUBE_MAP:
  208. mImageCI.imageType = VK_IMAGE_TYPE_2D;
  209. mImageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
  210. break;
  211. }
  212. int usage = props.getUsage();
  213. if ((usage & TU_RENDERTARGET) != 0)
  214. {
  215. mImageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
  216. mSupportsGPUWrites = true;
  217. mAccessFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  218. }
  219. else if ((usage & TU_DEPTHSTENCIL) != 0)
  220. {
  221. mImageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
  222. mSupportsGPUWrites = true;
  223. mAccessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
  224. }
  225. else if ((usage & TU_LOADSTORE) != 0)
  226. {
  227. mImageCI.usage = VK_IMAGE_USAGE_STORAGE_BIT;
  228. mSupportsGPUWrites = true;
  229. mAccessFlags = VK_ACCESS_SHADER_WRITE_BIT;
  230. }
  231. else
  232. {
  233. mImageCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
  234. mAccessFlags = VK_ACCESS_SHADER_READ_BIT;;
  235. }
  236. if ((usage & TU_CPUREADABLE) != 0)
  237. mImageCI.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
  238. VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
  239. VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
  240. if ((usage & TU_DYNAMIC) != 0) // Attempt to use linear tiling for dynamic textures, so we can directly map and modify them
  241. {
  242. // Only support 2D textures, with one sample and one mip level, only used for shader reads
  243. // (Optionally check vkGetPhysicalDeviceFormatProperties & vkGetPhysicalDeviceImageFormatProperties for
  244. // additional supported configs, but right now there doesn't seem to be any additional support)
  245. if(texType == TEX_TYPE_2D && props.getNumSamples() <= 1 && props.getNumMipmaps() == 0 &&
  246. props.getNumFaces() == 1 && (mImageCI.usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0)
  247. {
  248. mDirectlyMappable = true;
  249. tiling = VK_IMAGE_TILING_LINEAR;
  250. layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
  251. }
  252. }
  253. mImageCI.format = VulkanUtility::getPixelFormat(props.getFormat());
  254. mImageCI.extent = { props.getWidth(), props.getHeight(), props.getDepth() };
  255. mImageCI.mipLevels = props.getNumMipmaps() + 1;
  256. mImageCI.arrayLayers = props.getNumFaces();
  257. mImageCI.samples = VulkanUtility::getSampleFlags(props.getNumSamples());
  258. mImageCI.tiling = tiling;
  259. mImageCI.initialLayout = layout;
  260. mImageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  261. mImageCI.queueFamilyIndexCount = 0;
  262. mImageCI.pQueueFamilyIndices = nullptr;
  263. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
  264. VulkanDevice* devices[BS_MAX_DEVICES];
  265. VulkanUtility::getDevices(rapi, mDeviceMask, devices);
  266. // Allocate buffers per-device
  267. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  268. {
  269. if (devices[i] == nullptr)
  270. continue;
  271. mImages[i] = createImage(*devices[i]);
  272. }
  273. BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Texture);
  274. TextureCore::initialize();
  275. }
  276. VulkanImage* VulkanTextureCore::createImage(VulkanDevice& device)
  277. {
  278. bool directlyMappable = mImageCI.tiling == VK_IMAGE_TILING_LINEAR;
  279. VkMemoryPropertyFlags flags = directlyMappable ?
  280. (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) : // Note: Try using cached memory
  281. VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
  282. VkDevice vkDevice = device.getLogical();
  283. VkImage image;
  284. VkResult result = vkCreateImage(vkDevice, &mImageCI, gVulkanAllocator, &image);
  285. assert(result == VK_SUCCESS);
  286. VkMemoryRequirements memReqs;
  287. vkGetImageMemoryRequirements(vkDevice, image, &memReqs);
  288. VkDeviceMemory memory = device.allocateMemory(memReqs, flags);
  289. result = vkBindImageMemory(vkDevice, image, memory, 0);
  290. assert(result == VK_SUCCESS);
  291. return device.getResourceManager().create<VulkanImage>(image, memory, mImageCI.initialLayout, getProperties());
  292. }
  293. VulkanBuffer* VulkanTextureCore::createStaging(VulkanDevice& device, const PixelData& pixelData, bool readable)
  294. {
  295. VkBufferCreateInfo bufferCI;
  296. bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
  297. bufferCI.pNext = nullptr;
  298. bufferCI.flags = 0;
  299. bufferCI.size = pixelData.getSize();
  300. bufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  301. bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
  302. bufferCI.queueFamilyIndexCount = 0;
  303. bufferCI.pQueueFamilyIndices = nullptr;
  304. if (readable)
  305. bufferCI.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
  306. VkDevice vkDevice = device.getLogical();
  307. VkBuffer buffer;
  308. VkResult result = vkCreateBuffer(vkDevice, &bufferCI, gVulkanAllocator, &buffer);
  309. assert(result == VK_SUCCESS);
  310. VkMemoryRequirements memReqs;
  311. vkGetBufferMemoryRequirements(vkDevice, buffer, &memReqs);
  312. VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
  313. VkDeviceMemory memory = device.allocateMemory(memReqs, flags);
  314. result = vkBindBufferMemory(vkDevice, buffer, memory, 0);
  315. assert(result == VK_SUCCESS);
  316. return device.getResourceManager().create<VulkanBuffer>(buffer, VK_NULL_HANDLE, memory,
  317. pixelData.getRowPitch(), pixelData.getSlicePitch());
  318. }
  319. VkImageView VulkanTextureCore::getView(UINT32 deviceIdx) const
  320. {
  321. if (mImages[deviceIdx] == nullptr)
  322. return VK_NULL_HANDLE;
  323. return mImages[deviceIdx]->getView();
  324. }
  325. VkImageView VulkanTextureCore::getView(UINT32 deviceIdx, const TextureSurface& surface) const
  326. {
  327. if (mImages[deviceIdx] == nullptr)
  328. return VK_NULL_HANDLE;
  329. return mImages[deviceIdx]->getView(surface);
  330. }
  331. void VulkanTextureCore::copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel,
  332. const SPtr<TextureCore>& target, UINT32 queueIdx)
  333. {
  334. // TODO - Handle resolve here as well
  335. }
  336. PixelData VulkanTextureCore::lockImpl(GpuLockOptions options, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx,
  337. UINT32 queueIdx)
  338. {
  339. const TextureProperties& props = getProperties();
  340. if (props.getNumSamples() > 1)
  341. {
  342. LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
  343. return PixelData();
  344. }
  345. #if BS_PROFILING_ENABLED
  346. if (options == GBL_READ_ONLY || options == GBL_READ_WRITE)
  347. {
  348. BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_Texture);
  349. }
  350. if (options == GBL_READ_WRITE || options == GBL_WRITE_ONLY || options == GBL_WRITE_ONLY_DISCARD || options == GBL_WRITE_ONLY_NO_OVERWRITE)
  351. {
  352. BS_INC_RENDER_STAT_CAT(ResWrite, RenderStatObject_Texture);
  353. }
  354. #endif
  355. UINT32 mipWidth = std::max(1u, props.getWidth() >> mipLevel);
  356. UINT32 mipHeight = std::max(1u, props.getHeight() >> mipLevel);
  357. UINT32 mipDepth = std::max(1u, props.getDepth() >> mipLevel);
  358. PixelData lockedArea(mipWidth, mipHeight, mipDepth, props.getFormat());
  359. VulkanImage* image = mImages[deviceIdx];
  360. if (image == nullptr)
  361. return PixelData();
  362. mIsMapped = true;
  363. mMappedDeviceIdx = deviceIdx;
  364. mMappedGlobalQueueIdx = queueIdx;
  365. mMappedFace = face;
  366. mMappedMip = mipLevel;
  367. mMappedLockOptions = options;
  368. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
  369. VulkanDevice& device = *rapi._getDevice(deviceIdx);
  370. VulkanCommandBufferManager& cbManager = gVulkanCBManager();
  371. GpuQueueType queueType;
  372. UINT32 localQueueIdx = CommandSyncMask::getQueueIdxAndType(queueIdx, queueType);
  373. VulkanImageSubresource* subresource = image->getSubresource(face, mipLevel);
  374. // If memory is host visible try mapping it directly
  375. if (mDirectlyMappable)
  376. {
  377. // Initially the texture will be in preinitialized layout, and it will transition to general layout on first
  378. // use in shader. No further transitions are allowed for directly mappable textures.
  379. assert(image->getLayout() == VK_IMAGE_LAYOUT_PREINITIALIZED || image->getLayout() == VK_IMAGE_LAYOUT_GENERAL);
  380. // GPU should never be allowed to write to a directly mappable texture, since only linear tiling is supported
  381. // for direct mapping, and we don't support using it with either storage textures or render targets.
  382. assert(!mSupportsGPUWrites);
  383. // Check is the GPU currently reading from the image
  384. UINT32 useMask = subresource->getUseInfo(VulkanUseFlag::Read);
  385. bool isUsedOnGPU = useMask != 0;
  386. // We're safe to map directly since GPU isn't using the subresource
  387. if (!isUsedOnGPU)
  388. {
  389. image->map(face, mipLevel, lockedArea);
  390. return lockedArea;
  391. }
  392. // Caller guarantees he won't touch the same data as the GPU, so just map even though the GPU is using the
  393. // subresource
  394. if (options == GBL_WRITE_ONLY_NO_OVERWRITE)
  395. {
  396. image->map(face, mipLevel, lockedArea);
  397. return lockedArea;
  398. }
  399. // No GPU writes are are supported and we're only reading, so no need to wait on anything
  400. if (options == GBL_READ_ONLY)
  401. {
  402. image->map(face, mipLevel, lockedArea);
  403. return lockedArea;
  404. }
  405. // Caller doesn't care about buffer contents, so just discard the existing buffer and create a new one
  406. if (options == GBL_WRITE_ONLY_DISCARD)
  407. {
  408. // We need to discard the entire image, even though we're only writing to a single sub-resource
  409. image->destroy();
  410. image = createImage(device);
  411. mImages[deviceIdx] = image;
  412. image->map(face, mipLevel, lockedArea);
  413. return lockedArea;
  414. }
  415. // We need to both read and write, meaning we need to wait until existing reads complete before we return
  416. if (options == GBL_READ_WRITE)
  417. {
  418. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(deviceIdx, queueType, localQueueIdx);
  419. // Ensure flush() will wait for all queues currently reading from the texture to finish
  420. transferCB->appendMask(useMask);
  421. // Submit the command buffer and wait until it finishes
  422. transferCB->flush(true);
  423. image->map(face, mipLevel, lockedArea);
  424. return lockedArea;
  425. }
  426. // Otherwise, we're doing write only, in which case it's best to use the staging buffer to avoid waiting
  427. // and blocking, so fall through
  428. }
  429. bool needRead = options == GBL_READ_WRITE || options == GBL_READ_ONLY;
  430. // Allocate a staging buffer
  431. mStagingBuffer = createStaging(device, lockedArea, needRead);
  432. if (needRead) // If reading, we need to copy the current contents of the image to the staging buffer
  433. {
  434. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(deviceIdx, queueType, localQueueIdx);
  435. // Similar to above, if image supports GPU writes, we need to wait on any potential writes to complete
  436. if (mSupportsGPUWrites)
  437. {
  438. // Ensure flush() will wait for all queues currently writing to the image (if any) to finish
  439. UINT32 writeUseMask = subresource->getUseInfo(VulkanUseFlag::Write);
  440. transferCB->appendMask(writeUseMask);
  441. }
  442. VkImageSubresourceRange range;
  443. if ((props.getUsage() & TU_DEPTHSTENCIL) != 0)
  444. range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
  445. else
  446. range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  447. range.baseArrayLayer = face;
  448. range.layerCount = 1;
  449. range.baseMipLevel = mipLevel;
  450. range.levelCount = 1;
  451. VkImageSubresourceLayers rangeLayers;
  452. if ((props.getUsage() & TU_DEPTHSTENCIL) != 0)
  453. rangeLayers.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
  454. else
  455. rangeLayers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  456. rangeLayers.baseArrayLayer = range.baseArrayLayer;
  457. rangeLayers.layerCount = range.layerCount;
  458. rangeLayers.mipLevel = range.baseMipLevel;
  459. VkExtent3D extent;
  460. PixelUtil::getSizeForMipLevel(props.getWidth(), props.getHeight(), props.getDepth(), mMappedMip,
  461. extent.width, extent.height, extent.depth);
  462. // Transfer texture to a valid layout
  463. transferCB->setLayout(image->getHandle(), mAccessFlags, VK_ACCESS_TRANSFER_READ_BIT, image->getLayout(),
  464. VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, range);
  465. // Queue copy command
  466. image->copy(transferCB, mStagingBuffer, extent, rangeLayers, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
  467. // Transfer back to original layout
  468. transferCB->setLayout(image->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, mAccessFlags,
  469. VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image->getLayout(), range);
  470. // Ensure data written to the staging buffer is visible
  471. VkAccessFlags stagingAccessFlags;
  472. if (options == GBL_READ_ONLY)
  473. stagingAccessFlags = VK_ACCESS_HOST_READ_BIT;
  474. else // Must be read/write
  475. stagingAccessFlags = VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT;
  476. transferCB->memoryBarrier(mStagingBuffer->getHandle(),
  477. VK_ACCESS_TRANSFER_WRITE_BIT,
  478. stagingAccessFlags,
  479. VK_PIPELINE_STAGE_TRANSFER_BIT,
  480. VK_PIPELINE_STAGE_HOST_BIT);
  481. // Submit the command buffer and wait until it finishes
  482. transferCB->flush(true);
  483. }
  484. UINT8* data = mStagingBuffer->map(0, lockedArea.getSize());
  485. lockedArea.setExternalBuffer(data);
  486. return lockedArea;
  487. }
  488. void VulkanTextureCore::unlockImpl()
  489. {
  490. // Possibly map() failed with some error
  491. if (!mIsMapped)
  492. return;
  493. // Note: If we did any writes they need to be made visible to the GPU. However there is no need to execute
  494. // a pipeline barrier because (as per spec) host writes are implicitly visible to the device.
  495. if (mStagingBuffer == nullptr)
  496. mImages[mMappedDeviceIdx]->unmap();
  497. else
  498. {
  499. bool isWrite = mMappedLockOptions != GBL_READ_ONLY;
  500. // We the caller wrote anything to the staging buffer, we need to upload it back to the main buffer
  501. if (isWrite)
  502. {
  503. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
  504. VulkanDevice& device = *rapi._getDevice(mMappedDeviceIdx);
  505. VulkanCommandBufferManager& cbManager = gVulkanCBManager();
  506. GpuQueueType queueType;
  507. UINT32 localQueueIdx = CommandSyncMask::getQueueIdxAndType(mMappedGlobalQueueIdx, queueType);
  508. VulkanImage* image = mImages[mMappedDeviceIdx];
  509. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(mMappedDeviceIdx, queueType, localQueueIdx);
  510. VulkanImageSubresource* subresource = image->getSubresource(mMappedFace, mMappedMip);
  511. // If the subresource is used in any way on the GPU, we need to wait for that use to finish before
  512. // we issue our copy
  513. UINT32 useMask = subresource->getUseInfo(VulkanUseFlag::Read | VulkanUseFlag::Write);
  514. if (useMask != 0) // Subresource is currently used on the GPU
  515. {
  516. // Try to avoid the wait
  517. // Caller guarantees he won't touch the same data as the GPU, so just copy
  518. if (mMappedLockOptions == GBL_WRITE_ONLY_NO_OVERWRITE)
  519. {
  520. // Fall through to copy()
  521. }
  522. // Caller doesn't care about buffer contents, so just discard the existing buffer and create a new one
  523. else if (mMappedLockOptions == GBL_WRITE_ONLY_DISCARD)
  524. {
  525. // We need to discard the entire image, even though we're only writing to a single sub-resource
  526. image->destroy();
  527. image = createImage(device);
  528. mImages[mMappedDeviceIdx] = image;
  529. subresource = image->getSubresource(mMappedFace, mMappedMip);
  530. }
  531. else // Otherwise we have no choice but to issue a dependency between the queues
  532. transferCB->appendMask(useMask);
  533. }
  534. const TextureProperties& props = getProperties();
  535. VkImageSubresourceRange range;
  536. if ((props.getUsage() & TU_DEPTHSTENCIL) != 0)
  537. range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
  538. else
  539. range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  540. range.baseArrayLayer = mMappedFace;
  541. range.layerCount = 1;
  542. range.baseMipLevel = mMappedMip;
  543. range.levelCount = 1;
  544. VkImageSubresourceLayers rangeLayers;
  545. rangeLayers.aspectMask = range.aspectMask;
  546. rangeLayers.baseArrayLayer = range.baseArrayLayer;
  547. rangeLayers.layerCount = range.layerCount;
  548. rangeLayers.mipLevel = range.baseMipLevel;
  549. VkExtent3D extent;
  550. PixelUtil::getSizeForMipLevel(props.getWidth(), props.getHeight(), props.getDepth(), mMappedMip,
  551. extent.width, extent.height, extent.depth);
  552. VkImageLayout transferLayout;
  553. if (mDirectlyMappable)
  554. transferLayout = VK_IMAGE_LAYOUT_GENERAL;
  555. else
  556. transferLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
  557. // Transfer texture to a valid layout
  558. transferCB->setLayout(image->getHandle(), mAccessFlags, VK_ACCESS_TRANSFER_WRITE_BIT, image->getLayout(),
  559. transferLayout, range);
  560. // Queue copy command
  561. mStagingBuffer->copy(transferCB, image, extent, rangeLayers, transferLayout);
  562. // Transfer back to original layout
  563. transferCB->setLayout(image->getHandle(), VK_ACCESS_TRANSFER_WRITE_BIT, mAccessFlags,
  564. transferLayout, image->getLayout(), range);
  565. // Notify the command buffer that these resources are being used on it
  566. transferCB->getCB()->registerResource(mStagingBuffer, VK_ACCESS_TRANSFER_READ_BIT, VulkanUseFlag::Read);
  567. transferCB->getCB()->registerResource(image, mAccessFlags, image->getLayout(), range, VulkanUseFlag::Write);
  568. // We don't actually flush the transfer buffer here since it's an expensive operation, but it's instead
  569. // done automatically before next "normal" command buffer submission.
  570. }
  571. mStagingBuffer->unmap();
  572. mStagingBuffer->destroy();
  573. mStagingBuffer = nullptr;
  574. }
  575. mIsMapped = false;
  576. }
  577. void VulkanTextureCore::readDataImpl(PixelData& dest, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx, UINT32 queueIdx)
  578. {
  579. if (mProperties.getNumSamples() > 1)
  580. {
  581. LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
  582. return;
  583. }
  584. PixelData myData = lock(GBL_READ_ONLY, mipLevel, face, deviceIdx, queueIdx);
  585. #if BS_DEBUG_MODE
  586. if (dest.getConsecutiveSize() != myData.getConsecutiveSize())
  587. {
  588. unlock();
  589. BS_EXCEPT(InternalErrorException, "Buffer sizes don't match");
  590. }
  591. #endif
  592. PixelUtil::bulkPixelConversion(myData, dest);
  593. unlock();
  594. BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_Texture);
  595. }
  596. void VulkanTextureCore::writeDataImpl(const PixelData& src, UINT32 mipLevel, UINT32 face, bool discardWholeBuffer,
  597. UINT32 queueIdx)
  598. {
  599. if (mProperties.getNumSamples() > 1)
  600. {
  601. LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
  602. return;
  603. }
  604. PixelFormat format = mProperties.getFormat();
  605. mipLevel = Math::clamp(mipLevel, (UINT32)mipLevel, mProperties.getNumMipmaps());
  606. face = Math::clamp(face, (UINT32)0, mProperties.getNumFaces() - 1);
  607. if (face > 0 && mProperties.getTextureType() == TEX_TYPE_3D)
  608. {
  609. LOGERR("3D texture arrays are not supported.");
  610. return;
  611. }
  612. // Write to every device
  613. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  614. {
  615. if (mImages[i] == nullptr)
  616. continue;
  617. PixelData myData = lock(discardWholeBuffer ? GBL_WRITE_ONLY_DISCARD : GBL_WRITE_ONLY, mipLevel, face, i,
  618. queueIdx);
  619. PixelUtil::bulkPixelConversion(src, myData);
  620. unlock();
  621. }
  622. BS_INC_RENDER_STAT_CAT(ResWrite, RenderStatObject_Texture);
  623. }
  624. }