BsVulkanTexture.cpp 44 KB

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