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