Flutter Engine
The Flutter Engine
main.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 <chrono>
6#include <cstdlib>
7#include <iostream>
8#include <optional>
9#include <tuple>
10#include <vector>
11
12// Use vulkan.hpp's convenient proc table and resolver.
13#define VULKAN_HPP_NO_EXCEPTIONS 1
14#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
15#include "vulkan/vulkan.hpp"
16VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
17
18// Convenient reference to vulkan.hpp's global proc table.
19auto& d = vk::defaultDispatchLoaderDynamic;
20
21// GLFW needs to be included after Vulkan.
22#include "GLFW/glfw3.h"
23
24#include "embedder.h" // Flutter's Embedder ABI.
25
26static const bool g_enable_validation_layers = true;
27// This value is calculated after the window is created.
28static double g_pixelRatio = 1.0;
29static const size_t kInitialWindowWidth = 800;
30static const size_t kInitialWindowHeight = 600;
31// Use `VK_PRESENT_MODE_FIFO_KHR` for full vsync (one swap per screen refresh),
32// `VK_PRESENT_MODE_MAILBOX_KHR` for continual swap without horizontal tearing,
33// or `VK_PRESENT_MODE_IMMEDIATE_KHR` for no vsync.
35static constexpr FlutterViewId kImplicitViewId = 0;
36
37static_assert(FLUTTER_ENGINE_VERSION == 1,
38 "This Flutter Embedder was authored against the stable Flutter "
39 "API at version 1. There has been a serious breakage in the "
40 "API. Please read the ChangeLog and take appropriate action "
41 "before updating this assertion");
42
43/// Global struct for holding the Window+Vulkan state.
44struct {
45 GLFWwindow* window;
46
47 std::vector<const char*> enabled_instance_extensions;
48 VkInstance instance;
49 VkSurfaceKHR surface;
50
51 VkPhysicalDevice physical_device;
52 std::vector<const char*> enabled_device_extensions;
53 VkDevice device;
55 VkQueue queue;
56
58 std::vector<VkCommandBuffer> present_transition_buffers;
59
62
64 VkSwapchainKHR swapchain;
65 std::vector<VkImage> swapchain_images;
67
69
70 bool resize_pending = false;
72
73void GLFW_ErrorCallback(int error, const char* description) {
74 std::cerr << "GLFW Error: (" << error << ") " << description << std::endl;
75}
76
79 double x,
80 double y) {
81 FlutterPointerEvent event = {};
82 event.struct_size = sizeof(event);
83 event.phase = phase;
84 event.x = x * g_pixelRatio;
85 event.y = y * g_pixelRatio;
86 event.timestamp =
87 std::chrono::duration_cast<std::chrono::microseconds>(
88 std::chrono::high_resolution_clock::now().time_since_epoch())
89 .count();
90 // This example only supports a single window, therefore we assume the event
91 // occurred in the only view, the implicit view.
92 event.view_id = kImplicitViewId;
94}
95
96void GLFWcursorPositionCallback(GLFWwindow* window, double x, double y) {
98}
99
101 int key,
102 int action,
103 int mods) {
104 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) {
105 double x, y;
106 glfwGetCursorPos(window, &x, &y);
108 glfwSetCursorPosCallback(window, GLFWcursorPositionCallback);
109 }
110
111 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_RELEASE) {
112 double x, y;
113 glfwGetCursorPos(window, &x, &y);
115 glfwSetCursorPosCallback(window, nullptr);
116 }
117}
118
119void GLFWKeyCallback(GLFWwindow* window,
120 int key,
121 int scancode,
122 int action,
123 int mods) {
124 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
125 glfwSetWindowShouldClose(window, GLFW_TRUE);
126 }
127}
128
129void GLFWframebufferSizeCallback(GLFWwindow* window, int width, int height) {
130 g_state.resize_pending = true;
131
132 FlutterWindowMetricsEvent event = {};
133 event.struct_size = sizeof(event);
134 event.width = width;
135 event.height = height;
136 event.pixel_ratio = g_pixelRatio;
137 // This example only supports a single window, therefore we assume the event
138 // occurred in the only view, the implicit view.
139 event.view_id = kImplicitViewId;
141}
142
144 std::cerr
145 << "usage: embedder_example_vulkan <path to project> <path to icudtl.dat>"
146 << std::endl;
147}
148
150 if (g_state.resize_pending) {
151 g_state.resize_pending = false;
152 d.vkDestroySwapchainKHR(g_state.device, g_state.swapchain, nullptr);
153
154 d.vkQueueWaitIdle(g_state.queue);
155 d.vkResetCommandPool(g_state.device, g_state.swapchain_command_pool,
157 }
158
159 /// --------------------------------------------------------------------------
160 /// Choose an image format that can be presented to the surface, preferring
161 /// the common BGRA+sRGB if available.
162 /// --------------------------------------------------------------------------
163
164 uint32_t format_count;
165 d.vkGetPhysicalDeviceSurfaceFormatsKHR(
166 g_state.physical_device, g_state.surface, &format_count, nullptr);
167 std::vector<VkSurfaceFormatKHR> formats(format_count);
168 d.vkGetPhysicalDeviceSurfaceFormatsKHR(
169 g_state.physical_device, g_state.surface, &format_count, formats.data());
170 assert(!formats.empty()); // Shouldn't be possible.
171
172 g_state.surface_format = formats[0];
173 for (const auto& format : formats) {
174 if (format.format == VK_FORMAT_B8G8R8A8_UNORM &&
176 g_state.surface_format = format;
177 }
178 }
179
180 /// --------------------------------------------------------------------------
181 /// Choose the presentable image size that's as close as possible to the
182 /// window size.
183 /// --------------------------------------------------------------------------
184
185 VkExtent2D extent;
186
187 VkSurfaceCapabilitiesKHR surface_capabilities;
188 d.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
189 g_state.physical_device, g_state.surface, &surface_capabilities);
190
191 if (surface_capabilities.currentExtent.width != UINT32_MAX) {
192 // If the surface reports a specific extent, we must use it.
193 extent = surface_capabilities.currentExtent;
194 } else {
195 // `glfwGetWindowSize` returns the window size in screen coordinates, so we
196 // instead use `glfwGetFramebufferSize` to get the size in pixels in order
197 // to properly support high DPI displays.
198 int width, height;
199 glfwGetFramebufferSize(g_state.window, &width, &height);
200
201 VkExtent2D actual_extent = {
202 .width = static_cast<uint32_t>(width),
203 .height = static_cast<uint32_t>(height),
204 };
205 actual_extent.width =
206 std::max(surface_capabilities.minImageExtent.width,
207 std::min(surface_capabilities.maxImageExtent.width,
208 actual_extent.width));
209 actual_extent.height =
210 std::max(surface_capabilities.minImageExtent.height,
211 std::min(surface_capabilities.maxImageExtent.height,
212 actual_extent.height));
213 }
214
215 /// --------------------------------------------------------------------------
216 /// Choose the present mode.
217 /// --------------------------------------------------------------------------
218
219 uint32_t mode_count;
220 d.vkGetPhysicalDeviceSurfacePresentModesKHR(
221 g_state.physical_device, g_state.surface, &mode_count, nullptr);
222 std::vector<VkPresentModeKHR> modes(mode_count);
223 d.vkGetPhysicalDeviceSurfacePresentModesKHR(
224 g_state.physical_device, g_state.surface, &mode_count, modes.data());
225 assert(!formats.empty()); // Shouldn't be possible.
226
227 // If the preferred mode isn't available, just choose the first one.
228 VkPresentModeKHR present_mode = modes[0];
229 for (const auto& mode : modes) {
231 present_mode = mode;
232 break;
233 }
234 }
235
236 /// --------------------------------------------------------------------------
237 /// Create the swapchain.
238 /// --------------------------------------------------------------------------
239
242 .surface = g_state.surface,
243 .minImageCount = surface_capabilities.minImageCount + 1,
244 .imageFormat = g_state.surface_format.format,
245 .imageColorSpace = g_state.surface_format.colorSpace,
246 .imageExtent = extent,
247 .imageArrayLayers = 1,
249 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
250 .queueFamilyIndexCount = 0,
251 .pQueueFamilyIndices = nullptr,
252 .preTransform = surface_capabilities.currentTransform,
253 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
254 .presentMode = present_mode,
255 .clipped = true,
256 };
257 if (d.vkCreateSwapchainKHR(g_state.device, &info, nullptr,
258 &g_state.swapchain) != VK_SUCCESS) {
259 return false;
260 }
261
262 /// --------------------------------------------------------------------------
263 /// Fetch swapchain images.
264 /// --------------------------------------------------------------------------
265
266 uint32_t image_count;
267 d.vkGetSwapchainImagesKHR(g_state.device, g_state.swapchain, &image_count,
268 nullptr);
269 g_state.swapchain_images.resize(image_count);
270 d.vkGetSwapchainImagesKHR(g_state.device, g_state.swapchain, &image_count,
271 g_state.swapchain_images.data());
272
273 /// --------------------------------------------------------------------------
274 /// Record a command buffer for each of the images to be executed prior to
275 /// presenting.
276 /// --------------------------------------------------------------------------
277
278 g_state.present_transition_buffers.resize(g_state.swapchain_images.size());
279
280 VkCommandBufferAllocateInfo buffers_info = {
282 .commandPool = g_state.swapchain_command_pool,
284 .commandBufferCount =
285 static_cast<uint32_t>(g_state.present_transition_buffers.size()),
286 };
287 d.vkAllocateCommandBuffers(g_state.device, &buffers_info,
288 g_state.present_transition_buffers.data());
289
290 for (size_t i = 0; i < g_state.swapchain_images.size(); i++) {
291 auto image = g_state.swapchain_images[i];
292 auto buffer = g_state.present_transition_buffers[i];
293
294 VkCommandBufferBeginInfo begin_info = {
296 d.vkBeginCommandBuffer(buffer, &begin_info);
297
298 // Flutter Engine hands back the image after writing to it
299 VkImageMemoryBarrier barrier = {
301 .srcAccessMask = 0,
302 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
305 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
306 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
307 .image = image,
308 .subresourceRange = {
309 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
310 .baseMipLevel = 0,
311 .levelCount = 1,
312 .baseArrayLayer = 0,
313 .layerCount = 1,
314 }};
315 d.vkCmdPipelineBarrier(
316 buffer, // commandBuffer
319 0, // dependencyFlags
320 0, // memoryBarrierCount
321 nullptr, // pMemoryBarriers
322 0, // bufferMemoryBarrierCount
323 nullptr, // pBufferMemoryBarriers
324 1, // imageMemoryBarrierCount
325 &barrier // pImageMemoryBarriers
326 );
327
328 d.vkEndCommandBuffer(buffer);
329 }
330
331 return true; // \o/
332}
333
335 void* user_data,
336 const FlutterFrameInfo* frame_info) {
337 // If the GLFW framebuffer has been resized, discard the swapchain and create
338 // a new one.
339 if (g_state.resize_pending) {
341 }
342
343 d.vkAcquireNextImageKHR(g_state.device, g_state.swapchain, UINT64_MAX,
344 nullptr, g_state.image_ready_fence,
345 &g_state.last_image_index);
346
347 // Flutter Engine expects the image to be available for transitioning and
348 // attaching immediately, and so we need to force a host sync here before
349 // returning.
350 d.vkWaitForFences(g_state.device, 1, &g_state.image_ready_fence, true,
351 UINT64_MAX);
352 d.vkResetFences(g_state.device, 1, &g_state.image_ready_fence);
353
354 return {
355 .struct_size = sizeof(FlutterVulkanImage),
356 .image = reinterpret_cast<uint64_t>(
357 g_state.swapchain_images[g_state.last_image_index]),
358 .format = g_state.surface_format.format,
359 };
360}
361
363 VkPipelineStageFlags stage_flags =
365 VkSubmitInfo submit_info = {
367 .waitSemaphoreCount = 0,
368 .pWaitSemaphores = nullptr,
369 .pWaitDstStageMask = &stage_flags,
370 .commandBufferCount = 1,
371 .pCommandBuffers =
372 &g_state.present_transition_buffers[g_state.last_image_index],
373 .signalSemaphoreCount = 1,
374 .pSignalSemaphores = &g_state.present_transition_semaphore,
375 };
376 d.vkQueueSubmit(g_state.queue, 1, &submit_info, nullptr);
377
378 VkPresentInfoKHR present_info = {
380 .waitSemaphoreCount = 1,
381 .pWaitSemaphores = &g_state.present_transition_semaphore,
382 .swapchainCount = 1,
383 .pSwapchains = &g_state.swapchain,
384 .pImageIndices = &g_state.last_image_index,
385 };
386 VkResult result = d.vkQueuePresentKHR(g_state.queue, &present_info);
387
388 // If the swapchain is no longer compatible with the surface, discard the
389 // swapchain and create a new one.
392 }
393 d.vkQueueWaitIdle(g_state.queue);
394
395 return result == VK_SUCCESS;
396}
397
399 void* user_data,
401 const char* procname) {
402 auto* proc = glfwGetInstanceProcAddress(
403 reinterpret_cast<VkInstance>(instance), procname);
404 return reinterpret_cast<void*>(proc);
405}
406
407int main(int argc, char** argv) {
408 if (argc != 3) {
409 PrintUsage();
410 return 1;
411 }
412
413 std::string project_path = argv[1];
414 std::string icudtl_path = argv[2];
415
416 /// --------------------------------------------------------------------------
417 /// Create a GLFW window.
418 /// --------------------------------------------------------------------------
419
420 {
421 if (!glfwInit()) {
422 std::cerr << "Failed to initialize GLFW." << std::endl;
423 return EXIT_FAILURE;
424 }
425
426 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
427 g_state.window = glfwCreateWindow(kInitialWindowWidth, kInitialWindowHeight,
428 "Flutter", nullptr, nullptr);
429 if (!g_state.window) {
430 std::cerr << "Failed to create GLFW window." << std::endl;
431 return EXIT_FAILURE;
432 }
433
434 int framebuffer_width, framebuffer_height;
435 glfwGetFramebufferSize(g_state.window, &framebuffer_width,
436 &framebuffer_height);
437 g_pixelRatio = framebuffer_width / kInitialWindowWidth;
438
439 glfwSetErrorCallback(GLFW_ErrorCallback);
440 }
441
442 /// --------------------------------------------------------------------------
443 /// Dynamically load the Vulkan loader with GLFW and use it to populate GLAD's
444 /// proc table.
445 /// --------------------------------------------------------------------------
446
447 if (!glfwVulkanSupported()) {
448 std::cerr << "GLFW was unable to resolve either a Vulkan loader or a "
449 "compatible physical device!"
450 << std::endl;
451#if defined(__APPLE__)
452 std::cerr
453 << "NOTE: Apple platforms don't ship with a Vulkan loader or any "
454 "Vulkan drivers. Follow this guide to set up a Vulkan loader on "
455 "macOS and use the MoltenVK ICD: "
456 "https://vulkan.lunarg.com/doc/sdk/latest/mac/getting_started.html"
457 << std::endl;
458#endif
459 return EXIT_FAILURE;
460 }
461
462 VULKAN_HPP_DEFAULT_DISPATCHER.init(glfwGetInstanceProcAddress);
463
464 /// --------------------------------------------------------------------------
465 /// Create a Vulkan instance.
466 /// --------------------------------------------------------------------------
467
468 {
469 uint32_t extension_count;
470 const char** glfw_extensions =
471 glfwGetRequiredInstanceExtensions(&extension_count);
472 g_state.enabled_instance_extensions.resize(extension_count);
473 memcpy(g_state.enabled_instance_extensions.data(), glfw_extensions,
474 extension_count * sizeof(char*));
475
477 g_state.enabled_instance_extensions.push_back(
479 }
480
481 std::cout << "Enabling " << g_state.enabled_instance_extensions.size()
482 << " instance extensions:" << std::endl;
483 for (const auto& extension : g_state.enabled_instance_extensions) {
484 std::cout << " - " << extension << std::endl;
485 }
486
487 VkApplicationInfo app_info = {
489 .pNext = nullptr,
490 .pApplicationName = "Flutter",
491 .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
492 .pEngineName = "No Engine",
493 .engineVersion = VK_MAKE_VERSION(1, 0, 0),
494 .apiVersion = VK_MAKE_VERSION(1, 1, 0),
495 };
498 info.flags = 0;
499 info.pApplicationInfo = &app_info;
500 info.enabledExtensionCount = g_state.enabled_instance_extensions.size();
501 info.ppEnabledExtensionNames = g_state.enabled_instance_extensions.data();
503 auto available_layers = vk::enumerateInstanceLayerProperties();
504
505 const char* layer = "VK_LAYER_KHRONOS_validation";
506 for (const auto& l : available_layers.value) {
507 if (strcmp(l.layerName, layer) == 0) {
508 info.enabledLayerCount = 1;
509 info.ppEnabledLayerNames = &layer;
510 break;
511 }
512 }
513 }
514
515 if (d.vkCreateInstance(&info, nullptr, &g_state.instance) != VK_SUCCESS) {
516 std::cerr << "Failed to create Vulkan instance." << std::endl;
517 return EXIT_FAILURE;
518 }
519 }
520
521 // Load instance procs.
522 VULKAN_HPP_DEFAULT_DISPATCHER.init(vk::Instance(g_state.instance));
523
524 /// --------------------------------------------------------------------------
525 /// Create the window surface.
526 /// --------------------------------------------------------------------------
527
528 if (glfwCreateWindowSurface(g_state.instance, g_state.window, NULL,
529 &g_state.surface) != VK_SUCCESS) {
530 std::cerr << "Failed to create window surface." << std::endl;
531 return EXIT_FAILURE;
532 }
533
534 /// --------------------------------------------------------------------------
535 /// Select a compatible physical device.
536 /// --------------------------------------------------------------------------
537
538 {
539 uint32_t count;
540 d.vkEnumeratePhysicalDevices(g_state.instance, &count, nullptr);
541 std::vector<VkPhysicalDevice> physical_devices(count);
542 d.vkEnumeratePhysicalDevices(g_state.instance, &count,
543 physical_devices.data());
544
545 std::cout << "Enumerating " << count << " physical device(s)." << std::endl;
546
547 uint32_t selected_score = 0;
548 for (const auto& pdevice : physical_devices) {
551 d.vkGetPhysicalDeviceProperties(pdevice, &properties);
552 d.vkGetPhysicalDeviceFeatures(pdevice, &features);
553
554 std::cout << "Checking device: " << properties.deviceName << std::endl;
555
556 uint32_t score = 0;
557 std::vector<const char*> supported_extensions;
558
559 uint32_t qfp_count;
560 d.vkGetPhysicalDeviceQueueFamilyProperties(pdevice, &qfp_count, nullptr);
561 std::vector<VkQueueFamilyProperties> qfp(qfp_count);
562 d.vkGetPhysicalDeviceQueueFamilyProperties(pdevice, &qfp_count,
563 qfp.data());
564 std::optional<uint32_t> graphics_queue_family;
565 for (uint32_t i = 0; i < qfp.size(); i++) {
566 // Only pick graphics queues that can also present to the surface.
567 // Graphics queues that can't present are rare if not nonexistent, but
568 // the spec allows for this, so check it anyways.
569 VkBool32 surface_present_supported;
570 d.vkGetPhysicalDeviceSurfaceSupportKHR(pdevice, i, g_state.surface,
571 &surface_present_supported);
572
573 if (!graphics_queue_family.has_value() &&
574 qfp[i].queueFlags & VK_QUEUE_GRAPHICS_BIT &&
575 surface_present_supported) {
576 graphics_queue_family = i;
577 }
578 }
579
580 // Skip physical devices that don't have a graphics queue.
581 if (!graphics_queue_family.has_value()) {
582 std::cout << " - Skipping due to no suitable graphics queues."
583 << std::endl;
584 continue;
585 }
586
587 // Prefer discrete GPUs.
589 score += 1 << 30;
590 }
591
592 uint32_t extension_count;
593 d.vkEnumerateDeviceExtensionProperties(pdevice, nullptr, &extension_count,
594 nullptr);
595 std::vector<VkExtensionProperties> available_extensions(extension_count);
596 d.vkEnumerateDeviceExtensionProperties(pdevice, nullptr, &extension_count,
597 available_extensions.data());
598
599 bool supports_swapchain = false;
600 for (const auto& available_extension : available_extensions) {
602 available_extension.extensionName) == 0) {
603 supports_swapchain = true;
604 supported_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
605 }
606 // The spec requires VK_KHR_portability_subset be enabled whenever it's
607 // available on a device. It's present on compatibility ICDs like
608 // MoltenVK.
609 else if (strcmp("VK_KHR_portability_subset",
610 available_extension.extensionName) == 0) {
611 supported_extensions.push_back("VK_KHR_portability_subset");
612 }
613
614 // Prefer GPUs that support VK_KHR_get_memory_requirements2.
616 available_extension.extensionName) == 0) {
617 score += 1 << 29;
618 supported_extensions.push_back(
620 }
621 }
622
623 // Skip physical devices that don't have swapchain support.
624 if (!supports_swapchain) {
625 std::cout << " - Skipping due to lack of swapchain support."
626 << std::endl;
627 continue;
628 }
629
630 // Prefer GPUs with larger max texture sizes.
631 score += properties.limits.maxImageDimension2D;
632
633 if (selected_score < score) {
634 std::cout << " - This is the best device so far. Score: 0x" << std::hex
635 << score << std::dec << std::endl;
636
637 selected_score = score;
638 g_state.physical_device = pdevice;
639 g_state.enabled_device_extensions = supported_extensions;
640 g_state.queue_family_index = graphics_queue_family.value_or(
642 }
643 }
644
645 if (g_state.physical_device == nullptr) {
646 std::cerr << "Failed to find a compatible Vulkan physical device."
647 << std::endl;
648 return EXIT_FAILURE;
649 }
650 }
651
652 /// --------------------------------------------------------------------------
653 /// Create a logical device and a graphics queue handle.
654 /// --------------------------------------------------------------------------
655
656 std::cout << "Enabling " << g_state.enabled_device_extensions.size()
657 << " device extensions:" << std::endl;
658 for (const char* extension : g_state.enabled_device_extensions) {
659 std::cout << " - " << extension << std::endl;
660 }
661
662 {
663 VkPhysicalDeviceFeatures device_features = {};
664
665 VkDeviceQueueCreateInfo graphics_queue = {};
667 graphics_queue.queueFamilyIndex = g_state.queue_family_index;
668 graphics_queue.queueCount = 1;
669 float priority = 1.0f;
670 graphics_queue.pQueuePriorities = &priority;
671
672 VkDeviceCreateInfo device_info = {};
674 device_info.enabledExtensionCount =
675 g_state.enabled_device_extensions.size();
676 device_info.ppEnabledExtensionNames =
677 g_state.enabled_device_extensions.data();
678 device_info.pEnabledFeatures = &device_features;
679 device_info.queueCreateInfoCount = 1;
680 device_info.pQueueCreateInfos = &graphics_queue;
681
682 if (d.vkCreateDevice(g_state.physical_device, &device_info, nullptr,
683 &g_state.device) != VK_SUCCESS) {
684 std::cerr << "Failed to create Vulkan logical device." << std::endl;
685 return EXIT_FAILURE;
686 }
687 }
688
689 d.vkGetDeviceQueue(g_state.device, g_state.queue_family_index, 0,
690 &g_state.queue);
691
692 /// --------------------------------------------------------------------------
693 /// Create sync primitives and command pool to use in the render loop
694 /// callbacks.
695 /// --------------------------------------------------------------------------
696
697 {
699 d.vkCreateFence(g_state.device, &f_info, nullptr,
700 &g_state.image_ready_fence);
701
702 VkSemaphoreCreateInfo s_info = {
704 d.vkCreateSemaphore(g_state.device, &s_info, nullptr,
705 &g_state.present_transition_semaphore);
706
707 VkCommandPoolCreateInfo pool_info = {
709 .queueFamilyIndex = g_state.queue_family_index,
710 };
711 d.vkCreateCommandPool(g_state.device, &pool_info, nullptr,
712 &g_state.swapchain_command_pool);
713 }
714
715 /// --------------------------------------------------------------------------
716 /// Create swapchain.
717 /// --------------------------------------------------------------------------
718
719 if (!InitializeSwapchain()) {
720 std::cerr << "Failed to create swapchain." << std::endl;
721 return EXIT_FAILURE;
722 }
723
724 /// --------------------------------------------------------------------------
725 /// Start Flutter Engine.
726 /// --------------------------------------------------------------------------
727
728 {
729 FlutterRendererConfig config = {};
730 config.type = kVulkan;
731 config.vulkan.struct_size = sizeof(config.vulkan);
732 config.vulkan.version = VK_MAKE_VERSION(1, 1, 0);
733 config.vulkan.instance = g_state.instance;
734 config.vulkan.physical_device = g_state.physical_device;
735 config.vulkan.device = g_state.device;
736 config.vulkan.queue_family_index = g_state.queue_family_index;
737 config.vulkan.queue = g_state.queue;
739 g_state.enabled_instance_extensions.size();
741 g_state.enabled_instance_extensions.data();
743 g_state.enabled_device_extensions.size();
745 g_state.enabled_device_extensions.data();
750
751 // This directory is generated by `flutter build bundle`.
752 std::string assets_path = project_path + "/build/flutter_assets";
754 .struct_size = sizeof(FlutterProjectArgs),
755 .assets_path = assets_path.c_str(),
756 .icu_data_path =
757 icudtl_path.c_str(), // Find this in your bin/cache directory.
758 };
761 &g_state.engine);
762 if (result != kSuccess || g_state.engine == nullptr) {
763 std::cerr << "Failed to start Flutter Engine." << std::endl;
764 return EXIT_FAILURE;
765 }
766
767 // Trigger a FlutterEngineSendWindowMetricsEvent to communicate the initial
768 // size of the window.
769 int width, height;
770 glfwGetFramebufferSize(g_state.window, &width, &height);
772 g_state.resize_pending = false;
773 }
774
775 /// --------------------------------------------------------------------------
776 /// GLFW render loop.
777 /// --------------------------------------------------------------------------
778
779 glfwSetKeyCallback(g_state.window, GLFWKeyCallback);
780 glfwSetFramebufferSizeCallback(g_state.window, GLFWframebufferSizeCallback);
781 glfwSetMouseButtonCallback(g_state.window, GLFWmouseButtonCallback);
782
783 while (!glfwWindowShouldClose(g_state.window)) {
784 glfwWaitEvents();
785 }
786
787 /// --------------------------------------------------------------------------
788 /// Cleanup.
789 /// --------------------------------------------------------------------------
790
791 if (FlutterEngineShutdown(g_state.engine) != kSuccess) {
792 std::cerr << "Flutter Engine shutdown failed." << std::endl;
793 }
794
795 d.vkDestroyCommandPool(g_state.device, g_state.swapchain_command_pool,
796 nullptr);
797 d.vkDestroySemaphore(g_state.device, g_state.present_transition_semaphore,
798 nullptr);
799 d.vkDestroyFence(g_state.device, g_state.image_ready_fence, nullptr);
800
801 d.vkDestroyDevice(g_state.device, nullptr);
802 d.vkDestroySurfaceKHR(g_state.instance, g_state.surface, nullptr);
803 d.vkDestroyInstance(g_state.instance, nullptr);
804
805 glfwDestroyWindow(g_state.window);
806 glfwTerminate();
807
808 return 0;
809}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
int count
Definition: FontMgrTest.cpp:50
FlutterEngineResult FlutterEngineRun(size_t version, const FlutterRendererConfig *config, const FlutterProjectArgs *args, void *user_data, FLUTTER_API_SYMBOL(FlutterEngine) *engine_out)
Initialize and run a Flutter engine instance and return a handle to it. This is a convenience method ...
Definition: embedder.cc:1715
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition: embedder.cc:2320
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Shuts down a Flutter engine instance. The engine handle is no longer valid for any calls in the embed...
Definition: embedder.cc:2309
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition: embedder.cc:2429
@ kVulkan
Definition: embedder.h:86
FlutterPointerPhase
The phase of the pointer event.
Definition: embedder.h:965
@ kUp
Definition: embedder.h:973
@ kDown
Definition: embedder.h:980
@ kMove
Definition: embedder.h:985
FlutterEngineResult
Definition: embedder.h:72
@ kSuccess
Definition: embedder.h:73
int64_t FlutterViewId
Definition: embedder.h:275
void * FlutterVulkanInstanceHandle
Alias for VkInstance.
Definition: embedder.h:708
#define FLUTTER_ENGINE_VERSION
Definition: embedder.h:70
bool InitializeSwapchain()
Definition: main.cc:149
VkSwapchainKHR swapchain
Definition: main.cc:64
std::vector< VkCommandBuffer > present_transition_buffers
Definition: main.cc:58
int main(int argc, char **argv)
Definition: main.cc:407
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
bool resize_pending
Definition: main.cc:70
uint32_t last_image_index
Definition: main.cc:66
FlutterVulkanImage FlutterGetNextImageCallback(void *user_data, const FlutterFrameInfo *frame_info)
Definition: main.cc:334
bool FlutterPresentCallback(void *user_data, const FlutterVulkanImage *image)
Definition: main.cc:362
void PrintUsage()
Definition: main.cc:143
void GLFWframebufferSizeCallback(GLFWwindow *window, int width, int height)
Definition: main.cc:129
VkPhysicalDevice physical_device
Definition: main.cc:51
std::vector< const char * > enabled_device_extensions
Definition: main.cc:52
GLFWwindow * window
Definition: main.cc:45
static const bool g_enable_validation_layers
Definition: main.cc:26
VkDevice device
Definition: main.cc:53
static double g_pixelRatio
Definition: main.cc:28
FlutterEngine engine
Definition: main.cc:68
VkCommandPool swapchain_command_pool
Definition: main.cc:57
static constexpr FlutterViewId kImplicitViewId
Definition: main.cc:35
static const VkPresentModeKHR kPreferredPresentMode
Definition: main.cc:34
void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, FlutterPointerPhase phase, double x, double y)
Definition: main.cc:77
VkInstance instance
Definition: main.cc:48
VkQueue queue
Definition: main.cc:55
std::vector< const char * > enabled_instance_extensions
Definition: main.cc:47
VkSurfaceFormatKHR surface_format
Definition: main.cc:63
static const size_t kInitialWindowHeight
Definition: main.cc:30
VkSemaphore present_transition_semaphore
Definition: main.cc:61
void * FlutterGetInstanceProcAddressCallback(void *user_data, FlutterVulkanInstanceHandle instance, const char *procname)
Definition: main.cc:398
static const size_t kInitialWindowWidth
Definition: main.cc:29
void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
Definition: main.cc:119
VkFence image_ready_fence
Definition: main.cc:60
struct @13 g_state
Global struct for holding the Window+Vulkan state.
void GLFWcursorPositionCallback(GLFWwindow *window, double x, double y)
Definition: main.cc:96
uint32_t queue_family_index
Definition: main.cc:54
void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, int mods)
Definition: main.cc:100
std::vector< VkImage > swapchain_images
Definition: main.cc:65
VkSurfaceKHR surface
Definition: main.cc:49
void GLFW_ErrorCallback(int error, const char *description)
Definition: main.cc:73
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent * event
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
uint32_t uint32_t * format
#define GLFW_TRUE
Definition: flutter_glfw.cc:33
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
char ** argv
Definition: library.h:9
double y
double x
sk_sp< const SkImage > image
Definition: SkRecords.h:269
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
int32_t height
int32_t width
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition: embedder.h:1036
FlutterVulkanRendererConfig vulkan
Definition: embedder.h:832
FlutterRendererType type
Definition: embedder.h:827
FlutterVulkanQueueHandle queue
Definition: embedder.h:773
FlutterVulkanDeviceHandle device
Definition: embedder.h:764
FlutterVulkanInstanceProcAddressCallback get_instance_proc_address_callback
Definition: embedder.h:802
size_t struct_size
The size of this struct. Must be sizeof(FlutterVulkanRendererConfig).
Definition: embedder.h:752
size_t enabled_instance_extension_count
Definition: embedder.h:776
uint32_t queue_family_index
The queue family index of the VkQueue supplied in the next field.
Definition: embedder.h:766
FlutterVulkanImageCallback get_next_image_callback
Definition: embedder.h:806
const char ** enabled_instance_extensions
Definition: embedder.h:783
const char ** enabled_device_extensions
Definition: embedder.h:794
size_t enabled_device_extension_count
Definition: embedder.h:786
FlutterVulkanInstanceHandle instance
Definition: embedder.h:759
FlutterVulkanPresentCallback present_image_callback
Definition: embedder.h:812
FlutterVulkanPhysicalDeviceHandle physical_device
VkPhysicalDevice handle.
Definition: embedder.h:761
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition: embedder.h:843
VkStructureType sType
Definition: vulkan_core.h:3003
VkStructureType sType
Definition: vulkan_core.h:3891
VkStructureType sType
Definition: vulkan_core.h:3865
uint32_t enabledExtensionCount
Definition: vulkan_core.h:3265
const VkDeviceQueueCreateInfo * pQueueCreateInfos
Definition: vulkan_core.h:3262
const VkPhysicalDeviceFeatures * pEnabledFeatures
Definition: vulkan_core.h:3267
const char *const * ppEnabledExtensionNames
Definition: vulkan_core.h:3266
uint32_t queueCreateInfoCount
Definition: vulkan_core.h:3261
VkStructureType sType
Definition: vulkan_core.h:3258
const float * pQueuePriorities
Definition: vulkan_core.h:3254
VkStructureType sType
Definition: vulkan_core.h:3249
uint32_t width
Definition: vulkan_core.h:2858
uint32_t height
Definition: vulkan_core.h:2859
VkStructureType sType
Definition: vulkan_core.h:3386
VkStructureType sType
Definition: vulkan_core.h:2936
VkPhysicalDeviceType deviceType
Definition: vulkan_core.h:3234
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]
Definition: vulkan_core.h:3235
VkPhysicalDeviceLimits limits
Definition: vulkan_core.h:3237
VkStructureType sType
Definition: vulkan_core.h:7748
VkStructureType sType
Definition: vulkan_core.h:3392
VkStructureType sType
Definition: vulkan_core.h:3283
VkSurfaceTransformFlagBitsKHR currentTransform
Definition: vulkan_core.h:7656
void * user_data
VkFlags VkPipelineStageFlags
Definition: vulkan_core.h:2470
@ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
Definition: vulkan_core.h:1348
@ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
Definition: vulkan_core.h:1333
@ VK_COMMAND_BUFFER_LEVEL_PRIMARY
Definition: vulkan_core.h:2178
@ VK_SHARING_MODE_EXCLUSIVE
Definition: vulkan_core.h:1813
@ VK_IMAGE_ASPECT_COLOR_BIT
Definition: vulkan_core.h:2238
@ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
Definition: vulkan_core.h:2356
@ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
Definition: vulkan_core.h:7640
#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME
@ VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT
Definition: vulkan_core.h:2824
#define VK_MAKE_VERSION(major, minor, patch)
Definition: vulkan_core.h:78
#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME
Definition: vulkan_core.h:9396
@ VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
Definition: vulkan_core.h:1783
@ VK_QUEUE_GRAPHICS_BIT
Definition: vulkan_core.h:2413
VkResult
Definition: vulkan_core.h:140
@ VK_SUBOPTIMAL_KHR
Definition: vulkan_core.h:167
@ VK_SUCCESS
Definition: vulkan_core.h:141
@ VK_ERROR_OUT_OF_DATE_KHR
Definition: vulkan_core.h:168
@ VK_ACCESS_MEMORY_READ_BIT
Definition: vulkan_core.h:2215
VkPresentModeKHR
Definition: vulkan_core.h:7594
@ VK_PRESENT_MODE_FIFO_KHR
Definition: vulkan_core.h:7597
@ VK_FORMAT_B8G8R8A8_UNORM
Definition: vulkan_core.h:1503
@ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
Definition: vulkan_core.h:7605
uint32_t VkBool32
Definition: vulkan_core.h:94
#define VK_KHR_SWAPCHAIN_EXTENSION_NAME
Definition: vulkan_core.h:7707
@ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
Definition: vulkan_core.h:2445
@ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
Definition: vulkan_core.h:2448
#define VK_QUEUE_FAMILY_IGNORED
Definition: vulkan_core.h:127
@ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
Definition: vulkan_core.h:211
@ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO
Definition: vulkan_core.h:204
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
Definition: vulkan_core.h:242
@ VK_STRUCTURE_TYPE_APPLICATION_INFO
Definition: vulkan_core.h:202
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
Definition: vulkan_core.h:244
@ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
Definition: vulkan_core.h:203
@ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
Definition: vulkan_core.h:418
@ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
Definition: vulkan_core.h:210
@ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
Definition: vulkan_core.h:205
@ VK_STRUCTURE_TYPE_SUBMIT_INFO
Definition: vulkan_core.h:206
@ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO
Definition: vulkan_core.h:241
@ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR
Definition: vulkan_core.h:419
@ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER
Definition: vulkan_core.h:247