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 #include <lib/ui/scenic/cpp/commands.h>
9 
10 #include <algorithm>
11 
12 #include "flutter/fml/trace_event.h"
14 #include "third_party/skia/include/core/SkCanvas.h"
15 #include "third_party/skia/include/gpu/GrBackendSemaphore.h"
16 #include "third_party/skia/include/gpu/GrBackendSurface.h"
17 #include "third_party/skia/include/gpu/GrDirectContext.h"
18 
19 #define LOG_AND_RETURN(cond, msg) \
20  if (cond) { \
21  FML_LOG(ERROR) << msg; \
22  return false; \
23  }
24 
25 namespace flutter_runner {
26 
27 namespace {
28 
29 constexpr SkColorType kSkiaColorType = kRGBA_8888_SkColorType;
30 constexpr VkFormat kVulkanFormat = VK_FORMAT_R8G8B8A8_UNORM;
31 constexpr VkImageCreateFlags kVulkanImageCreateFlags = 0;
32 // TODO: We should only keep usages that are actually required by Skia.
33 constexpr VkImageUsageFlags kVkImageUsage =
34  VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
35  VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
36 
37 } // namespace
38 
39 bool VulkanSurface::CreateVulkanImage(vulkan::VulkanProvider& vulkan_provider,
40  const SkISize& size,
41  VulkanImage* out_vulkan_image) {
42  TRACE_EVENT0("flutter", "CreateVulkanImage");
43 
44  FML_CHECK(!size.isEmpty());
45  FML_CHECK(out_vulkan_image != nullptr);
46 
47  out_vulkan_image->vk_collection_image_create_info = {
48  .sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIAX,
49  .pNext = nullptr,
50  .collection = collection_,
51  .index = 0,
52  };
53 
54  out_vulkan_image->vk_image_create_info = {
55  .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
56  .pNext = &out_vulkan_image->vk_collection_image_create_info,
57  .flags = kVulkanImageCreateFlags,
58  .imageType = VK_IMAGE_TYPE_2D,
59  .format = kVulkanFormat,
60  .extent = VkExtent3D{static_cast<uint32_t>(size.width()),
61  static_cast<uint32_t>(size.height()), 1},
62  .mipLevels = 1,
63  .arrayLayers = 1,
64  .samples = VK_SAMPLE_COUNT_1_BIT,
65  .tiling = VK_IMAGE_TILING_OPTIMAL,
66  .usage = kVkImageUsage,
67  .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
68  .queueFamilyIndexCount = 0,
69  .pQueueFamilyIndices = nullptr,
70  .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
71  };
72 
74  vulkan_provider.vk().SetBufferCollectionConstraintsFUCHSIAX(
75  vulkan_provider.vk_device(), collection_,
76  &out_vulkan_image->vk_image_create_info)) != VK_SUCCESS) {
77  return false;
78  }
79 
80  {
81  VkImage vk_image = VK_NULL_HANDLE;
82  if (VK_CALL_LOG_ERROR(vulkan_provider.vk().CreateImage(
83  vulkan_provider.vk_device(),
84  &out_vulkan_image->vk_image_create_info, nullptr, &vk_image)) !=
85  VK_SUCCESS) {
86  return false;
87  }
88 
89  out_vulkan_image->vk_image = vulkan::VulkanHandle<VkImage_T*>{
90  vk_image, [&vulkan_provider = vulkan_provider](VkImage image) {
91  vulkan_provider.vk().DestroyImage(vulkan_provider.vk_device(), image,
92  NULL);
93  }};
94  }
95 
96  vulkan_provider.vk().GetImageMemoryRequirements(
97  vulkan_provider.vk_device(), out_vulkan_image->vk_image,
98  &out_vulkan_image->vk_memory_requirements);
99 
100  return true;
101 }
102 
104  vulkan::VulkanProvider& vulkan_provider,
105  fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
106  fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
107  sk_sp<GrDirectContext> context,
108  scenic::Session* session,
109  const SkISize& size,
110  uint32_t buffer_id)
111  : vulkan_provider_(vulkan_provider), session_(session), wait_(this) {
112  FML_CHECK(session_ || flatland_allocator.is_bound());
113  FML_CHECK(context != nullptr);
114 
115  if (!AllocateDeviceMemory(sysmem_allocator, flatland_allocator,
116  std::move(context), size, buffer_id)) {
117  FML_LOG(ERROR) << "VulkanSurface: Could not allocate device memory.";
118  return;
119  }
120 
121  if (!CreateFences()) {
122  FML_LOG(ERROR) << "VulkanSurface: Could not create signal fences.";
123  return;
124  }
125 
126  PushSessionImageSetupOps(session);
127 
128  std::fill(size_history_.begin(), size_history_.end(), SkISize::MakeEmpty());
129 
130  wait_.set_object(release_event_.get());
131  wait_.set_trigger(ZX_EVENT_SIGNALED);
132  Reset();
133 
134  valid_ = true;
135 }
136 
138  if (session_) {
139  if (image_id_) {
140  session_->Enqueue(scenic::NewReleaseResourceCmd(image_id_));
141  }
142  if (buffer_id_) {
143  session_->DeregisterBufferCollection(buffer_id_);
144  }
145  } else {
146  release_image_callback_();
147  }
148  wait_.Cancel();
149  wait_.set_object(ZX_HANDLE_INVALID);
150 }
151 
153  return valid_;
154 }
155 
156 SkISize VulkanSurface::GetSize() const {
157  if (!valid_) {
158  return SkISize::Make(0, 0);
159  }
160 
161  return SkISize::Make(sk_surface_->width(), sk_surface_->height());
162 }
163 
164 vulkan::VulkanHandle<VkSemaphore> VulkanSurface::SemaphoreFromEvent(
165  const zx::event& event) const {
166  VkResult result;
167  VkSemaphore semaphore;
168 
169  zx::event semaphore_event;
170  zx_status_t status = event.duplicate(ZX_RIGHT_SAME_RIGHTS, &semaphore_event);
171  if (status != ZX_OK) {
172  FML_LOG(ERROR) << "VulkanSurface: Failed to duplicate semaphore event";
174  }
175 
176  VkSemaphoreCreateInfo create_info = {
177  .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
178  .pNext = nullptr,
179  .flags = 0,
180  };
181 
182  result = VK_CALL_LOG_ERROR(vulkan_provider_.vk().CreateSemaphore(
183  vulkan_provider_.vk_device(), &create_info, nullptr, &semaphore));
184  if (result != VK_SUCCESS) {
186  }
187 
188  VkImportSemaphoreZirconHandleInfoFUCHSIA import_info = {
189  .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA,
190  .pNext = nullptr,
191  .semaphore = semaphore,
192  .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA,
193  .zirconHandle = 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  FML_LOG(ERROR) << "VulkanSurface: Failed to create acquire event";
212  return false;
213  }
214 
215  acquire_semaphore_ = SemaphoreFromEvent(acquire_event_);
216  if (!acquire_semaphore_) {
217  FML_LOG(ERROR) << "VulkanSurface: Failed to create acquire semaphore";
218  return false;
219  }
220 
221  if (zx::event::create(0, &release_event_) != ZX_OK) {
222  FML_LOG(ERROR) << "VulkanSurface: Failed to create release event";
223  return false;
224  }
225 
226  command_buffer_fence_ = vulkan_provider_.CreateFence();
227 
228  return true;
229 }
230 
231 bool VulkanSurface::AllocateDeviceMemory(
232  fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
233  fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
234  sk_sp<GrDirectContext> context,
235  const SkISize& size,
236  uint32_t buffer_id) {
237  if (size.isEmpty()) {
238  FML_LOG(ERROR)
239  << "VulkanSurface: Failed to allocate surface, size is empty";
240  return false;
241  }
242 
243  fuchsia::sysmem::BufferCollectionTokenSyncPtr vulkan_token;
244  zx_status_t status =
245  sysmem_allocator->AllocateSharedCollection(vulkan_token.NewRequest());
246  LOG_AND_RETURN(status != ZX_OK,
247  "VulkanSurface: Failed to allocate collection");
248  fuchsia::sysmem::BufferCollectionTokenSyncPtr scenic_token;
249  status = vulkan_token->Duplicate(std::numeric_limits<uint32_t>::max(),
250  scenic_token.NewRequest());
251  LOG_AND_RETURN(status != ZX_OK,
252  "VulkanSurface: Failed to duplicate collection token");
253  status = vulkan_token->Sync();
254  LOG_AND_RETURN(status != ZX_OK,
255  "VulkanSurface: Failed to sync collection token");
256 
257  if (session_) {
258  session_->RegisterBufferCollection(buffer_id, std::move(scenic_token));
259  buffer_id_ = buffer_id;
260  } else {
261  fuchsia::ui::composition::BufferCollectionExportToken export_token;
262  status =
263  zx::eventpair::create(0, &export_token.value, &import_token_.value);
264 
265  fuchsia::ui::composition::RegisterBufferCollectionArgs args;
266  args.set_export_token(std::move(export_token));
267  args.set_buffer_collection_token(std::move(scenic_token));
268  args.set_usage(
269  fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
270  flatland_allocator->RegisterBufferCollection(
271  std::move(args),
272  [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
273  result) {
274  if (result.is_err()) {
275  FML_LOG(ERROR)
276  << "RegisterBufferCollection call to Scenic Allocator failed";
277  }
278  });
279  }
280 
281  VkBufferCollectionCreateInfoFUCHSIAX import_info{
282  .sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIAX,
283  .pNext = nullptr,
284  .collectionToken = vulkan_token.Unbind().TakeChannel().release(),
285  };
286  VkBufferCollectionFUCHSIAX collection;
287  if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().CreateBufferCollectionFUCHSIAX(
288  vulkan_provider_.vk_device(), &import_info, nullptr, &collection)) !=
289  VK_SUCCESS) {
290  return false;
291  }
292 
294  collection, [&vulkan_provider = vulkan_provider_](
295  VkBufferCollectionFUCHSIAX collection) {
296  vulkan_provider.vk().DestroyBufferCollectionFUCHSIAX(
297  vulkan_provider.vk_device(), collection, nullptr);
298  }};
299 
300  VulkanImage vulkan_image;
301  LOG_AND_RETURN(!CreateVulkanImage(vulkan_provider_, size, &vulkan_image),
302  "VulkanSurface: Failed to create VkImage");
303 
304  vulkan_image_ = std::move(vulkan_image);
305  const VkMemoryRequirements& memory_requirements =
306  vulkan_image_.vk_memory_requirements;
307  VkImageCreateInfo& image_create_info = vulkan_image_.vk_image_create_info;
308 
309  VkBufferCollectionPropertiesFUCHSIAX properties{
310  .sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIAX};
311  if (VK_CALL_LOG_ERROR(
312  vulkan_provider_.vk().GetBufferCollectionPropertiesFUCHSIAX(
313  vulkan_provider_.vk_device(), collection_, &properties)) !=
314  VK_SUCCESS) {
315  return false;
316  }
317 
318  VkImportMemoryBufferCollectionFUCHSIAX import_memory_info = {
319  .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIAX,
320  .pNext = nullptr,
321  .collection = collection_,
322  .index = 0,
323  };
324  auto bits = memory_requirements.memoryTypeBits & properties.memoryTypeBits;
325  FML_DCHECK(bits != 0);
326  VkMemoryAllocateInfo allocation_info = {
327  .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
328  .pNext = &import_memory_info,
329  .allocationSize = memory_requirements.size,
330  .memoryTypeIndex = static_cast<uint32_t>(__builtin_ctz(bits)),
331  };
332 
333  {
334  TRACE_EVENT1("flutter", "vkAllocateMemory", "allocationSize",
335  allocation_info.allocationSize);
336  VkDeviceMemory vk_memory = VK_NULL_HANDLE;
337  if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().AllocateMemory(
338  vulkan_provider_.vk_device(), &allocation_info, NULL,
339  &vk_memory)) != VK_SUCCESS) {
340  return false;
341  }
342 
344  vk_memory,
345  [&vulkan_provider = vulkan_provider_](VkDeviceMemory memory) {
346  vulkan_provider.vk().FreeMemory(vulkan_provider.vk_device(), memory,
347  NULL);
348  }};
349 
350  vk_memory_info_ = allocation_info;
351  }
352 
353  // Bind image memory.
354  if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().BindImageMemory(
355  vulkan_provider_.vk_device(), vulkan_image_.vk_image, vk_memory_,
356  0)) != VK_SUCCESS) {
357  return false;
358  }
359 
360  return SetupSkiaSurface(std::move(context), size, kSkiaColorType,
361  image_create_info, memory_requirements);
362 }
363 
364 bool VulkanSurface::SetupSkiaSurface(sk_sp<GrDirectContext> context,
365  const SkISize& size,
366  SkColorType color_type,
367  const VkImageCreateInfo& image_create_info,
368  const VkMemoryRequirements& memory_reqs) {
369  FML_CHECK(context != nullptr);
370 
371  GrVkAlloc alloc;
372  alloc.fMemory = vk_memory_;
373  alloc.fOffset = 0;
374  alloc.fSize = memory_reqs.size;
375  alloc.fFlags = 0;
376 
377  GrVkImageInfo image_info;
378  image_info.fImage = vulkan_image_.vk_image;
379  image_info.fAlloc = alloc;
380  image_info.fImageTiling = image_create_info.tiling;
381  image_info.fImageLayout = image_create_info.initialLayout;
382  image_info.fFormat = image_create_info.format;
383  image_info.fImageUsageFlags = image_create_info.usage;
384  image_info.fSampleCount = 1;
385  image_info.fLevelCount = image_create_info.mipLevels;
386 
387  GrBackendRenderTarget sk_render_target(size.width(), size.height(), 0,
388  image_info);
389 
390  SkSurfaceProps sk_surface_props(0, kUnknown_SkPixelGeometry);
391 
392  auto sk_surface =
393  SkSurface::MakeFromBackendRenderTarget(context.get(), //
394  sk_render_target, //
395  kTopLeft_GrSurfaceOrigin, //
396  color_type, //
397  SkColorSpace::MakeSRGB(), //
398  &sk_surface_props //
399  );
400 
401  if (!sk_surface || sk_surface->getCanvas() == nullptr) {
402  FML_LOG(ERROR)
403  << "VulkanSurface: SkSurface::MakeFromBackendRenderTarget failed";
404  return false;
405  }
406  sk_surface_ = std::move(sk_surface);
407 
408  return true;
409 }
410 
411 void VulkanSurface::PushSessionImageSetupOps(scenic::Session* session) {
412  if (session) {
413  if (image_id_ == 0)
414  image_id_ = session->AllocResourceId();
415  session->Enqueue(scenic::NewCreateImage2Cmd(
416  image_id_, sk_surface_->width(), sk_surface_->height(), buffer_id_, 0));
417  }
418 }
419 
420 void VulkanSurface::SetImageId(uint32_t image_id) {
421  FML_CHECK(image_id_ == 0);
422  FML_CHECK(!session_);
423  image_id_ = image_id;
424 }
425 
427  return image_id_;
428 }
429 
430 sk_sp<SkSurface> VulkanSurface::GetSkiaSurface() const {
431  return valid_ ? sk_surface_ : nullptr;
432 }
433 
434 fuchsia::ui::composition::BufferCollectionImportToken
436  FML_CHECK(!session_);
437  fuchsia::ui::composition::BufferCollectionImportToken import_dup;
438  import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value);
439  return import_dup;
440 }
441 
443  FML_CHECK(!session_);
444  zx::event fence;
445  acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
446  return fence;
447 }
448 
450  FML_CHECK(!session_);
451  zx::event fence;
452  release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
453  return fence;
454 }
456  ReleaseImageCallback release_image_callback) {
457  FML_CHECK(!session_);
458  release_image_callback_ = release_image_callback;
459 }
460 
462  size_history_[size_history_index_] = GetSize();
463  size_history_index_ = (size_history_index_ + 1) % kSizeHistorySize;
464  age_++;
465  return age_;
466 }
467 
469  if (session_) {
470  zx::event acquire, release;
471  if (acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &acquire) != ZX_OK ||
472  release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &release) != ZX_OK) {
473  return false;
474  }
475  session_->EnqueueAcquireFence(std::move(acquire));
476  session_->EnqueueReleaseFence(std::move(release));
477  }
478 
479  age_ = 0;
480  return true;
481 }
482 
484  const std::function<void(void)>& on_writes_committed) {
485  FML_CHECK(on_writes_committed);
486 
487  if (!valid_) {
488  on_writes_committed();
489  return;
490  }
491 
492  dart_utils::Check(pending_on_writes_committed_ == nullptr,
493  "Attempted to signal a write on the surface when the "
494  "previous write has not yet been acknowledged by the "
495  "compositor.");
496 
497  pending_on_writes_committed_ = on_writes_committed;
498 }
499 
500 void VulkanSurface::Reset() {
501  if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
502  release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
503  valid_ = false;
504  FML_LOG(ERROR) << "Could not reset fences. The surface is no longer valid.";
505  }
506 
507  VkFence fence = command_buffer_fence_;
508 
509  if (command_buffer_) {
510  VK_CALL_LOG_ERROR(vulkan_provider_.vk().WaitForFences(
511  vulkan_provider_.vk_device(), 1, &fence, VK_TRUE, UINT64_MAX));
512  command_buffer_.reset();
513  }
514 
515  VK_CALL_LOG_ERROR(vulkan_provider_.vk().ResetFences(
516  vulkan_provider_.vk_device(), 1, &fence));
517 
518  // Need to make a new acquire semaphore every frame or else validation layers
519  // get confused about why no one is waiting on it in this VkInstance
520  acquire_semaphore_.Reset();
521  acquire_semaphore_ = SemaphoreFromEvent(acquire_event_);
522  if (!acquire_semaphore_) {
523  FML_LOG(ERROR) << "failed to create acquire semaphore";
524  }
525 
526  wait_.Begin(async_get_default_dispatcher());
527 
528  // It is safe for the caller to collect the surface in the callback.
529  auto callback = pending_on_writes_committed_;
530  pending_on_writes_committed_ = nullptr;
531  if (callback) {
532  callback();
533  }
534 }
535 
536 void VulkanSurface::OnHandleReady(async_dispatcher_t* dispatcher,
537  async::WaitBase* wait,
538  zx_status_t status,
539  const zx_packet_signal_t* signal) {
540  if (status != ZX_OK)
541  return;
542  FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED);
543  Reset();
544 }
545 
546 } // namespace flutter_runner
VkImageCreateInfo vk_image_create_info
G_BEGIN_DECLS FlValue * args
sk_sp< SkSurface > GetSkiaSurface() const override
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
#define FML_DCHECK(condition)
Definition: logging.h:86
#define LOG_AND_RETURN(cond, msg)
VulkanSurface(vulkan::VulkanProvider &vulkan_provider, fuchsia::sysmem::AllocatorSyncPtr &sysmem_allocator, fuchsia::ui::composition::AllocatorPtr &flatland_allocator, sk_sp< GrDirectContext > context, scenic::Session *session, const SkISize &size, uint32_t buffer_id)
SkISize GetSize() const override
uint32_t color_type
fuchsia::ui::composition::BufferCollectionImportToken GetBufferCollectionImportToken() override
Dart_NativeFunction function
Definition: fuchsia.cc:51
GAsyncResult * result
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
vulkan::VulkanHandle< VkFence > CreateFence()
#define FML_LOG(severity)
Definition: logging.h:65
FlKeyEvent FlKeyResponderAsyncCallback callback
FlKeyEvent * event
bool IsValid() const override
VkMemoryRequirements vk_memory_requirements
virtual const vulkan::VulkanHandle< VkDevice > & vk_device()=0
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
std::function< void()> ReleaseImageCallback
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
Definition: trace_event.h:94
#define FML_CHECK(condition)
Definition: logging.h:68
void SetImageId(uint32_t image_id) override
uint32_t GetImageId() override
void SetReleaseImageCallback(ReleaseImageCallback release_image_callback) override
zx::event GetReleaseFence() override
size_t AdvanceAndGetAge() override
zx::event GetAcquireFence() override
vulkan::VulkanHandle< VkImage > vk_image