BsVulkanTexture.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  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. VULKAN_IMAGE_DESC createDesc(VkImage image, VkDeviceMemory memory, VkImageLayout layout, const TextureProperties& props)
  15. {
  16. VULKAN_IMAGE_DESC desc;
  17. desc.image = image;
  18. desc.memory = memory;
  19. desc.type = props.getTextureType();
  20. desc.format = VulkanUtility::getPixelFormat(props.getFormat());
  21. desc.numFaces = props.getNumFaces();
  22. desc.numMipLevels = props.getNumMipmaps() + 1;
  23. desc.isDepthStencil = (props.getUsage() & TU_DEPTHSTENCIL) != 0;
  24. desc.isStorage = (props.getUsage() & TU_LOADSTORE) != 0;
  25. desc.layout = layout;
  26. return desc;
  27. }
  28. VulkanImage::VulkanImage(VulkanResourceManager* owner, VkImage image, VkDeviceMemory memory, VkImageLayout layout,
  29. const TextureProperties& props, bool ownsImage)
  30. : VulkanImage(owner, createDesc(image, memory, layout, props), ownsImage)
  31. { }
  32. VulkanImage::VulkanImage(VulkanResourceManager* owner, const VULKAN_IMAGE_DESC& desc, bool ownsImage)
  33. : VulkanResource(owner, false), mImage(desc.image), mMemory(desc.memory), mLayout(desc.layout)
  34. , mFramebufferMainView(VK_NULL_HANDLE), mOwnsImage(ownsImage), mNumFaces(desc.numFaces)
  35. , mNumMipLevels(desc.numMipLevels), mIsStorage(desc.isStorage)
  36. {
  37. mImageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  38. mImageViewCI.pNext = nullptr;
  39. mImageViewCI.flags = 0;
  40. mImageViewCI.image = desc.image;
  41. mImageViewCI.format = desc.format;
  42. mImageViewCI.components = {
  43. VK_COMPONENT_SWIZZLE_R,
  44. VK_COMPONENT_SWIZZLE_G,
  45. VK_COMPONENT_SWIZZLE_B,
  46. VK_COMPONENT_SWIZZLE_A
  47. };
  48. switch (desc.type)
  49. {
  50. case TEX_TYPE_1D:
  51. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_1D;
  52. break;
  53. default:
  54. case TEX_TYPE_2D:
  55. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
  56. break;
  57. case TEX_TYPE_3D:
  58. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_3D;
  59. break;
  60. case TEX_TYPE_CUBE_MAP:
  61. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
  62. break;
  63. }
  64. mIsDepthStencil = desc.isDepthStencil;
  65. TextureSurface completeSurface(0, desc.numMipLevels, 0, desc.numFaces);
  66. if (mIsDepthStencil)
  67. {
  68. mFramebufferMainView = createView(completeSurface, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
  69. mMainView = createView(completeSurface, VK_IMAGE_ASPECT_DEPTH_BIT);
  70. }
  71. else
  72. mMainView = createView(completeSurface, VK_IMAGE_ASPECT_COLOR_BIT);
  73. UINT32 numSubresources = mNumFaces * mNumMipLevels;
  74. mSubresources = (VulkanImageSubresource**)bs_alloc(sizeof(VulkanImageSubresource*) * numSubresources);
  75. for (UINT32 i = 0; i < numSubresources; i++)
  76. mSubresources[i] = owner->create<VulkanImageSubresource>();
  77. }
  78. VulkanImage::~VulkanImage()
  79. {
  80. VulkanDevice& device = mOwner->getDevice();
  81. VkDevice vkDevice = device.getLogical();
  82. UINT32 numSubresources = mNumFaces * mNumMipLevels;
  83. for (UINT32 i = 0; i < numSubresources; i++)
  84. {
  85. assert(!mSubresources[i]->isBound()); // Image beeing freed but its subresources are still bound somewhere
  86. mSubresources[i]->destroy();
  87. }
  88. vkDestroyImageView(vkDevice, mMainView, gVulkanAllocator);
  89. if(mFramebufferMainView != VK_NULL_HANDLE)
  90. vkDestroyImageView(vkDevice, mFramebufferMainView, gVulkanAllocator);
  91. for(auto& entry : mImageInfos)
  92. vkDestroyImageView(vkDevice, entry.view, gVulkanAllocator);
  93. if (mOwnsImage)
  94. {
  95. vkDestroyImage(vkDevice, mImage, gVulkanAllocator);
  96. device.freeMemory(mMemory);
  97. }
  98. }
  99. VkImageView VulkanImage::getView(bool framebuffer) const
  100. {
  101. if (!mIsDepthStencil || !framebuffer)
  102. return mMainView;
  103. return mFramebufferMainView;
  104. }
  105. VkImageView VulkanImage::getView(const TextureSurface& surface, bool framebuffer) const
  106. {
  107. for(auto& entry : mImageInfos)
  108. {
  109. if (surface.mipLevel == entry.surface.mipLevel &&
  110. surface.numMipLevels == entry.surface.numMipLevels &&
  111. surface.arraySlice == entry.surface.arraySlice &&
  112. surface.numArraySlices == entry.surface.numArraySlices)
  113. {
  114. if(!mIsDepthStencil)
  115. return entry.view;
  116. else
  117. {
  118. if (framebuffer == entry.framebuffer)
  119. return entry.view;
  120. }
  121. }
  122. }
  123. ImageViewInfo info;
  124. info.surface = surface;
  125. info.framebuffer = framebuffer;
  126. if (mIsDepthStencil)
  127. {
  128. if(framebuffer)
  129. info.view = createView(surface, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
  130. else
  131. info.view = createView(surface, VK_IMAGE_ASPECT_DEPTH_BIT);
  132. }
  133. else
  134. info.view = createView(surface, VK_IMAGE_ASPECT_COLOR_BIT);
  135. mImageInfos.push_back(info);
  136. return info.view;
  137. }
  138. VkImageView VulkanImage::createView(const TextureSurface& surface, VkImageAspectFlags aspectMask) const
  139. {
  140. VkImageViewType oldViewType = mImageViewCI.viewType;
  141. if(surface.numArraySlices > 1)
  142. {
  143. switch (oldViewType)
  144. {
  145. case VK_IMAGE_VIEW_TYPE_1D:
  146. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
  147. break;
  148. case VK_IMAGE_VIEW_TYPE_2D:
  149. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
  150. break;
  151. case VK_IMAGE_VIEW_TYPE_CUBE:
  152. mImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
  153. break;
  154. default:
  155. break;
  156. }
  157. }
  158. mImageViewCI.subresourceRange.aspectMask = aspectMask;
  159. mImageViewCI.subresourceRange.baseMipLevel = surface.mipLevel;
  160. mImageViewCI.subresourceRange.levelCount = surface.numMipLevels;
  161. mImageViewCI.subresourceRange.baseArrayLayer = surface.arraySlice;
  162. mImageViewCI.subresourceRange.layerCount = surface.numArraySlices;
  163. VkImageView view;
  164. VkResult result = vkCreateImageView(mOwner->getDevice().getLogical(), &mImageViewCI, gVulkanAllocator, &view);
  165. assert(result == VK_SUCCESS);
  166. mImageViewCI.viewType = oldViewType;
  167. return view;
  168. }
  169. VkImageSubresourceRange VulkanImage::getRange() const
  170. {
  171. VkImageSubresourceRange range;
  172. range.aspectMask = mImageViewCI.subresourceRange.aspectMask;
  173. range.baseArrayLayer = 0;
  174. range.layerCount = mNumFaces;
  175. range.baseMipLevel = 0;
  176. range.levelCount = mNumMipLevels;
  177. return range;
  178. }
  179. VulkanImageSubresource* VulkanImage::getSubresource(UINT32 face, UINT32 mipLevel)
  180. {
  181. return mSubresources[face * mNumMipLevels + mipLevel];
  182. }
  183. void VulkanImage::map(UINT32 face, UINT32 mipLevel, PixelData& output) const
  184. {
  185. VulkanDevice& device = mOwner->getDevice();
  186. VkImageSubresource range;
  187. range.mipLevel = mipLevel;
  188. range.arrayLayer = face;
  189. if (mImageViewCI.subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
  190. range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  191. else // Depth stencil, but we only map depth
  192. range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
  193. VkSubresourceLayout layout;
  194. vkGetImageSubresourceLayout(device.getLogical(), mImage, &range, &layout);
  195. assert(layout.size == output.getSize());
  196. output.setRowPitch((UINT32)layout.rowPitch);
  197. output.setSlicePitch((UINT32)layout.depthPitch);
  198. UINT8* data;
  199. VkResult result = vkMapMemory(device.getLogical(), mMemory, layout.offset, layout.size, 0, (void**)&data);
  200. assert(result == VK_SUCCESS);
  201. output.setExternalBuffer(data);
  202. }
  203. UINT8* VulkanImage::map(UINT32 offset, UINT32 size) const
  204. {
  205. VulkanDevice& device = mOwner->getDevice();
  206. UINT8* data;
  207. VkResult result = vkMapMemory(device.getLogical(), mMemory, offset, size, 0, (void**)&data);
  208. assert(result == VK_SUCCESS);
  209. return data;
  210. }
  211. void VulkanImage::unmap()
  212. {
  213. VulkanDevice& device = mOwner->getDevice();
  214. vkUnmapMemory(device.getLogical(), mMemory);
  215. }
  216. void VulkanImage::copy(VulkanTransferBuffer* cb, VulkanBuffer* destination, const VkExtent3D& extent,
  217. const VkImageSubresourceLayers& range, VkImageLayout layout)
  218. {
  219. VkBufferImageCopy region;
  220. region.bufferRowLength = destination->getRowPitch();
  221. region.bufferImageHeight = destination->getSliceHeight();
  222. region.bufferOffset = 0;
  223. region.imageOffset.x = 0;
  224. region.imageOffset.y = 0;
  225. region.imageOffset.z = 0;
  226. region.imageExtent = extent;
  227. region.imageSubresource = range;
  228. vkCmdCopyImageToBuffer(cb->getCB()->getHandle(), mImage, layout, destination->getHandle(), 1, &region);
  229. }
  230. VkAccessFlags VulkanImage::getAccessFlags(VkImageLayout layout)
  231. {
  232. VkAccessFlags accessFlags;
  233. switch (layout)
  234. {
  235. case VK_IMAGE_LAYOUT_GENERAL: // Only used for render targets that are also read by shaders, or for storage textures
  236. if (mIsStorage)
  237. accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
  238. else
  239. accessFlags = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
  240. break;
  241. case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
  242. accessFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
  243. break;
  244. case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
  245. accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
  246. break;
  247. case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
  248. accessFlags = VK_ACCESS_SHADER_READ_BIT;
  249. break;
  250. case VK_IMAGE_LAYOUT_UNDEFINED:
  251. case VK_IMAGE_LAYOUT_PREINITIALIZED:
  252. accessFlags = 0;
  253. break;
  254. default:
  255. accessFlags = 0;
  256. LOGWRN("Unsupported source layout for Vulkan image.");
  257. break;
  258. }
  259. return accessFlags;
  260. }
  261. VulkanImageSubresource::VulkanImageSubresource(VulkanResourceManager* owner)
  262. :VulkanResource(owner, false)
  263. { }
  264. VulkanTextureCore::VulkanTextureCore(const TEXTURE_DESC& desc, const SPtr<PixelData>& initialData,
  265. GpuDeviceFlags deviceMask)
  266. : TextureCore(desc, initialData, deviceMask), mImages(), mDeviceMask(deviceMask), mStagingBuffer(nullptr)
  267. , mMappedDeviceIdx(-1), mMappedGlobalQueueIdx(-1), mMappedMip(0), mMappedFace(0), mMappedRowPitch(false)
  268. , mMappedSlicePitch(false), mMappedLockOptions(GBL_WRITE_ONLY), mDirectlyMappable(false), mSupportsGPUWrites(false)
  269. , mIsMapped(false)
  270. {
  271. }
  272. VulkanTextureCore::~VulkanTextureCore()
  273. {
  274. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  275. {
  276. if (mImages[i] == nullptr)
  277. return;
  278. mImages[i]->destroy();
  279. }
  280. assert(mStagingBuffer == nullptr);
  281. BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_Texture);
  282. }
  283. void VulkanTextureCore::initialize()
  284. {
  285. THROW_IF_NOT_CORE_THREAD;
  286. const TextureProperties& props = mProperties;
  287. mImageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  288. mImageCI.pNext = nullptr;
  289. mImageCI.flags = 0;
  290. TextureType texType = props.getTextureType();
  291. switch(texType)
  292. {
  293. case TEX_TYPE_1D:
  294. mImageCI.imageType = VK_IMAGE_TYPE_1D;
  295. break;
  296. case TEX_TYPE_2D:
  297. mImageCI.imageType = VK_IMAGE_TYPE_2D;
  298. break;
  299. case TEX_TYPE_3D:
  300. mImageCI.imageType = VK_IMAGE_TYPE_3D;
  301. break;
  302. case TEX_TYPE_CUBE_MAP:
  303. mImageCI.imageType = VK_IMAGE_TYPE_2D;
  304. mImageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
  305. break;
  306. }
  307. // Note: I force rendertarget and depthstencil types to be readable in shader. Depending on performance impact
  308. // it might be beneficial to allow the user to enable this explicitly only when needed.
  309. int usage = props.getUsage();
  310. if ((usage & TU_RENDERTARGET) != 0)
  311. {
  312. mImageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
  313. mSupportsGPUWrites = true;
  314. }
  315. else if ((usage & TU_DEPTHSTENCIL) != 0)
  316. {
  317. mImageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
  318. mSupportsGPUWrites = true;
  319. }
  320. else if ((usage & TU_LOADSTORE) != 0)
  321. {
  322. mImageCI.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
  323. mSupportsGPUWrites = true;
  324. }
  325. else
  326. {
  327. mImageCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
  328. }
  329. mImageCI.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
  330. VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
  331. VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
  332. if ((usage & TU_DYNAMIC) != 0) // Attempt to use linear tiling for dynamic textures, so we can directly map and modify them
  333. {
  334. // Only support 2D textures, with one sample and one mip level, only used for shader reads
  335. // (Optionally check vkGetPhysicalDeviceFormatProperties & vkGetPhysicalDeviceImageFormatProperties for
  336. // additional supported configs, but right now there doesn't seem to be any additional support)
  337. if(texType == TEX_TYPE_2D && props.getNumSamples() <= 1 && props.getNumMipmaps() == 0 &&
  338. props.getNumFaces() == 1 && (mImageCI.usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0)
  339. {
  340. // Also, only support normal textures, not render targets or storage textures
  341. if (!mSupportsGPUWrites)
  342. {
  343. mDirectlyMappable = true;
  344. tiling = VK_IMAGE_TILING_LINEAR;
  345. layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
  346. }
  347. }
  348. }
  349. mImageCI.format = VulkanUtility::getPixelFormat(props.getFormat());
  350. mImageCI.extent = { props.getWidth(), props.getHeight(), props.getDepth() };
  351. mImageCI.mipLevels = props.getNumMipmaps() + 1;
  352. mImageCI.arrayLayers = props.getNumFaces();
  353. mImageCI.samples = VulkanUtility::getSampleFlags(props.getNumSamples());
  354. mImageCI.tiling = tiling;
  355. mImageCI.initialLayout = layout;
  356. mImageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  357. mImageCI.queueFamilyIndexCount = 0;
  358. mImageCI.pQueueFamilyIndices = nullptr;
  359. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
  360. VulkanDevice* devices[BS_MAX_DEVICES];
  361. VulkanUtility::getDevices(rapi, mDeviceMask, devices);
  362. // Allocate buffers per-device
  363. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  364. {
  365. if (devices[i] == nullptr)
  366. continue;
  367. mImages[i] = createImage(*devices[i]);
  368. }
  369. BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Texture);
  370. TextureCore::initialize();
  371. }
  372. VulkanImage* VulkanTextureCore::createImage(VulkanDevice& device)
  373. {
  374. bool directlyMappable = mImageCI.tiling == VK_IMAGE_TILING_LINEAR;
  375. VkMemoryPropertyFlags flags = directlyMappable ?
  376. (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) : // Note: Try using cached memory
  377. VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
  378. VkDevice vkDevice = device.getLogical();
  379. VkImage image;
  380. VkResult result = vkCreateImage(vkDevice, &mImageCI, gVulkanAllocator, &image);
  381. assert(result == VK_SUCCESS);
  382. VkMemoryRequirements memReqs;
  383. vkGetImageMemoryRequirements(vkDevice, image, &memReqs);
  384. VkDeviceMemory memory = device.allocateMemory(memReqs, flags);
  385. result = vkBindImageMemory(vkDevice, image, memory, 0);
  386. assert(result == VK_SUCCESS);
  387. return device.getResourceManager().create<VulkanImage>(image, memory, mImageCI.initialLayout, getProperties());
  388. }
  389. VulkanBuffer* VulkanTextureCore::createStaging(VulkanDevice& device, const PixelData& pixelData, bool readable)
  390. {
  391. VkBufferCreateInfo bufferCI;
  392. bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
  393. bufferCI.pNext = nullptr;
  394. bufferCI.flags = 0;
  395. bufferCI.size = pixelData.getSize();
  396. bufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  397. bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
  398. bufferCI.queueFamilyIndexCount = 0;
  399. bufferCI.pQueueFamilyIndices = nullptr;
  400. if (readable)
  401. bufferCI.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
  402. VkDevice vkDevice = device.getLogical();
  403. VkBuffer buffer;
  404. VkResult result = vkCreateBuffer(vkDevice, &bufferCI, gVulkanAllocator, &buffer);
  405. assert(result == VK_SUCCESS);
  406. VkMemoryRequirements memReqs;
  407. vkGetBufferMemoryRequirements(vkDevice, buffer, &memReqs);
  408. VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
  409. VkDeviceMemory memory = device.allocateMemory(memReqs, flags);
  410. result = vkBindBufferMemory(vkDevice, buffer, memory, 0);
  411. assert(result == VK_SUCCESS);
  412. VkBufferView view = VK_NULL_HANDLE;
  413. return device.getResourceManager().create<VulkanBuffer>(buffer, view, memory,
  414. pixelData.getRowPitch(), pixelData.getSlicePitch());
  415. }
  416. void VulkanTextureCore::copyImage(VulkanTransferBuffer* cb, VulkanImage* srcImage, VulkanImage* dstImage,
  417. VkImageLayout srcFinalLayout, VkImageLayout dstFinalLayout)
  418. {
  419. UINT32 numFaces = mProperties.getNumFaces();
  420. UINT32 numMipmaps = mProperties.getNumMipmaps() + 1;
  421. UINT32 mipWidth = mProperties.getWidth();
  422. UINT32 mipHeight = mProperties.getHeight();
  423. UINT32 mipDepth = mProperties.getDepth();
  424. VkImageCopy* imageRegions = bs_stack_alloc<VkImageCopy>(numMipmaps);
  425. for(UINT32 i = 0; i < numMipmaps; i++)
  426. {
  427. VkImageCopy& imageRegion = imageRegions[i];
  428. imageRegion.srcOffset = { 0, 0, 0 };
  429. imageRegion.dstOffset = { 0, 0, 0 };
  430. imageRegion.extent = { mipWidth, mipHeight, mipDepth };
  431. imageRegion.srcSubresource.baseArrayLayer = 0;
  432. imageRegion.srcSubresource.layerCount = numFaces;
  433. imageRegion.srcSubresource.mipLevel = i;
  434. imageRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  435. imageRegion.dstSubresource.baseArrayLayer = 0;
  436. imageRegion.dstSubresource.layerCount = numFaces;
  437. imageRegion.dstSubresource.mipLevel = i;
  438. imageRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  439. if (mipWidth != 1) mipWidth /= 2;
  440. if (mipHeight != 1) mipHeight /= 2;
  441. if (mipDepth != 1) mipDepth /= 2;
  442. }
  443. VkImageSubresourceRange range;
  444. range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  445. range.baseArrayLayer = 0;
  446. range.layerCount = numFaces;
  447. range.baseMipLevel = 0;
  448. range.levelCount = numMipmaps;
  449. VkAccessFlags srcAccessMask = srcImage->getAccessFlags(srcImage->getLayout());
  450. VkAccessFlags dstAccessMask = dstImage->getAccessFlags(dstImage->getLayout());
  451. VkImageLayout transferSrcLayout, transferDstLayout;
  452. if (mDirectlyMappable)
  453. {
  454. transferSrcLayout = VK_IMAGE_LAYOUT_GENERAL;
  455. transferDstLayout = VK_IMAGE_LAYOUT_GENERAL;
  456. }
  457. else
  458. {
  459. transferSrcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
  460. transferDstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
  461. }
  462. // Transfer textures to a valid layout
  463. cb->setLayout(srcImage->getHandle(), srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcImage->getLayout(),
  464. transferSrcLayout, range);
  465. cb->setLayout(dstImage->getHandle(), dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT,
  466. dstImage->getLayout(), transferDstLayout, range);
  467. vkCmdCopyImage(cb->getCB()->getHandle(), srcImage->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
  468. dstImage->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, numMipmaps, imageRegions);
  469. // Transfer back to final layouts
  470. srcAccessMask = srcImage->getAccessFlags(srcFinalLayout);
  471. cb->setLayout(srcImage->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, srcAccessMask,
  472. transferSrcLayout, srcFinalLayout, range);
  473. dstAccessMask = dstImage->getAccessFlags(dstFinalLayout);
  474. cb->setLayout(dstImage->getHandle(), VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask,
  475. transferDstLayout, dstFinalLayout, range);
  476. bs_stack_free(imageRegions);
  477. }
  478. VkImageLayout VulkanTextureCore::getOptimalLayout() const
  479. {
  480. int usage = getProperties().getUsage();
  481. if ((usage & TU_RENDERTARGET) != 0)
  482. return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
  483. else if ((usage & TU_DEPTHSTENCIL) != 0)
  484. return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
  485. else if ((usage & TU_LOADSTORE) != 0)
  486. return VK_IMAGE_LAYOUT_GENERAL;
  487. else if ((usage & TU_DYNAMIC) != 0)
  488. return VK_IMAGE_LAYOUT_GENERAL;
  489. else
  490. return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  491. }
  492. void VulkanTextureCore::copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel,
  493. const SPtr<TextureCore>& target, UINT32 queueIdx)
  494. {
  495. VulkanTextureCore* other = static_cast<VulkanTextureCore*>(target.get());
  496. const TextureProperties& srcProps = mProperties;
  497. const TextureProperties& dstProps = other->getProperties();
  498. bool srcHasMultisample = srcProps.getNumSamples() > 1;
  499. bool destHasMultisample = dstProps.getNumSamples() > 1;
  500. if ((srcProps.getUsage() & TU_DEPTHSTENCIL) != 0 || (dstProps.getUsage() & TU_DEPTHSTENCIL) != 0)
  501. {
  502. LOGERR("Texture copy/resolve isn't supported for depth-stencil textures.");
  503. return;
  504. }
  505. bool needsResolve = srcHasMultisample && !destHasMultisample;
  506. bool isMSCopy = srcHasMultisample || destHasMultisample;
  507. if (!needsResolve && isMSCopy)
  508. {
  509. if (srcProps.getNumSamples() != dstProps.getNumSamples())
  510. {
  511. LOGERR("When copying textures their multisample counts must match. Ignoring copy.");
  512. return;
  513. }
  514. }
  515. VulkanCommandBufferManager& cbManager = gVulkanCBManager();
  516. GpuQueueType queueType;
  517. UINT32 localQueueIdx = CommandSyncMask::getQueueIdxAndType(queueIdx, queueType);
  518. VkImageLayout transferSrcLayout, transferDstLayout;
  519. if (mDirectlyMappable)
  520. {
  521. transferSrcLayout = VK_IMAGE_LAYOUT_GENERAL;
  522. transferDstLayout = VK_IMAGE_LAYOUT_GENERAL;
  523. }
  524. else
  525. {
  526. transferSrcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
  527. transferDstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
  528. }
  529. UINT32 mipWidth, mipHeight, mipDepth;
  530. PixelUtil::getSizeForMipLevel(srcProps.getWidth(), srcProps.getHeight(), srcProps.getDepth(), srcMipLevel,
  531. mipWidth, mipHeight, mipDepth);
  532. VkImageResolve resolveRegion;
  533. resolveRegion.srcOffset = { 0, 0, 0 };
  534. resolveRegion.dstOffset = { 0, 0, 0 };
  535. resolveRegion.extent = { mipWidth, mipHeight, mipDepth };
  536. resolveRegion.srcSubresource.baseArrayLayer = srcFace;
  537. resolveRegion.srcSubresource.layerCount = 1;
  538. resolveRegion.srcSubresource.mipLevel = srcMipLevel;
  539. resolveRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  540. resolveRegion.dstSubresource.baseArrayLayer = destFace;
  541. resolveRegion.dstSubresource.layerCount = 1;
  542. resolveRegion.dstSubresource.mipLevel = destMipLevel;
  543. resolveRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  544. VkImageCopy imageRegion;
  545. imageRegion.srcOffset = { 0, 0, 0 };
  546. imageRegion.dstOffset = { 0, 0, 0 };
  547. imageRegion.extent = { mipWidth, mipHeight, mipDepth };
  548. imageRegion.srcSubresource.baseArrayLayer = srcFace;
  549. imageRegion.srcSubresource.layerCount = 1;
  550. imageRegion.srcSubresource.mipLevel = srcMipLevel;
  551. imageRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  552. imageRegion.dstSubresource.baseArrayLayer = destFace;
  553. imageRegion.dstSubresource.layerCount = 1;
  554. imageRegion.dstSubresource.mipLevel = destMipLevel;
  555. imageRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  556. VkImageSubresourceRange srcRange;
  557. srcRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  558. srcRange.baseArrayLayer = srcFace;
  559. srcRange.layerCount = 1;
  560. srcRange.baseMipLevel = srcMipLevel;
  561. srcRange.levelCount = 1;
  562. VkImageSubresourceRange dstRange;
  563. dstRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  564. dstRange.baseArrayLayer = destFace;
  565. dstRange.layerCount = 1;
  566. dstRange.baseMipLevel = destMipLevel;
  567. dstRange.levelCount = 1;
  568. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
  569. for(UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  570. {
  571. VulkanDevice& device = *rapi._getDevice(i);
  572. VulkanImage* srcImage = mImages[i];
  573. VulkanImage* dstImage = other->getResource(i);
  574. if (srcImage == nullptr || dstImage == nullptr)
  575. continue;
  576. VkImageLayout srcLayout = srcImage->getLayout();
  577. VkImageLayout dstLayout = dstImage->getLayout();
  578. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(i, queueType, localQueueIdx);
  579. VkCommandBuffer vkCmdBuf = transferCB->getCB()->getHandle();
  580. // If destination subresource is queued for some operation on a CB (ignoring the ones we're waiting for), then
  581. // we need to make a copy of the image to avoid modifying its use in the previous operation
  582. UINT32 useCount = dstImage->getUseCount();
  583. UINT32 boundCount = dstImage->getBoundCount();
  584. bool isBoundWithoutUse = boundCount > useCount;
  585. if (isBoundWithoutUse)
  586. {
  587. VulkanImage* newImage = createImage(device);
  588. // Avoid copying original contents if the image only has one sub-resource, which we'll overwrite anyway
  589. if (dstProps.getNumMipmaps() > 0 || dstProps.getNumFaces() > 1)
  590. {
  591. VkImageLayout oldDstLayout = dstLayout;
  592. if (oldDstLayout == VK_IMAGE_LAYOUT_UNDEFINED || oldDstLayout == VK_IMAGE_LAYOUT_PREINITIALIZED)
  593. oldDstLayout = getOptimalLayout();
  594. dstLayout = getOptimalLayout();
  595. copyImage(transferCB, dstImage, newImage, oldDstLayout, dstLayout);
  596. VkAccessFlags accessMask = dstImage->getAccessFlags(oldDstLayout);
  597. transferCB->getCB()->registerResource(dstImage, accessMask, oldDstLayout, oldDstLayout,
  598. VulkanUseFlag::Read);
  599. }
  600. dstImage->destroy();
  601. dstImage = newImage;
  602. mImages[i] = dstImage;
  603. }
  604. VkAccessFlags srcAccessMask = srcImage->getAccessFlags(srcLayout);
  605. VkAccessFlags dstAccessMask = dstImage->getAccessFlags(dstLayout);
  606. // Transfer textures to a valid layout
  607. transferCB->setLayout(srcImage->getHandle(), srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcLayout,
  608. transferSrcLayout, srcRange);
  609. transferCB->setLayout(dstImage->getHandle(), dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT,
  610. dstLayout, transferDstLayout, dstRange);
  611. if (srcHasMultisample && !destHasMultisample) // Resolving from MS to non-MS texture
  612. {
  613. vkCmdResolveImage(vkCmdBuf, srcImage->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
  614. dstImage->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &resolveRegion);
  615. }
  616. else // Just a normal copy
  617. {
  618. vkCmdCopyImage(vkCmdBuf, srcImage->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
  619. dstImage->getHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageRegion);
  620. }
  621. // Transfer back to original layouts
  622. if (srcLayout == VK_IMAGE_LAYOUT_UNDEFINED || srcLayout == VK_IMAGE_LAYOUT_PREINITIALIZED)
  623. srcLayout = getOptimalLayout();
  624. srcAccessMask = srcImage->getAccessFlags(srcLayout);
  625. transferCB->setLayout(srcImage->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, srcAccessMask,
  626. transferSrcLayout, srcLayout, srcRange);
  627. if (dstLayout == VK_IMAGE_LAYOUT_UNDEFINED || dstLayout == VK_IMAGE_LAYOUT_PREINITIALIZED)
  628. dstLayout = other->getOptimalLayout();
  629. dstAccessMask = dstImage->getAccessFlags(dstLayout);
  630. transferCB->setLayout(dstImage->getHandle(), VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask,
  631. transferDstLayout, dstLayout, dstRange);
  632. // Notify the command buffer that these resources are being used on it
  633. transferCB->getCB()->registerResource(srcImage, srcAccessMask, srcLayout, srcLayout, VulkanUseFlag::Read);
  634. transferCB->getCB()->registerResource(dstImage, dstAccessMask, dstLayout, dstLayout, VulkanUseFlag::Write);
  635. // Need to wait if subresource we're reading from is being written, or if the subresource we're writing to is
  636. // being accessed in any way
  637. VulkanImageSubresource* srcSubresource = srcImage->getSubresource(srcFace, srcMipLevel);
  638. VulkanImageSubresource* dstSubresource = dstImage->getSubresource(srcFace, srcMipLevel);
  639. UINT32 srcUseFlags = srcSubresource->getUseInfo(VulkanUseFlag::Write);
  640. UINT32 dstUseFlags = dstSubresource->getUseInfo(VulkanUseFlag::Read | VulkanUseFlag::Write);
  641. transferCB->appendMask(srcUseFlags | dstUseFlags);
  642. }
  643. }
  644. PixelData VulkanTextureCore::lockImpl(GpuLockOptions options, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx,
  645. UINT32 queueIdx)
  646. {
  647. const TextureProperties& props = getProperties();
  648. if (props.getNumSamples() > 1)
  649. {
  650. LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
  651. return PixelData();
  652. }
  653. #if BS_PROFILING_ENABLED
  654. if (options == GBL_READ_ONLY || options == GBL_READ_WRITE)
  655. {
  656. BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_Texture);
  657. }
  658. if (options == GBL_READ_WRITE || options == GBL_WRITE_ONLY || options == GBL_WRITE_ONLY_DISCARD || options == GBL_WRITE_ONLY_NO_OVERWRITE)
  659. {
  660. BS_INC_RENDER_STAT_CAT(ResWrite, RenderStatObject_Texture);
  661. }
  662. #endif
  663. UINT32 mipWidth = std::max(1u, props.getWidth() >> mipLevel);
  664. UINT32 mipHeight = std::max(1u, props.getHeight() >> mipLevel);
  665. UINT32 mipDepth = std::max(1u, props.getDepth() >> mipLevel);
  666. PixelData lockedArea(mipWidth, mipHeight, mipDepth, props.getFormat());
  667. VulkanImage* image = mImages[deviceIdx];
  668. if (image == nullptr)
  669. return PixelData();
  670. mIsMapped = true;
  671. mMappedDeviceIdx = deviceIdx;
  672. mMappedGlobalQueueIdx = queueIdx;
  673. mMappedFace = face;
  674. mMappedMip = mipLevel;
  675. mMappedLockOptions = options;
  676. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
  677. VulkanDevice& device = *rapi._getDevice(deviceIdx);
  678. VulkanCommandBufferManager& cbManager = gVulkanCBManager();
  679. GpuQueueType queueType;
  680. UINT32 localQueueIdx = CommandSyncMask::getQueueIdxAndType(queueIdx, queueType);
  681. VulkanImageSubresource* subresource = image->getSubresource(face, mipLevel);
  682. // If memory is host visible try mapping it directly
  683. if (mDirectlyMappable)
  684. {
  685. // Initially the texture will be in preinitialized layout, and it will transition to general layout on first
  686. // use in shader. No further transitions are allowed for directly mappable textures.
  687. assert(image->getLayout() == VK_IMAGE_LAYOUT_PREINITIALIZED || image->getLayout() == VK_IMAGE_LAYOUT_GENERAL);
  688. // GPU should never be allowed to write to a directly mappable texture, since only linear tiling is supported
  689. // for direct mapping, and we don't support using it with either storage textures or render targets.
  690. assert(!mSupportsGPUWrites);
  691. // Check is the GPU currently reading from the image
  692. UINT32 useMask = subresource->getUseInfo(VulkanUseFlag::Read);
  693. bool isUsedOnGPU = useMask != 0;
  694. // We're safe to map directly since GPU isn't using the subresource
  695. if (!isUsedOnGPU)
  696. {
  697. // If some CB has an operation queued that will be using the current contents of the image, create a new
  698. // image so we don't modify the previous use of the image
  699. if (subresource->isBound())
  700. {
  701. VulkanImage* newImage = createImage(device);
  702. // Copy contents of the current image to the new one, unless caller explicitly specifies he doesn't
  703. // care about the current contents
  704. if (options != GBL_WRITE_ONLY_DISCARD)
  705. {
  706. VkMemoryRequirements memReqs;
  707. vkGetImageMemoryRequirements(device.getLogical(), image->getHandle(), &memReqs);
  708. UINT8* src = image->map(0, (UINT32)memReqs.size);
  709. UINT8* dst = newImage->map(0, (UINT32)memReqs.size);
  710. memcpy(dst, src, memReqs.size);
  711. image->unmap();
  712. newImage->unmap();
  713. }
  714. image->destroy();
  715. image = newImage;
  716. mImages[deviceIdx] = image;
  717. }
  718. image->map(face, mipLevel, lockedArea);
  719. return lockedArea;
  720. }
  721. // Caller guarantees he won't touch the same data as the GPU, so just map even though the GPU is using the
  722. // subresource
  723. if (options == GBL_WRITE_ONLY_NO_OVERWRITE)
  724. {
  725. image->map(face, mipLevel, lockedArea);
  726. return lockedArea;
  727. }
  728. // Caller doesn't care about buffer contents, so just discard the existing buffer and create a new one
  729. if (options == GBL_WRITE_ONLY_DISCARD)
  730. {
  731. // We need to discard the entire image, even though we're only writing to a single sub-resource
  732. image->destroy();
  733. image = createImage(device);
  734. mImages[deviceIdx] = image;
  735. image->map(face, mipLevel, lockedArea);
  736. return lockedArea;
  737. }
  738. // We need to read the buffer contents
  739. if (options == GBL_READ_ONLY || options == GBL_READ_WRITE)
  740. {
  741. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(deviceIdx, queueType, localQueueIdx);
  742. // Ensure flush() will wait for all queues currently using to the texture (if any) to finish
  743. // If only reading, wait for all writes to complete, otherwise wait on both writes and reads
  744. if (options == GBL_READ_ONLY)
  745. useMask = subresource->getUseInfo(VulkanUseFlag::Write);
  746. else
  747. useMask = subresource->getUseInfo(VulkanUseFlag::Read | VulkanUseFlag::Write);
  748. transferCB->appendMask(useMask);
  749. // Submit the command buffer and wait until it finishes
  750. transferCB->flush(true);
  751. // If writing and some CB has an operation queued that will be using the current contents of the image,
  752. // create a new image so we don't modify the previous use of the image
  753. if (options == GBL_READ_WRITE && subresource->isBound())
  754. {
  755. VulkanImage* newImage = createImage(device);
  756. VkMemoryRequirements memReqs;
  757. vkGetImageMemoryRequirements(device.getLogical(), image->getHandle(), &memReqs);
  758. UINT8* src = image->map(0, (UINT32)memReqs.size);
  759. UINT8* dst = newImage->map(0, (UINT32)memReqs.size);
  760. memcpy(dst, src, memReqs.size);
  761. image->unmap();
  762. newImage->unmap();
  763. image->destroy();
  764. image = newImage;
  765. mImages[deviceIdx] = image;
  766. }
  767. image->map(face, mipLevel, lockedArea);
  768. return lockedArea;
  769. }
  770. // Otherwise, we're doing write only, in which case it's best to use the staging buffer to avoid waiting
  771. // and blocking, so fall through
  772. }
  773. // Can't use direct mapping, so use a staging buffer
  774. // We might need to copy the current contents of the image to the staging buffer. Even if the user doesn't plan on
  775. // reading, it is still required as we will eventually copy all of the contents back to the original image,
  776. // and we can't write potentially uninitialized data. The only exception is when the caller specifies the image
  777. // contents should be discarded in which he guarantees he will overwrite the entire locked area with his own
  778. // contents.
  779. bool needRead = options != GBL_WRITE_ONLY_DISCARD_RANGE && options != GBL_WRITE_ONLY_DISCARD;
  780. // Allocate a staging buffer
  781. mStagingBuffer = createStaging(device, lockedArea, needRead);
  782. if (needRead) // If reading, we need to copy the current contents of the image to the staging buffer
  783. {
  784. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(deviceIdx, queueType, localQueueIdx);
  785. // Similar to above, if image supports GPU writes or is currently being written to, we need to wait on any
  786. // potential writes to complete
  787. UINT32 writeUseMask = subresource->getUseInfo(VulkanUseFlag::Write);
  788. if (mSupportsGPUWrites || writeUseMask != 0)
  789. {
  790. // Ensure flush() will wait for all queues currently writing to the image (if any) to finish
  791. transferCB->appendMask(writeUseMask);
  792. }
  793. VkImageSubresourceRange range;
  794. if ((props.getUsage() & TU_DEPTHSTENCIL) != 0)
  795. range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
  796. else
  797. range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  798. range.baseArrayLayer = face;
  799. range.layerCount = 1;
  800. range.baseMipLevel = mipLevel;
  801. range.levelCount = 1;
  802. VkImageSubresourceLayers rangeLayers;
  803. if ((props.getUsage() & TU_DEPTHSTENCIL) != 0)
  804. rangeLayers.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
  805. else
  806. rangeLayers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  807. rangeLayers.baseArrayLayer = range.baseArrayLayer;
  808. rangeLayers.layerCount = range.layerCount;
  809. rangeLayers.mipLevel = range.baseMipLevel;
  810. VkExtent3D extent;
  811. PixelUtil::getSizeForMipLevel(props.getWidth(), props.getHeight(), props.getDepth(), mMappedMip,
  812. extent.width, extent.height, extent.depth);
  813. // Transfer texture to a valid layout
  814. VkAccessFlags currentAccessMask = image->getAccessFlags(image->getLayout());
  815. transferCB->setLayout(image->getHandle(), currentAccessMask, VK_ACCESS_TRANSFER_READ_BIT, image->getLayout(),
  816. VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, range);
  817. // Queue copy command
  818. image->copy(transferCB, mStagingBuffer, extent, rangeLayers, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
  819. // Transfer back to original layout
  820. VkImageLayout dstLayout = image->getLayout();
  821. if (dstLayout == VK_IMAGE_LAYOUT_UNDEFINED || dstLayout == VK_IMAGE_LAYOUT_PREINITIALIZED)
  822. {
  823. dstLayout = getOptimalLayout();
  824. currentAccessMask = image->getAccessFlags(dstLayout);
  825. }
  826. transferCB->setLayout(image->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, currentAccessMask,
  827. VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstLayout, range);
  828. transferCB->getCB()->registerResource(image, currentAccessMask, dstLayout, dstLayout, VulkanUseFlag::Read);
  829. // Ensure data written to the staging buffer is visible
  830. VkAccessFlags stagingAccessFlags;
  831. if (options == GBL_READ_ONLY)
  832. stagingAccessFlags = VK_ACCESS_HOST_READ_BIT;
  833. else // Must be read/write
  834. stagingAccessFlags = VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT;
  835. transferCB->memoryBarrier(mStagingBuffer->getHandle(),
  836. VK_ACCESS_TRANSFER_WRITE_BIT,
  837. stagingAccessFlags,
  838. VK_PIPELINE_STAGE_TRANSFER_BIT,
  839. VK_PIPELINE_STAGE_HOST_BIT);
  840. // Submit the command buffer and wait until it finishes
  841. transferCB->flush(true);
  842. }
  843. UINT8* data = mStagingBuffer->map(0, lockedArea.getSize());
  844. lockedArea.setExternalBuffer(data);
  845. return lockedArea;
  846. }
  847. void VulkanTextureCore::unlockImpl()
  848. {
  849. // Possibly map() failed with some error
  850. if (!mIsMapped)
  851. return;
  852. // Note: If we did any writes they need to be made visible to the GPU. However there is no need to execute
  853. // a pipeline barrier because (as per spec) host writes are implicitly visible to the device.
  854. if (mStagingBuffer == nullptr)
  855. mImages[mMappedDeviceIdx]->unmap();
  856. else
  857. {
  858. mStagingBuffer->unmap();
  859. bool isWrite = mMappedLockOptions != GBL_READ_ONLY;
  860. // We the caller wrote anything to the staging buffer, we need to upload it back to the main buffer
  861. if (isWrite)
  862. {
  863. VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
  864. VulkanDevice& device = *rapi._getDevice(mMappedDeviceIdx);
  865. VulkanCommandBufferManager& cbManager = gVulkanCBManager();
  866. GpuQueueType queueType;
  867. UINT32 localQueueIdx = CommandSyncMask::getQueueIdxAndType(mMappedGlobalQueueIdx, queueType);
  868. VulkanImage* image = mImages[mMappedDeviceIdx];
  869. VkImageLayout curLayout = image->getLayout();
  870. VulkanTransferBuffer* transferCB = cbManager.getTransferBuffer(mMappedDeviceIdx, queueType, localQueueIdx);
  871. VulkanImageSubresource* subresource = image->getSubresource(mMappedFace, mMappedMip);
  872. // If the subresource is used in any way on the GPU, we need to wait for that use to finish before
  873. // we issue our copy
  874. UINT32 useMask = subresource->getUseInfo(VulkanUseFlag::Read | VulkanUseFlag::Write);
  875. bool isNormalWrite = false;
  876. if (useMask != 0) // Subresource is currently used on the GPU
  877. {
  878. // Try to avoid the wait by checking for special write conditions
  879. // Caller guarantees he won't touch the same data as the GPU, so just copy
  880. if (mMappedLockOptions == GBL_WRITE_ONLY_NO_OVERWRITE)
  881. {
  882. // Fall through to copy()
  883. }
  884. // Caller doesn't care about buffer contents, so just discard the existing buffer and create a new one
  885. else if (mMappedLockOptions == GBL_WRITE_ONLY_DISCARD)
  886. {
  887. // We need to discard the entire image, even though we're only writing to a single sub-resource
  888. image->destroy();
  889. image = createImage(device);
  890. mImages[mMappedDeviceIdx] = image;
  891. subresource = image->getSubresource(mMappedFace, mMappedMip);
  892. }
  893. else // Otherwise we have no choice but to issue a dependency between the queues
  894. {
  895. transferCB->appendMask(useMask);
  896. isNormalWrite = true;
  897. }
  898. }
  899. else
  900. isNormalWrite = true;
  901. const TextureProperties& props = getProperties();
  902. // Check if the subresource will still be bound somewhere after the CBs using it finish
  903. if (isNormalWrite)
  904. {
  905. UINT32 useCount = subresource->getUseCount();
  906. UINT32 boundCount = subresource->getBoundCount();
  907. bool isBoundWithoutUse = boundCount > useCount;
  908. // If image is queued for some operation on a CB, then we need to make a copy of the subresource to
  909. // avoid modifying its use in the previous operation
  910. if (isBoundWithoutUse)
  911. {
  912. VulkanImage* newImage = createImage(device);
  913. // Avoid copying original contents if the image only has one sub-resource, which we'll overwrite anyway
  914. if (props.getNumMipmaps() > 0 || props.getNumFaces() > 1)
  915. {
  916. VkImageLayout oldImgLayout = image->getLayout();
  917. if (oldImgLayout == VK_IMAGE_LAYOUT_UNDEFINED || oldImgLayout == VK_IMAGE_LAYOUT_PREINITIALIZED)
  918. oldImgLayout = getOptimalLayout();
  919. curLayout = getOptimalLayout();
  920. copyImage(transferCB, image, newImage, oldImgLayout, curLayout);
  921. VkAccessFlags accessMask = image->getAccessFlags(oldImgLayout);
  922. transferCB->getCB()->registerResource(image, accessMask, oldImgLayout, oldImgLayout,
  923. VulkanUseFlag::Read);
  924. }
  925. image->destroy();
  926. image = newImage;
  927. mImages[mMappedDeviceIdx] = image;
  928. }
  929. }
  930. VkImageSubresourceRange range;
  931. if ((props.getUsage() & TU_DEPTHSTENCIL) != 0)
  932. range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
  933. else
  934. range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  935. range.baseArrayLayer = mMappedFace;
  936. range.layerCount = 1;
  937. range.baseMipLevel = mMappedMip;
  938. range.levelCount = 1;
  939. VkImageSubresourceLayers rangeLayers;
  940. rangeLayers.aspectMask = range.aspectMask;
  941. rangeLayers.baseArrayLayer = range.baseArrayLayer;
  942. rangeLayers.layerCount = range.layerCount;
  943. rangeLayers.mipLevel = range.baseMipLevel;
  944. VkExtent3D extent;
  945. PixelUtil::getSizeForMipLevel(props.getWidth(), props.getHeight(), props.getDepth(), mMappedMip,
  946. extent.width, extent.height, extent.depth);
  947. VkImageLayout transferLayout;
  948. if (mDirectlyMappable)
  949. transferLayout = VK_IMAGE_LAYOUT_GENERAL;
  950. else
  951. transferLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
  952. // Transfer texture to a valid layout
  953. VkAccessFlags currentAccessMask = image->getAccessFlags(curLayout);
  954. transferCB->setLayout(image->getHandle(), currentAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT,
  955. curLayout, transferLayout, range);
  956. // Queue copy command
  957. mStagingBuffer->copy(transferCB, image, extent, rangeLayers, transferLayout);
  958. // Transfer back to original (or optimal if initial layout was undefined/preinitialized)
  959. VkImageLayout dstLayout = curLayout;
  960. if(dstLayout == VK_IMAGE_LAYOUT_UNDEFINED || dstLayout == VK_IMAGE_LAYOUT_PREINITIALIZED)
  961. dstLayout = getOptimalLayout();
  962. currentAccessMask = image->getAccessFlags(dstLayout);
  963. transferCB->setLayout(image->getHandle(), VK_ACCESS_TRANSFER_WRITE_BIT, currentAccessMask,
  964. transferLayout, dstLayout, range);
  965. // Notify the command buffer that these resources are being used on it
  966. transferCB->getCB()->registerResource(mStagingBuffer, VK_ACCESS_TRANSFER_READ_BIT, VulkanUseFlag::Read);
  967. transferCB->getCB()->registerResource(image, currentAccessMask, dstLayout, dstLayout, VulkanUseFlag::Write);
  968. // We don't actually flush the transfer buffer here since it's an expensive operation, but it's instead
  969. // done automatically before next "normal" command buffer submission.
  970. }
  971. mStagingBuffer->destroy();
  972. mStagingBuffer = nullptr;
  973. }
  974. mIsMapped = false;
  975. }
  976. void VulkanTextureCore::readDataImpl(PixelData& dest, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx, UINT32 queueIdx)
  977. {
  978. if (mProperties.getNumSamples() > 1)
  979. {
  980. LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
  981. return;
  982. }
  983. PixelData myData = lock(GBL_READ_ONLY, mipLevel, face, deviceIdx, queueIdx);
  984. #if BS_DEBUG_MODE
  985. if (dest.getConsecutiveSize() != myData.getConsecutiveSize())
  986. {
  987. unlock();
  988. BS_EXCEPT(InternalErrorException, "Buffer sizes don't match");
  989. }
  990. #endif
  991. PixelUtil::bulkPixelConversion(myData, dest);
  992. unlock();
  993. BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_Texture);
  994. }
  995. void VulkanTextureCore::writeDataImpl(const PixelData& src, UINT32 mipLevel, UINT32 face, bool discardWholeBuffer,
  996. UINT32 queueIdx)
  997. {
  998. if (mProperties.getNumSamples() > 1)
  999. {
  1000. LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
  1001. return;
  1002. }
  1003. PixelFormat format = mProperties.getFormat();
  1004. mipLevel = Math::clamp(mipLevel, (UINT32)mipLevel, mProperties.getNumMipmaps());
  1005. face = Math::clamp(face, (UINT32)0, mProperties.getNumFaces() - 1);
  1006. if (face > 0 && mProperties.getTextureType() == TEX_TYPE_3D)
  1007. {
  1008. LOGERR("3D texture arrays are not supported.");
  1009. return;
  1010. }
  1011. // Write to every device
  1012. for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
  1013. {
  1014. if (mImages[i] == nullptr)
  1015. continue;
  1016. PixelData myData = lock(discardWholeBuffer ? GBL_WRITE_ONLY_DISCARD : GBL_WRITE_ONLY_DISCARD_RANGE,
  1017. mipLevel, face, i, queueIdx);
  1018. PixelUtil::bulkPixelConversion(src, myData);
  1019. unlock();
  1020. }
  1021. BS_INC_RENDER_STAT_CAT(ResWrite, RenderStatObject_Texture);
  1022. }
  1023. }