• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Flutter 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 
5 #include "vulkan_surface.h"
6 
7 #include <lib/async/default.h>
8 
9 #include <algorithm>
10 
11 #include "flutter/fml/trace_event.h"
12 #include "runtime/dart/utils/inlines.h"
13 #include "third_party/skia/include/core/SkCanvas.h"
14 #include "third_party/skia/include/gpu/GrBackendSemaphore.h"
15 #include "third_party/skia/include/gpu/GrBackendSurface.h"
16 #include "third_party/skia/include/gpu/GrContext.h"
17 
18 namespace flutter_runner {
19 
20 namespace {
21 
22 constexpr SkColorType kSkiaColorType = kBGRA_8888_SkColorType;
23 
24 }  // namespace
25 
CreateVulkanImage(vulkan::VulkanProvider & vulkan_provider,const SkISize & size,VulkanImage * out_vulkan_image)26 bool CreateVulkanImage(vulkan::VulkanProvider& vulkan_provider,
27                        const SkISize& size,
28                        VulkanImage* out_vulkan_image) {
29   TRACE_EVENT0("flutter", "CreateVulkanImage");
30 
31   FML_DCHECK(!size.isEmpty());
32   FML_DCHECK(out_vulkan_image != nullptr);
33 
34   // The image creation parameters need to be the same as those in scenic
35   // (garnet/lib/ui/gfx/resources/gpu_image.cc and
36   // garnet/public/lib/escher/util/image_utils.cc) or else the different vulkan
37   // devices may interpret the bytes differently.
38   // TODO(SCN-1369): Use API to coordinate this with scenic.
39   out_vulkan_image->vk_external_image_create_info = {
40       .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
41       .pNext = nullptr,
42       .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA,
43   };
44   out_vulkan_image->vk_image_create_info = {
45       .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
46       .pNext = &out_vulkan_image->vk_external_image_create_info,
47       .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
48       .imageType = VK_IMAGE_TYPE_2D,
49       .format = VK_FORMAT_B8G8R8A8_UNORM,
50       .extent = VkExtent3D{static_cast<uint32_t>(size.width()),
51                            static_cast<uint32_t>(size.height()), 1},
52       .mipLevels = 1,
53       .arrayLayers = 1,
54       .samples = VK_SAMPLE_COUNT_1_BIT,
55       .tiling = VK_IMAGE_TILING_OPTIMAL,
56       .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
57                VK_IMAGE_USAGE_TRANSFER_DST_BIT |
58                VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
59       .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
60       .queueFamilyIndexCount = 0,
61       .pQueueFamilyIndices = nullptr,
62       .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
63   };
64 
65   {
66     VkImage vk_image = VK_NULL_HANDLE;
67 
68     if (VK_CALL_LOG_ERROR(vulkan_provider.vk().CreateImage(
69             vulkan_provider.vk_device(),
70             &out_vulkan_image->vk_image_create_info, nullptr, &vk_image)) !=
71         VK_SUCCESS) {
72       return false;
73     }
74 
75     out_vulkan_image->vk_image = {
76         vk_image, [& vulkan_provider = vulkan_provider](VkImage image) {
77           vulkan_provider.vk().DestroyImage(vulkan_provider.vk_device(), image,
78                                             NULL);
79         }};
80   }
81 
82   vulkan_provider.vk().GetImageMemoryRequirements(
83       vulkan_provider.vk_device(), out_vulkan_image->vk_image,
84       &out_vulkan_image->vk_memory_requirements);
85 
86   return true;
87 }
88 
VulkanSurface(vulkan::VulkanProvider & vulkan_provider,sk_sp<GrContext> context,scenic::Session * session,const SkISize & size)89 VulkanSurface::VulkanSurface(vulkan::VulkanProvider& vulkan_provider,
90                              sk_sp<GrContext> context,
91                              scenic::Session* session,
92                              const SkISize& size)
93     : vulkan_provider_(vulkan_provider), session_(session), wait_(this) {
94   FML_DCHECK(session_);
95 
96   zx::vmo exported_vmo;
97   if (!AllocateDeviceMemory(std::move(context), size, exported_vmo)) {
98     FML_DLOG(INFO) << "Could not allocate device memory.";
99     return;
100   }
101 
102   uint64_t vmo_size;
103   zx_status_t status = exported_vmo.get_size(&vmo_size);
104   FML_DCHECK(status == ZX_OK);
105 
106   if (!CreateFences()) {
107     FML_DLOG(INFO) << "Could not create signal fences.";
108     return;
109   }
110 
111   scenic_memory_ = std::make_unique<scenic::Memory>(
112       session, std::move(exported_vmo), vmo_size,
113       fuchsia::images::MemoryType::VK_DEVICE_MEMORY);
114   if (!PushSessionImageSetupOps(session)) {
115     FML_DLOG(INFO) << "Could not push session image setup ops.";
116     return;
117   }
118 
119   std::fill(size_history_.begin(), size_history_.end(), SkISize::MakeEmpty());
120 
121   wait_.set_object(release_event_.get());
122   wait_.set_trigger(ZX_EVENT_SIGNALED);
123   Reset();
124 
125   valid_ = true;
126 }
127 
~VulkanSurface()128 VulkanSurface::~VulkanSurface() {
129   wait_.Cancel();
130   wait_.set_object(ZX_HANDLE_INVALID);
131 }
132 
IsValid() const133 bool VulkanSurface::IsValid() const {
134   return valid_;
135 }
136 
GetSize() const137 SkISize VulkanSurface::GetSize() const {
138   if (!valid_) {
139     return SkISize::Make(0, 0);
140   }
141 
142   return SkISize::Make(sk_surface_->width(), sk_surface_->height());
143 }
144 
SemaphoreFromEvent(const zx::event & event) const145 vulkan::VulkanHandle<VkSemaphore> VulkanSurface::SemaphoreFromEvent(
146     const zx::event& event) const {
147   VkResult result;
148   VkSemaphore semaphore;
149 
150   zx::event semaphore_event;
151   zx_status_t status = event.duplicate(ZX_RIGHT_SAME_RIGHTS, &semaphore_event);
152   if (status != ZX_OK) {
153     FML_DLOG(ERROR) << "failed to duplicate semaphore event";
154     return vulkan::VulkanHandle<VkSemaphore>();
155   }
156 
157   VkSemaphoreCreateInfo create_info = {
158       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
159       .pNext = nullptr,
160       .flags = 0,
161   };
162 
163   result = VK_CALL_LOG_ERROR(vulkan_provider_.vk().CreateSemaphore(
164       vulkan_provider_.vk_device(), &create_info, nullptr, &semaphore));
165   if (result != VK_SUCCESS) {
166     return vulkan::VulkanHandle<VkSemaphore>();
167   }
168 
169   VkImportSemaphoreZirconHandleInfoFUCHSIA import_info = {
170       .sType =
171           VK_STRUCTURE_TYPE_TEMP_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA,
172       .pNext = nullptr,
173       .semaphore = semaphore,
174       .handleType =
175           VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
176       .handle = static_cast<uint32_t>(semaphore_event.release())};
177 
178   result = VK_CALL_LOG_ERROR(
179       vulkan_provider_.vk().ImportSemaphoreZirconHandleFUCHSIA(
180           vulkan_provider_.vk_device(), &import_info));
181   if (result != VK_SUCCESS) {
182     return vulkan::VulkanHandle<VkSemaphore>();
183   }
184 
185   return vulkan::VulkanHandle<VkSemaphore>(
186       semaphore, [&vulkan_provider = vulkan_provider_](VkSemaphore semaphore) {
187         vulkan_provider.vk().DestroySemaphore(vulkan_provider.vk_device(),
188                                               semaphore, nullptr);
189       });
190 }
191 
CreateFences()192 bool VulkanSurface::CreateFences() {
193   if (zx::event::create(0, &acquire_event_) != ZX_OK) {
194     return false;
195   }
196 
197   acquire_semaphore_ = SemaphoreFromEvent(acquire_event_);
198   if (!acquire_semaphore_) {
199     FML_DLOG(ERROR) << "failed to create acquire semaphore";
200     return false;
201   }
202 
203   if (zx::event::create(0, &release_event_) != ZX_OK) {
204     return false;
205   }
206 
207   command_buffer_fence_ = vulkan_provider_.CreateFence();
208 
209   return true;
210 }
211 
AllocateDeviceMemory(sk_sp<GrContext> context,const SkISize & size,zx::vmo & exported_vmo)212 bool VulkanSurface::AllocateDeviceMemory(sk_sp<GrContext> context,
213                                          const SkISize& size,
214                                          zx::vmo& exported_vmo) {
215   if (size.isEmpty()) {
216     return false;
217   }
218 
219   VulkanImage vulkan_image;
220   if (!CreateVulkanImage(vulkan_provider_, size, &vulkan_image)) {
221     FML_DLOG(ERROR) << "Failed to create VkImage";
222     return false;
223   }
224 
225   vulkan_image_ = std::move(vulkan_image);
226   const VkMemoryRequirements& memory_reqs =
227       vulkan_image_.vk_memory_requirements;
228   const VkImageCreateInfo& image_create_info =
229       vulkan_image_.vk_image_create_info;
230 
231   uint32_t memory_type = 0;
232   for (; memory_type < 32; memory_type++) {
233     if ((memory_reqs.memoryTypeBits & (1 << memory_type))) {
234       break;
235     }
236   }
237 
238   VkExportMemoryAllocateInfoKHR export_allocate_info = {
239       .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
240       .pNext = nullptr,
241       .handleTypes =
242           VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA};
243 
244   const VkMemoryAllocateInfo alloc_info = {
245       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
246       .pNext = &export_allocate_info,
247       .allocationSize = memory_reqs.size,
248       .memoryTypeIndex = memory_type,
249   };
250 
251   {
252     TRACE_EVENT0("flutter", "vkAllocateMemory");
253     VkDeviceMemory vk_memory = VK_NULL_HANDLE;
254     if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().AllocateMemory(
255             vulkan_provider_.vk_device(), &alloc_info, NULL, &vk_memory)) !=
256         VK_SUCCESS) {
257       return false;
258     }
259 
260     vk_memory_ = {vk_memory, [& vulkan_provider =
261                                   vulkan_provider_](VkDeviceMemory memory) {
262                     vulkan_provider.vk().FreeMemory(vulkan_provider.vk_device(),
263                                                     memory, NULL);
264                   }};
265 
266     vk_memory_info_ = alloc_info;
267   }
268 
269   // Bind image memory.
270   if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().BindImageMemory(
271           vulkan_provider_.vk_device(), vulkan_image_.vk_image, vk_memory_,
272           0)) != VK_SUCCESS) {
273     return false;
274   }
275 
276   {
277     // Acquire the VMO for the device memory.
278     uint32_t vmo_handle = 0;
279 
280     VkMemoryGetZirconHandleInfoFUCHSIA get_handle_info = {
281         VK_STRUCTURE_TYPE_TEMP_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA, nullptr,
282         vk_memory_, VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA};
283     if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().GetMemoryZirconHandleFUCHSIA(
284             vulkan_provider_.vk_device(), &get_handle_info, &vmo_handle)) !=
285         VK_SUCCESS) {
286       return false;
287     }
288 
289     exported_vmo.reset(static_cast<zx_handle_t>(vmo_handle));
290   }
291 
292   // Assert that the VMO size was sufficient.
293   size_t vmo_size = 0;
294   if (exported_vmo.get_size(&vmo_size) != ZX_OK ||
295       vmo_size < memory_reqs.size) {
296     return false;
297   }
298 
299   return SetupSkiaSurface(std::move(context), size, kSkiaColorType,
300                           image_create_info, memory_reqs);
301 }
302 
SetupSkiaSurface(sk_sp<GrContext> context,const SkISize & size,SkColorType color_type,const VkImageCreateInfo & image_create_info,const VkMemoryRequirements & memory_reqs)303 bool VulkanSurface::SetupSkiaSurface(sk_sp<GrContext> context,
304                                      const SkISize& size,
305                                      SkColorType color_type,
306                                      const VkImageCreateInfo& image_create_info,
307                                      const VkMemoryRequirements& memory_reqs) {
308   if (context == nullptr) {
309     return false;
310   }
311 
312   const GrVkImageInfo image_info = {
313       vulkan_image_.vk_image,                // image
314       {vk_memory_, 0, memory_reqs.size, 0},  // alloc
315       image_create_info.tiling,              // tiling
316       image_create_info.initialLayout,       // layout
317       image_create_info.format,              // format
318       image_create_info.mipLevels,           // level count
319   };
320 
321   GrBackendRenderTarget sk_render_target(size.width(), size.height(), 0,
322                                          image_info);
323 
324   SkSurfaceProps sk_surface_props(
325       SkSurfaceProps::InitType::kLegacyFontHost_InitType);
326 
327   auto sk_surface =
328       SkSurface::MakeFromBackendRenderTarget(context.get(),             //
329                                              sk_render_target,          //
330                                              kTopLeft_GrSurfaceOrigin,  //
331                                              color_type,                //
332                                              nullptr,                   //
333                                              &sk_surface_props          //
334       );
335 
336   if (!sk_surface || sk_surface->getCanvas() == nullptr) {
337     return false;
338   }
339   sk_surface_ = std::move(sk_surface);
340 
341   return true;
342 }
343 
PushSessionImageSetupOps(scenic::Session * session)344 bool VulkanSurface::PushSessionImageSetupOps(scenic::Session* session) {
345   FML_DCHECK(scenic_memory_ != nullptr);
346 
347   if (sk_surface_ == nullptr) {
348     return false;
349   }
350 
351   fuchsia::images::ImageInfo image_info;
352   image_info.width = sk_surface_->width();
353   image_info.height = sk_surface_->height();
354   image_info.stride = 4 * sk_surface_->width();
355   image_info.pixel_format = fuchsia::images::PixelFormat::BGRA_8;
356   image_info.color_space = fuchsia::images::ColorSpace::SRGB;
357   switch (vulkan_image_.vk_image_create_info.tiling) {
358     case VK_IMAGE_TILING_OPTIMAL:
359       image_info.tiling = fuchsia::images::Tiling::GPU_OPTIMAL;
360       break;
361     case VK_IMAGE_TILING_LINEAR:
362       image_info.tiling = fuchsia::images::Tiling::LINEAR;
363       break;
364     default:
365       FML_DLOG(ERROR) << "Bad image tiling: "
366                       << vulkan_image_.vk_image_create_info.tiling;
367       return false;
368   }
369 
370   session_image_ = std::make_unique<scenic::Image>(
371       *scenic_memory_, 0 /* memory offset */, std::move(image_info));
372 
373   return session_image_ != nullptr;
374 }
375 
GetImage()376 scenic::Image* VulkanSurface::GetImage() {
377   if (!valid_) {
378     return 0;
379   }
380   return session_image_.get();
381 }
382 
GetSkiaSurface() const383 sk_sp<SkSurface> VulkanSurface::GetSkiaSurface() const {
384   return valid_ ? sk_surface_ : nullptr;
385 }
386 
BindToImage(sk_sp<GrContext> context,VulkanImage vulkan_image)387 bool VulkanSurface::BindToImage(sk_sp<GrContext> context,
388                                 VulkanImage vulkan_image) {
389   FML_DCHECK(vulkan_image.vk_memory_requirements.size <=
390              vk_memory_info_.allocationSize);
391 
392   vulkan_image_ = std::move(vulkan_image);
393 
394   // Bind image memory.
395   if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().BindImageMemory(
396           vulkan_provider_.vk_device(), vulkan_image_.vk_image, vk_memory_,
397           0)) != VK_SUCCESS) {
398     valid_ = false;
399     return false;
400   }
401 
402   const auto& extent = vulkan_image.vk_image_create_info.extent;
403   auto size = SkISize::Make(extent.width, extent.height);
404 
405   if (!SetupSkiaSurface(std::move(context), size, kSkiaColorType,
406                         vulkan_image.vk_image_create_info,
407                         vulkan_image.vk_memory_requirements)) {
408     FML_DLOG(ERROR) << "Failed to setup skia surface";
409     valid_ = false;
410     return false;
411   }
412 
413   if (sk_surface_ == nullptr) {
414     valid_ = false;
415     return false;
416   }
417 
418   if (!PushSessionImageSetupOps(session_)) {
419     FML_DLOG(ERROR) << "Could not push session image setup ops.";
420     valid_ = false;
421     return false;
422   }
423 
424   return true;
425 }
426 
AdvanceAndGetAge()427 size_t VulkanSurface::AdvanceAndGetAge() {
428   size_history_[size_history_index_] = GetSize();
429   size_history_index_ = (size_history_index_ + 1) % kSizeHistorySize;
430   age_++;
431   return age_;
432 }
433 
FlushSessionAcquireAndReleaseEvents()434 bool VulkanSurface::FlushSessionAcquireAndReleaseEvents() {
435   zx::event acquire, release;
436 
437   if (acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &acquire) != ZX_OK ||
438       release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &release) != ZX_OK) {
439     return false;
440   }
441 
442   session_->EnqueueAcquireFence(std::move(acquire));
443   session_->EnqueueReleaseFence(std::move(release));
444   age_ = 0;
445   return true;
446 }
447 
SignalWritesFinished(std::function<void (void)> on_writes_committed)448 void VulkanSurface::SignalWritesFinished(
449     std::function<void(void)> on_writes_committed) {
450   FML_DCHECK(on_writes_committed);
451 
452   if (!valid_) {
453     on_writes_committed();
454     return;
455   }
456 
457   dart_utils::Check(pending_on_writes_committed_ == nullptr,
458                     "Attempted to signal a write on the surface when the "
459                     "previous write has not yet been acknowledged by the "
460                     "compositor.");
461 
462   pending_on_writes_committed_ = on_writes_committed;
463 }
464 
Reset()465 void VulkanSurface::Reset() {
466   if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
467       release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
468     valid_ = false;
469     FML_DLOG(ERROR)
470         << "Could not reset fences. The surface is no longer valid.";
471   }
472 
473   VkFence fence = command_buffer_fence_;
474 
475   if (command_buffer_) {
476     VK_CALL_LOG_ERROR(vulkan_provider_.vk().WaitForFences(
477         vulkan_provider_.vk_device(), 1, &fence, VK_TRUE, UINT64_MAX));
478     command_buffer_.reset();
479   }
480 
481   VK_CALL_LOG_ERROR(vulkan_provider_.vk().ResetFences(
482       vulkan_provider_.vk_device(), 1, &fence));
483 
484   // Need to make a new  acquire semaphore every frame or else validation layers
485   // get confused about why no one is waiting on it in this VkInstance
486   acquire_semaphore_.Reset();
487   acquire_semaphore_ = SemaphoreFromEvent(acquire_event_);
488   if (!acquire_semaphore_) {
489     FML_DLOG(ERROR) << "failed to create acquire semaphore";
490   }
491 
492   wait_.Begin(async_get_default_dispatcher());
493 
494   // It is safe for the caller to collect the surface in the callback.
495   auto callback = pending_on_writes_committed_;
496   pending_on_writes_committed_ = nullptr;
497   if (callback) {
498     callback();
499   }
500 }
501 
OnHandleReady(async_dispatcher_t * dispatcher,async::WaitBase * wait,zx_status_t status,const zx_packet_signal_t * signal)502 void VulkanSurface::OnHandleReady(async_dispatcher_t* dispatcher,
503                                   async::WaitBase* wait,
504                                   zx_status_t status,
505                                   const zx_packet_signal_t* signal) {
506   if (status != ZX_OK)
507     return;
508   FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED);
509   Reset();
510 }
511 
512 }  // namespace flutter_runner
513