VulkanGraphicsDevice.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. #include "pch.h"
  2. #include <third_party/libyuv/include/libyuv/convert.h>
  3. #include "GraphicsDevice/GraphicsUtility.h"
  4. #include "UnityVulkanInterfaceFunctions.h"
  5. #include "VulkanGraphicsDevice.h"
  6. #include "VulkanTexture2D.h"
  7. #include "VulkanUtility.h"
  8. #include "WebRTCMacros.h"
  9. #if CUDA_PLATFORM
  10. #include "GraphicsDevice/Cuda/GpuMemoryBufferCudaHandle.h"
  11. #else
  12. #include "GpuMemoryBuffer.h"
  13. #endif
  14. namespace unity
  15. {
  16. namespace webrtc
  17. {
  18. VulkanGraphicsDevice::VulkanGraphicsDevice(
  19. UnityGraphicsVulkan* unityVulkan,
  20. const VkInstance instance,
  21. const VkPhysicalDevice physicalDevice,
  22. const VkDevice device,
  23. const VkQueue graphicsQueue,
  24. const uint32_t queueFamilyIndex,
  25. UnityGfxRenderer renderer,
  26. ProfilerMarkerFactory* profiler)
  27. : IGraphicsDevice(renderer, profiler)
  28. , m_unityVulkan(unityVulkan)
  29. , m_physicalDevice(physicalDevice)
  30. , m_device(device)
  31. , m_graphicsQueue(graphicsQueue)
  32. , m_commandPool(nullptr)
  33. , m_queueFamilyIndex(queueFamilyIndex)
  34. , m_allocator(nullptr)
  35. #if CUDA_PLATFORM
  36. , m_instance(instance)
  37. , m_isCudaSupport(false)
  38. #endif
  39. {
  40. if (profiler)
  41. m_maker = profiler->CreateMarker(
  42. "VulkanGraphicsDevice.CopyImage", kUnityProfilerCategoryOther, kUnityProfilerMarkerFlagDefault, 0);
  43. }
  44. //---------------------------------------------------------------------------------------------------------------------
  45. bool VulkanGraphicsDevice::InitV()
  46. {
  47. #if CUDA_PLATFORM
  48. m_isCudaSupport = InitCudaContext();
  49. #endif
  50. return VK_SUCCESS == CreateCommandPool();
  51. }
  52. #if CUDA_PLATFORM
  53. bool VulkanGraphicsDevice::InitCudaContext()
  54. {
  55. if (!VulkanUtility::LoadInstanceFunctions(m_instance))
  56. return false;
  57. if (!VulkanUtility::LoadDeviceFunctions(m_device))
  58. return false;
  59. CUresult result = m_cudaContext.Init(m_instance, m_physicalDevice);
  60. if (CUDA_SUCCESS != result)
  61. return false;
  62. return true;
  63. }
  64. #endif
  65. //---------------------------------------------------------------------------------------------------------------------
  66. void VulkanGraphicsDevice::ShutdownV()
  67. {
  68. #if CUDA_PLATFORM
  69. m_cudaContext.Shutdown();
  70. #endif
  71. VULKAN_SAFE_DESTROY_COMMAND_POOL(m_device, m_commandPool, m_allocator)
  72. }
  73. //---------------------------------------------------------------------------------------------------------------------
  74. std::unique_ptr<UnityVulkanImage> VulkanGraphicsDevice::AccessTexture(void* ptr) const
  75. {
  76. // cannot do resource uploads inside renderpass
  77. m_unityVulkan->EnsureOutsideRenderPass();
  78. std::unique_ptr<UnityVulkanImage> unityVulkanImage = std::make_unique<UnityVulkanImage>();
  79. VkImageSubresource subResource { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
  80. if (!m_unityVulkan->AccessTexture(
  81. ptr,
  82. &subResource,
  83. VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
  84. VK_PIPELINE_STAGE_TRANSFER_BIT,
  85. VK_ACCESS_TRANSFER_READ_BIT,
  86. kUnityVulkanResourceAccess_PipelineBarrier,
  87. unityVulkanImage.get()))
  88. {
  89. return nullptr;
  90. }
  91. return unityVulkanImage;
  92. }
  93. static VkResult BeginCommandBuffer(VkCommandBuffer commandBuffer)
  94. {
  95. VkCommandBufferBeginInfo beginInfo = {};
  96. beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  97. return vkBeginCommandBuffer(commandBuffer, &beginInfo);
  98. }
  99. static VkResult QueueSubmit(VkQueue queue, VkCommandBuffer commandBuffer, VkFence fence)
  100. {
  101. VkSubmitInfo submitInfo = {};
  102. submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  103. submitInfo.commandBufferCount = 1;
  104. submitInfo.pCommandBuffers = &commandBuffer;
  105. return vkQueueSubmit(queue, 1, &submitInfo, fence);
  106. }
  107. // Returns null if failed
  108. ITexture2D* VulkanGraphicsDevice::CreateDefaultTextureV(
  109. const uint32_t w, const uint32_t h, UnityRenderingExtTextureFormat textureFormat)
  110. {
  111. std::unique_ptr<VulkanTexture2D> vulkanTexture = std::make_unique<VulkanTexture2D>(w, h);
  112. if (!vulkanTexture->Init(m_physicalDevice, m_device, m_commandPool))
  113. {
  114. RTC_LOG(LS_ERROR) << "VulkanTexture2D::Init failed.";
  115. return nullptr;
  116. }
  117. return vulkanTexture.release();
  118. }
  119. ITexture2D*
  120. VulkanGraphicsDevice::CreateCPUReadTextureV(uint32_t w, uint32_t h, UnityRenderingExtTextureFormat textureFormat)
  121. {
  122. std::unique_ptr<VulkanTexture2D> vulkanTexture = std::make_unique<VulkanTexture2D>(w, h);
  123. if (!vulkanTexture->InitCpuRead(m_physicalDevice, m_device, m_commandPool))
  124. {
  125. RTC_LOG(LS_ERROR) << "VulkanTexture2D::InitCpuRead failed.";
  126. return nullptr;
  127. }
  128. return vulkanTexture.release();
  129. }
  130. //---------------------------------------------------------------------------------------------------------------------
  131. bool VulkanGraphicsDevice::CopyResourceV(ITexture2D* dest, ITexture2D* src)
  132. {
  133. VulkanTexture2D* destTexture = reinterpret_cast<VulkanTexture2D*>(dest);
  134. VulkanTexture2D* srcTexture = reinterpret_cast<VulkanTexture2D*>(src);
  135. if (destTexture == srcTexture)
  136. return false;
  137. if (destTexture == nullptr || srcTexture == nullptr)
  138. return false;
  139. VkCommandBuffer commandBuffer = destTexture->GetCommandBuffer();
  140. VkResult result = BeginCommandBuffer(commandBuffer);
  141. if (result != VK_SUCCESS)
  142. {
  143. RTC_LOG(LS_ERROR) << "BeginCommandBuffer failed. result:" << result;
  144. return false;
  145. }
  146. // Transition the src texture layout.
  147. result = VulkanUtility::DoImageLayoutTransition(
  148. commandBuffer,
  149. srcTexture->GetImage(),
  150. srcTexture->GetTextureFormat(),
  151. VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
  152. VK_PIPELINE_STAGE_TRANSFER_BIT,
  153. VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
  154. VK_PIPELINE_STAGE_TRANSFER_BIT);
  155. if (result != VK_SUCCESS)
  156. {
  157. RTC_LOG(LS_ERROR) << "DoImageLayoutTransition failed. result:" << result;
  158. return false;
  159. }
  160. // Transition the dst texture layout.
  161. result = VulkanUtility::DoImageLayoutTransition(
  162. commandBuffer,
  163. destTexture->GetImage(),
  164. destTexture->GetTextureFormat(),
  165. VK_IMAGE_LAYOUT_UNDEFINED,
  166. VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
  167. VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
  168. VK_PIPELINE_STAGE_TRANSFER_BIT);
  169. if (result != VK_SUCCESS)
  170. {
  171. RTC_LOG(LS_ERROR) << "DoImageLayoutTransition failed. result:" << result;
  172. return false;
  173. }
  174. result = VulkanUtility::CopyImage(
  175. commandBuffer,
  176. srcTexture->GetImage(),
  177. destTexture->GetImage(),
  178. destTexture->GetWidth(),
  179. destTexture->GetHeight());
  180. if (result != VK_SUCCESS)
  181. {
  182. RTC_LOG(LS_ERROR) << "CopyImage failed. result:" << result;
  183. return false;
  184. }
  185. // transition the src texture layout back to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
  186. result = VulkanUtility::DoImageLayoutTransition(
  187. commandBuffer,
  188. srcTexture->GetImage(),
  189. srcTexture->GetTextureFormat(),
  190. VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
  191. VK_PIPELINE_STAGE_TRANSFER_BIT,
  192. VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
  193. VK_PIPELINE_STAGE_TRANSFER_BIT);
  194. if (result != VK_SUCCESS)
  195. {
  196. RTC_LOG(LS_ERROR) << "DoImageLayoutTransition failed. result:" << result;
  197. return false;
  198. }
  199. result = vkEndCommandBuffer(commandBuffer);
  200. if (result != VK_SUCCESS)
  201. {
  202. RTC_LOG(LS_ERROR) << "vkEndCommandBuffer failed. result:" << result;
  203. return false;
  204. }
  205. VkFence fence = destTexture->GetFence();
  206. result = QueueSubmit(m_graphicsQueue, commandBuffer, fence);
  207. if (result != VK_SUCCESS)
  208. {
  209. RTC_LOG(LS_ERROR) << "vkQueueSubmit failed. result:" << result;
  210. return false;
  211. }
  212. return true;
  213. }
  214. //---------------------------------------------------------------------------------------------------------------------
  215. bool VulkanGraphicsDevice::CopyResourceFromNativeV(ITexture2D* dest, void* nativeTexturePtr)
  216. {
  217. if (nullptr == dest || nullptr == nativeTexturePtr)
  218. return false;
  219. VulkanTexture2D* destTexture = reinterpret_cast<VulkanTexture2D*>(dest);
  220. UnityVulkanImage* unityVulkanImage = static_cast<UnityVulkanImage*>(nativeTexturePtr);
  221. VkCommandBuffer commandBuffer = destTexture->GetCommandBuffer();
  222. VkResult result = BeginCommandBuffer(commandBuffer);
  223. if (result != VK_SUCCESS)
  224. {
  225. RTC_LOG(LS_ERROR) << "BeginCommandBuffer failed. result:" << result;
  226. return false;
  227. }
  228. // Transition the src texture layout.
  229. result = VulkanUtility::DoImageLayoutTransition(
  230. commandBuffer,
  231. unityVulkanImage->image,
  232. unityVulkanImage->format,
  233. VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
  234. VK_PIPELINE_STAGE_TRANSFER_BIT,
  235. VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
  236. VK_PIPELINE_STAGE_TRANSFER_BIT);
  237. if (result != VK_SUCCESS)
  238. {
  239. RTC_LOG(LS_ERROR) << "DoImageLayoutTransition failed. result:" << result;
  240. return false;
  241. }
  242. // Transition the dst texture layout.
  243. result = VulkanUtility::DoImageLayoutTransition(
  244. commandBuffer,
  245. destTexture->GetImage(),
  246. destTexture->GetTextureFormat(),
  247. VK_IMAGE_LAYOUT_UNDEFINED,
  248. VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
  249. VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
  250. VK_PIPELINE_STAGE_TRANSFER_BIT);
  251. if (result != VK_SUCCESS)
  252. {
  253. RTC_LOG(LS_ERROR) << "DoImageLayoutTransition failed. result:" << result;
  254. return false;
  255. }
  256. VkImage image = unityVulkanImage->image;
  257. if (destTexture->GetImage() == image)
  258. return false;
  259. {
  260. std::unique_ptr<const ScopedProfiler> profiler;
  261. if (m_profiler)
  262. profiler = m_profiler->CreateScopedProfiler(*m_maker);
  263. // The layouts of All VulkanTexture2D should be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
  264. // so no transition for destTex
  265. result = VulkanUtility::CopyImage(
  266. commandBuffer, image, destTexture->GetImage(), destTexture->GetWidth(), destTexture->GetHeight());
  267. if (result != VK_SUCCESS)
  268. {
  269. RTC_LOG(LS_ERROR) << "CopyImage failed. result:" << result;
  270. return false;
  271. }
  272. }
  273. result = vkEndCommandBuffer(commandBuffer);
  274. if (result != VK_SUCCESS)
  275. {
  276. RTC_LOG(LS_ERROR) << "vkEndCommandBuffer failed. result:" << result;
  277. return false;
  278. }
  279. VkFence fence = destTexture->GetFence();
  280. result = QueueSubmit(m_graphicsQueue, commandBuffer, fence);
  281. if (result != VK_SUCCESS)
  282. {
  283. RTC_LOG(LS_ERROR) << "vkQueueSubmit failed. result:" << result;
  284. return false;
  285. }
  286. return true;
  287. }
  288. VkResult VulkanGraphicsDevice::CreateCommandPool()
  289. {
  290. VkCommandPoolCreateInfo poolInfo = {};
  291. poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  292. poolInfo.queueFamilyIndex = m_queueFamilyIndex;
  293. poolInfo.flags = 0;
  294. return vkCreateCommandPool(m_device, &poolInfo, m_allocator, &m_commandPool);
  295. }
  296. rtc::scoped_refptr<webrtc::I420Buffer> VulkanGraphicsDevice::ConvertRGBToI420(ITexture2D* tex)
  297. {
  298. VulkanTexture2D* vulkanTexture = static_cast<VulkanTexture2D*>(tex);
  299. const int32_t width = static_cast<int32_t>(tex->GetWidth());
  300. const int32_t height = static_cast<int32_t>(tex->GetHeight());
  301. const VkDeviceMemory dstImageMemory = vulkanTexture->GetTextureImageMemory();
  302. VkImageSubresource subresource { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
  303. VkSubresourceLayout subresourceLayout;
  304. vkGetImageSubresourceLayout(m_device, vulkanTexture->GetImage(), &subresource, &subresourceLayout);
  305. const int32_t rowPitch = static_cast<int32_t>(subresourceLayout.rowPitch);
  306. void* data;
  307. std::vector<uint8_t> dst;
  308. dst.resize(vulkanTexture->GetTextureImageMemorySize());
  309. const VkResult result = vkMapMemory(m_device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, &data);
  310. if (result != VK_SUCCESS)
  311. {
  312. return nullptr;
  313. }
  314. std::memcpy(static_cast<void*>(dst.data()), data, dst.size());
  315. vkUnmapMemory(m_device, dstImageMemory);
  316. // convert format to i420
  317. rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = webrtc::I420Buffer::Create(width, height);
  318. libyuv::ARGBToI420(
  319. dst.data(),
  320. rowPitch,
  321. i420Buffer->MutableDataY(),
  322. i420Buffer->StrideY(),
  323. i420Buffer->MutableDataU(),
  324. i420Buffer->StrideU(),
  325. i420Buffer->MutableDataV(),
  326. i420Buffer->StrideV(),
  327. width,
  328. height);
  329. return i420Buffer;
  330. }
  331. std::unique_ptr<GpuMemoryBufferHandle> VulkanGraphicsDevice::Map(ITexture2D* texture)
  332. {
  333. #if CUDA_PLATFORM
  334. if (!IsCudaSupport())
  335. return nullptr;
  336. // set context on the thread.
  337. cuCtxPushCurrent(GetCUcontext());
  338. VulkanTexture2D* vulkanTexture = static_cast<VulkanTexture2D*>(texture);
  339. void* exportHandle = VulkanUtility::GetExportHandle(m_device, vulkanTexture->GetTextureImageMemory());
  340. if (!exportHandle)
  341. {
  342. RTC_LOG(LS_ERROR) << "cannot get export handle";
  343. throw;
  344. }
  345. CUDA_EXTERNAL_MEMORY_HANDLE_DESC memDesc = {};
  346. #ifndef _WIN32
  347. memDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD;
  348. #else
  349. memDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32;
  350. #endif
  351. memDesc.handle.fd = static_cast<int>(reinterpret_cast<uintptr_t>(exportHandle));
  352. memDesc.size = vulkanTexture->GetTextureImageMemorySize();
  353. CUresult result;
  354. CUexternalMemory externalMemory;
  355. result = cuImportExternalMemory(&externalMemory, &memDesc);
  356. if (result != CUDA_SUCCESS)
  357. {
  358. RTC_LOG(LS_ERROR) << "cuImportExternalMemory error:" << result;
  359. throw;
  360. }
  361. const VkExtent2D extent = { texture->GetWidth(), texture->GetHeight() };
  362. CUDA_ARRAY3D_DESCRIPTOR arrayDesc = {};
  363. arrayDesc.Width = extent.width;
  364. arrayDesc.Height = extent.height;
  365. arrayDesc.Depth = 0; /* CUDA 2D arrays are defined to have depth 0 */
  366. arrayDesc.Format = CU_AD_FORMAT_UNSIGNED_INT32;
  367. arrayDesc.NumChannels = 1;
  368. arrayDesc.Flags = CUDA_ARRAY3D_SURFACE_LDST | CUDA_ARRAY3D_COLOR_ATTACHMENT;
  369. CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC mipmapArrayDesc = {};
  370. mipmapArrayDesc.arrayDesc = arrayDesc;
  371. mipmapArrayDesc.numLevels = 1;
  372. CUmipmappedArray mipmappedArray;
  373. result = cuExternalMemoryGetMappedMipmappedArray(&mipmappedArray, externalMemory, &mipmapArrayDesc);
  374. if (result != CUDA_SUCCESS)
  375. {
  376. RTC_LOG(LS_ERROR) << "cuExternalMemoryGetMappedMipmappedArray error:" << result;
  377. throw;
  378. }
  379. CUarray array;
  380. result = cuMipmappedArrayGetLevel(&array, mipmappedArray, 0);
  381. if (result != CUDA_SUCCESS)
  382. {
  383. RTC_LOG(LS_ERROR) << "cuMipmappedArrayGetLevel error:" << result;
  384. throw;
  385. }
  386. cuCtxPopCurrent(nullptr);
  387. std::unique_ptr<GpuMemoryBufferCudaHandle> handle = std::make_unique<GpuMemoryBufferCudaHandle>();
  388. handle->context = GetCUcontext();
  389. handle->mappedArray = array;
  390. handle->externalMemory = externalMemory;
  391. return std::move(handle);
  392. #else
  393. return nullptr;
  394. #endif
  395. }
  396. bool VulkanGraphicsDevice::WaitIdleForTest()
  397. {
  398. VkResult result = vkQueueWaitIdle(m_graphicsQueue);
  399. if (result != VK_SUCCESS)
  400. {
  401. RTC_LOG(LS_INFO) << "vkQueueWaitIdle failed. result:" << result;
  402. return false;
  403. }
  404. return true;
  405. }
  406. bool VulkanGraphicsDevice::WaitSync(const ITexture2D* texture, uint64_t nsTimeout)
  407. {
  408. const VulkanTexture2D* vulkanTexture = static_cast<const VulkanTexture2D*>(texture);
  409. VkFence fence = vulkanTexture->GetFence();
  410. VkResult result = vkWaitForFences(m_device, 1, &fence, true, nsTimeout);
  411. if (result != VK_SUCCESS)
  412. {
  413. RTC_LOG(LS_INFO) << "vkWaitForFences failed. result:" << result;
  414. return false;
  415. }
  416. return true;
  417. }
  418. bool VulkanGraphicsDevice::ResetSync(const ITexture2D* texture)
  419. {
  420. const VulkanTexture2D* vulkanTexture = static_cast<const VulkanTexture2D*>(texture);
  421. VkCommandBuffer commandBuffer = vulkanTexture->GetCommandBuffer();
  422. VkFence fence = vulkanTexture->GetFence();
  423. VkResult result = vkGetFenceStatus(m_device, fence);
  424. if (result != VK_SUCCESS)
  425. {
  426. RTC_LOG(LS_INFO) << "vkGetFenceStatus failed. result:" << result;
  427. return false;
  428. }
  429. result = vkResetFences(m_device, 1, &fence);
  430. if (result != VK_SUCCESS)
  431. {
  432. RTC_LOG(LS_INFO) << "vkResetFences failed. result:" << result;
  433. return false;
  434. }
  435. result = vkResetCommandBuffer(commandBuffer, 0);
  436. if (result != VK_SUCCESS)
  437. {
  438. RTC_LOG(LS_INFO) << "vkResetCommandBuffer failed. result:" << result;
  439. return false;
  440. }
  441. return true;
  442. }
  443. } // end namespace webrtc
  444. } // end namespace unity