vulkan_gl_interop.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "android_webview/browser/gfx/vulkan_gl_interop.h"
  5. #include <utility>
  6. #include "android_webview/browser/gfx/render_thread_manager.h"
  7. #include "base/android/android_hardware_buffer_compat.h"
  8. #include "base/android/scoped_hardware_buffer_fence_sync.h"
  9. #include "gpu/command_buffer/service/skia_utils.h"
  10. #include "gpu/ipc/common/android/android_image_reader_utils.h"
  11. #include "gpu/vulkan/vulkan_fence_helper.h"
  12. #include "gpu/vulkan/vulkan_function_pointers.h"
  13. #include "gpu/vulkan/vulkan_image.h"
  14. #include "gpu/vulkan/vulkan_implementation.h"
  15. #include "third_party/skia/include/core/SkCanvas.h"
  16. #include "third_party/skia/include/core/SkColorSpace.h"
  17. #include "third_party/skia/include/gpu/GrBackendSemaphore.h"
  18. #include "third_party/skia/include/gpu/vk/GrVkBackendContext.h"
  19. #include "third_party/skia/include/gpu/vk/GrVkExtensions.h"
  20. #include "third_party/skia/src/gpu/vk/GrVkSecondaryCBDrawContext.h"
  21. #include "ui/gfx/gpu_memory_buffer.h"
  22. #include "ui/gl/gl_bindings.h"
  23. #include "ui/gl/gl_context_egl.h"
  24. #include "ui/gl/gl_image_ahardwarebuffer.h"
  25. #include "ui/gl/gl_surface_egl.h"
  26. #include "ui/gl/init/gl_factory.h"
  27. namespace android_webview {
  28. namespace {
  29. void CleanupInFlightDraw(sk_sp<GrVkSecondaryCBDrawContext> draw_context,
  30. VkSemaphore post_draw_semaphore,
  31. gpu::VulkanDeviceQueue* device_queue,
  32. bool /* context_lost */) {
  33. VkDevice device = device_queue->GetVulkanDevice();
  34. // We do the same thing whether or not context is lost.
  35. draw_context->releaseResources();
  36. draw_context.reset();
  37. if (post_draw_semaphore != VK_NULL_HANDLE)
  38. vkDestroySemaphore(device, post_draw_semaphore, nullptr);
  39. }
  40. } // namespace
  41. // static
  42. VulkanGLInterop::GLNonOwnedCompatibilityContext* VulkanGLInterop::g_gl_context =
  43. nullptr;
  44. class VulkanGLInterop::GLNonOwnedCompatibilityContext
  45. : public gl::GLContextEGL {
  46. public:
  47. GLNonOwnedCompatibilityContext()
  48. : gl::GLContextEGL(nullptr),
  49. surface_(base::MakeRefCounted<gl::PbufferGLSurfaceEGL>(
  50. gl::GLSurfaceEGL::GetGLDisplayEGL(),
  51. gfx::Size(1, 1))) {
  52. gl::GLContextAttribs attribs;
  53. Initialize(surface_.get(), attribs);
  54. DCHECK(!g_gl_context);
  55. g_gl_context = this;
  56. }
  57. GLNonOwnedCompatibilityContext(const GLNonOwnedCompatibilityContext&) =
  58. delete;
  59. GLNonOwnedCompatibilityContext& operator=(
  60. const GLNonOwnedCompatibilityContext&) = delete;
  61. bool MakeCurrentImpl(gl::GLSurface* surface) override {
  62. // A GLNonOwnedCompatibilityContext may have set the GetRealCurrent()
  63. // pointer to itself, while re-using our EGL context. In these cases just
  64. // call SetCurrent to restore expected GetRealContext().
  65. if (GetHandle() == eglGetCurrentContext()) {
  66. if (surface) {
  67. // No one should change the current EGL surface without also changing
  68. // the context.
  69. DCHECK(eglGetCurrentSurface(EGL_DRAW) == surface->GetHandle());
  70. }
  71. SetCurrent(surface);
  72. return true;
  73. }
  74. return gl::GLContextEGL::MakeCurrentImpl(surface);
  75. }
  76. bool MakeCurrent() { return gl::GLContext::MakeCurrent(surface_.get()); }
  77. static scoped_refptr<GLNonOwnedCompatibilityContext> GetOrCreateInstance() {
  78. if (g_gl_context)
  79. return base::WrapRefCounted(g_gl_context);
  80. return base::WrapRefCounted(new GLNonOwnedCompatibilityContext);
  81. }
  82. private:
  83. ~GLNonOwnedCompatibilityContext() override {
  84. DCHECK_EQ(g_gl_context, this);
  85. g_gl_context = nullptr;
  86. }
  87. scoped_refptr<gl::GLSurface> surface_;
  88. };
  89. VulkanGLInterop::InFlightInteropDraw::InFlightInteropDraw(
  90. AwVulkanContextProvider* vk_context_provider)
  91. : vk_context_provider(vk_context_provider) {}
  92. VulkanGLInterop::InFlightInteropDraw::~InFlightInteropDraw() {
  93. // If |draw_context| is valid, we encountered an error during Vk drawing and
  94. // should call vkQueueWaitIdle to ensure safe shutdown.
  95. bool encountered_error = !!draw_context;
  96. if (encountered_error) {
  97. // Clean up one-off objects which may have been left alive due to an error.
  98. // Clean up |ahb_skimage| first, as doing so generates Vk commands we need
  99. // to flush before the vkQueueWaitIdle below.
  100. if (ahb_skimage) {
  101. ahb_skimage.reset();
  102. vk_context_provider->GetGrContext()->flushAndSubmit();
  103. }
  104. // We encountered an error and are not sure when our Vk objects are safe to
  105. // delete. VkQueueWaitIdle to ensure safety.
  106. vkQueueWaitIdle(vk_context_provider->queue());
  107. if (draw_context) {
  108. draw_context->releaseResources();
  109. draw_context.reset();
  110. }
  111. if (post_draw_semaphore != VK_NULL_HANDLE) {
  112. vkDestroySemaphore(vk_context_provider->device(), post_draw_semaphore,
  113. nullptr);
  114. post_draw_semaphore = VK_NULL_HANDLE;
  115. }
  116. }
  117. DCHECK(!draw_context);
  118. DCHECK(!ahb_skimage);
  119. DCHECK(post_draw_semaphore == VK_NULL_HANDLE);
  120. // Clean up re-usable components that are expected to still be alive.
  121. if (texture_id)
  122. glDeleteTextures(1, &texture_id);
  123. if (framebuffer_id)
  124. glDeleteFramebuffersEXT(1, &framebuffer_id);
  125. if (vulkan_image) {
  126. vk_context_provider->GetDeviceQueue()
  127. ->GetFenceHelper()
  128. ->EnqueueVulkanObjectCleanupForSubmittedWork(std::move(vulkan_image));
  129. }
  130. }
  131. VulkanGLInterop::VulkanGLInterop(
  132. RenderThreadManager* render_thread_manager,
  133. AwVulkanContextProvider* vulkan_context_provider)
  134. : render_thread_manager_(render_thread_manager),
  135. vulkan_context_provider_(vulkan_context_provider),
  136. gl_context_(GLNonOwnedCompatibilityContext::GetOrCreateInstance()) {
  137. DCHECK(render_thread_manager_);
  138. DCHECK(vulkan_context_provider_);
  139. }
  140. VulkanGLInterop::~VulkanGLInterop() {
  141. // Clear the queue.
  142. { auto queue = std::move(in_flight_interop_draws_); }
  143. gl_context_.reset();
  144. }
  145. void VulkanGLInterop::DrawVk(sk_sp<GrVkSecondaryCBDrawContext> draw_context,
  146. sk_sp<SkColorSpace> color_space,
  147. const HardwareRendererDrawParams& params,
  148. const OverlaysParams& overlays_params) {
  149. if (!gl_context_)
  150. return;
  151. if (!gl_context_->MakeCurrent()) {
  152. LOG(ERROR) << "Failed to make GL context current for drawing.";
  153. return;
  154. }
  155. // If |pending_draw_| is non-null, we called DrawVk twice without PostDrawVk.
  156. DCHECK(!pending_draw_);
  157. // Use a temporary to automatically clean up if we hit a failure case.
  158. std::unique_ptr<InFlightInteropDraw> pending_draw;
  159. // If we've exhausted our buffers, re-use an existing one.
  160. // TODO(ericrk): Benchmark using more than 1 buffer.
  161. if (in_flight_interop_draws_.size() >= 1 /* single buffering */) {
  162. pending_draw = std::move(in_flight_interop_draws_.front());
  163. in_flight_interop_draws_.pop();
  164. }
  165. // If prev buffer is wrong size, just re-allocate.
  166. if (pending_draw && pending_draw->ahb_image->GetSize() !=
  167. gfx::Size(params.width, params.height)) {
  168. pending_draw.reset();
  169. }
  170. // If we weren't able to re-use a previous draw, create one.
  171. if (!pending_draw) {
  172. pending_draw =
  173. std::make_unique<InFlightInteropDraw>(vulkan_context_provider_);
  174. AHardwareBuffer_Desc desc = {};
  175. desc.width = params.width;
  176. desc.height = params.height;
  177. desc.layers = 1; // number of images
  178. // TODO(ericrk): Handle other formats.
  179. desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
  180. desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER |
  181. AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER |
  182. AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
  183. AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
  184. AHardwareBuffer* buffer = nullptr;
  185. base::AndroidHardwareBufferCompat::GetInstance().Allocate(&desc, &buffer);
  186. if (!buffer) {
  187. LOG(ERROR) << "Failed to allocate AHardwareBuffer for WebView rendering.";
  188. return;
  189. }
  190. auto scoped_buffer =
  191. base::android::ScopedHardwareBufferHandle::Adopt(buffer);
  192. pending_draw->ahb_image = base::MakeRefCounted<gl::GLImageAHardwareBuffer>(
  193. gfx::Size(params.width, params.height));
  194. if (!pending_draw->ahb_image->Initialize(scoped_buffer.get(),
  195. false /* preserved */)) {
  196. LOG(ERROR) << "Failed to initialize GLImage for AHardwareBuffer.";
  197. return;
  198. }
  199. glGenTextures(1, static_cast<GLuint*>(&pending_draw->texture_id));
  200. GLenum target = GL_TEXTURE_2D;
  201. glBindTexture(target, pending_draw->texture_id);
  202. if (!pending_draw->ahb_image->BindTexImage(target)) {
  203. LOG(ERROR) << "Failed to bind GLImage for AHardwareBuffer.";
  204. return;
  205. }
  206. glBindTexture(target, 0);
  207. glGenFramebuffersEXT(1, &pending_draw->framebuffer_id);
  208. glBindFramebufferEXT(GL_FRAMEBUFFER, pending_draw->framebuffer_id);
  209. glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  210. GL_TEXTURE_2D, pending_draw->texture_id, 0);
  211. if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) !=
  212. GL_FRAMEBUFFER_COMPLETE) {
  213. LOG(ERROR) << "Failed to set up framebuffer for WebView GL drawing.";
  214. return;
  215. }
  216. }
  217. // Ask GL to wait on any Vk sync_fd before writing.
  218. gpu::InsertEglFenceAndWait(std::move(pending_draw->sync_fd));
  219. // Bind buffer and render with GL.
  220. base::ScopedFD gl_done_fd;
  221. {
  222. glBindFramebufferEXT(GL_FRAMEBUFFER, pending_draw->framebuffer_id);
  223. glViewport(0, 0, params.width, params.height);
  224. glDisable(GL_STENCIL_TEST);
  225. glDisable(GL_SCISSOR_TEST);
  226. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  227. glClear(GL_COLOR_BUFFER_BIT);
  228. render_thread_manager_->DrawOnRT(/*save_restore=*/false, params,
  229. overlays_params);
  230. gl_done_fd = gpu::CreateEglFenceAndExportFd();
  231. }
  232. pending_draw->draw_context = std::move(draw_context);
  233. // If we have a |gl_done_fd|, create a Skia GrBackendSemaphore from
  234. // |gl_done_fd| and wait.
  235. if (gl_done_fd.is_valid()) {
  236. VkSemaphore gl_done_semaphore =
  237. vulkan_context_provider_->GetVulkanImplementation()
  238. ->ImportSemaphoreHandle(
  239. vulkan_context_provider_->device(),
  240. gpu::SemaphoreHandle(
  241. VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
  242. std::move(gl_done_fd)));
  243. if (gl_done_semaphore == VK_NULL_HANDLE) {
  244. LOG(ERROR) << "Could not create Vulkan semaphore for GL completion.";
  245. return;
  246. }
  247. GrBackendSemaphore gr_semaphore;
  248. gr_semaphore.initVulkan(gl_done_semaphore);
  249. if (!pending_draw->draw_context->wait(1, &gr_semaphore)) {
  250. // If wait returns false, we must clean up the |gl_done_semaphore|.
  251. vkDestroySemaphore(vulkan_context_provider_->device(), gl_done_semaphore,
  252. nullptr);
  253. LOG(ERROR) << "Could not wait on GL completion semaphore.";
  254. return;
  255. }
  256. }
  257. // Create a VkImage and import AHB.
  258. if (!pending_draw->vulkan_image) {
  259. auto handle = base::android::ScopedHardwareBufferHandle::Create(
  260. pending_draw->ahb_image->GetAHardwareBuffer()->buffer());
  261. gfx::GpuMemoryBufferHandle gmb_handle(std::move(handle));
  262. auto* device_queue = vulkan_context_provider_->GetDeviceQueue();
  263. auto vulkan_image = gpu::VulkanImage::CreateFromGpuMemoryBufferHandle(
  264. device_queue, std::move(gmb_handle),
  265. gfx::Size(params.width, params.height), VK_FORMAT_R8G8B8A8_UNORM,
  266. /*usage=*/0, /*flags=*/0, /*image_tiling=*/VK_IMAGE_TILING_OPTIMAL,
  267. /*queue_family_index=*/VK_QUEUE_FAMILY_EXTERNAL);
  268. if (!vulkan_image) {
  269. LOG(ERROR) << "Could not create VkImage from AHB.";
  270. return;
  271. }
  272. pending_draw->image_info = gpu::CreateGrVkImageInfo(vulkan_image.get());
  273. pending_draw->vulkan_image = std::move(vulkan_image);
  274. }
  275. // Create an SkImage from AHB.
  276. GrBackendTexture backend_texture(params.width, params.height,
  277. pending_draw->image_info);
  278. pending_draw->ahb_skimage = SkImage::MakeFromTexture(
  279. vulkan_context_provider_->GetGrContext(), backend_texture,
  280. kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
  281. color_space);
  282. if (!pending_draw->ahb_skimage) {
  283. LOG(ERROR) << "Could not create SkImage from VkImage.";
  284. return;
  285. }
  286. // Draw the SkImage.
  287. SkPaint paint;
  288. paint.setBlendMode(SkBlendMode::kSrcOver);
  289. pending_draw->draw_context->getCanvas()->drawImage(
  290. pending_draw->ahb_skimage, 0, 0, SkSamplingOptions(), &paint);
  291. pending_draw->draw_context->flush();
  292. // Transfer |pending_draw| to |pending_draw_| for handling in
  293. // PostDrawVkInterop.
  294. pending_draw_ = std::move(pending_draw);
  295. }
  296. void VulkanGLInterop::PostDrawVk() {
  297. // Use a temporary to automatically clean up if we hit a failure case.
  298. std::unique_ptr<InFlightInteropDraw> pending_draw = std::move(pending_draw_);
  299. if (!gl_context_ || !pending_draw)
  300. return;
  301. // Get the final state of the SkImage so that we can pass this back to Skia
  302. // during re-use.
  303. GrBackendTexture backend_texture =
  304. pending_draw->ahb_skimage->getBackendTexture(
  305. true /* flushPendingGrContextIO */);
  306. GrVkImageInfo image_info;
  307. if (!backend_texture.getVkImageInfo(&image_info)) {
  308. LOG(ERROR) << "Could not get Vk image info.";
  309. return;
  310. }
  311. // Copy image layout to our cached image info.
  312. pending_draw->image_info.fImageLayout = image_info.fImageLayout;
  313. // Release the SkImage so that Skia transitions it back to
  314. // VK_QUEUE_FAMILY_EXTERNAL.
  315. pending_draw->ahb_skimage.reset();
  316. // Create a semaphore to track the image's transition back to external.
  317. VkExportSemaphoreCreateInfo export_info;
  318. export_info.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
  319. export_info.pNext = nullptr;
  320. export_info.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
  321. VkSemaphoreCreateInfo sem_info;
  322. sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
  323. sem_info.pNext = &export_info;
  324. sem_info.flags = 0;
  325. VkResult result =
  326. vkCreateSemaphore(vulkan_context_provider_->device(), &sem_info, nullptr,
  327. &pending_draw->post_draw_semaphore);
  328. if (result != VK_SUCCESS) {
  329. LOG(ERROR) << "Could not create VkSemaphore.";
  330. return;
  331. }
  332. GrBackendSemaphore gr_post_draw_semaphore;
  333. gr_post_draw_semaphore.initVulkan(pending_draw->post_draw_semaphore);
  334. // Flush so that we know the image's transition has been submitted and that
  335. // the |post_draw_semaphore| is pending.
  336. GrFlushInfo flushInfo;
  337. flushInfo.fNumSemaphores = 1;
  338. flushInfo.fSignalSemaphores = &gr_post_draw_semaphore;
  339. GrSemaphoresSubmitted submitted =
  340. vulkan_context_provider_->GetGrContext()->flush(flushInfo);
  341. vulkan_context_provider_->GetGrContext()->submit();
  342. if (submitted != GrSemaphoresSubmitted::kYes) {
  343. LOG(ERROR) << "Skia could not submit GrSemaphore.";
  344. return;
  345. }
  346. gpu::SemaphoreHandle semaphore_handle =
  347. vulkan_context_provider_->GetVulkanImplementation()->GetSemaphoreHandle(
  348. vulkan_context_provider_->device(),
  349. pending_draw->post_draw_semaphore);
  350. if (!semaphore_handle.is_valid()) {
  351. LOG(ERROR) << "Could not retrieve SyncFD from |post_draw_semaphore|.";
  352. return;
  353. }
  354. pending_draw->sync_fd = semaphore_handle.TakeHandle();
  355. gpu::VulkanFenceHelper* fence_helper =
  356. vulkan_context_provider_->GetDeviceQueue()->GetFenceHelper();
  357. fence_helper->EnqueueCleanupTaskForSubmittedWork(base::BindOnce(
  358. &CleanupInFlightDraw, std::move(pending_draw->draw_context),
  359. pending_draw->post_draw_semaphore));
  360. pending_draw->post_draw_semaphore = VK_NULL_HANDLE;
  361. // Add the |pending_draw| to |in_flight_interop_draws_|.
  362. in_flight_interop_draws_.push(std::move(pending_draw));
  363. // Process cleanup tasks and generate fences at the end of each PostDrawVk.
  364. // TODO(ericrk): We'd ideally combine this with the flushAndSignalSemaphores
  365. // above.
  366. fence_helper->GenerateCleanupFence();
  367. fence_helper->ProcessCleanupTasks();
  368. }
  369. void VulkanGLInterop::MakeGLContextCurrentIgnoreFailure() {
  370. if (gl_context_)
  371. gl_context_->MakeCurrent();
  372. }
  373. } // namespace android_webview