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