Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
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) {
230 if (mode == kPreferredPresentMode) {
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(
641 std::numeric_limits<uint32_t>::max());
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";
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
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:1711
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2314
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:2303
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition embedder.cc:2423
@ kVulkan
Definition embedder.h:86
FlutterPointerPhase
The phase of the pointer event.
Definition embedder.h:963
@ kUp
Definition embedder.h:971
@ kDown
Definition embedder.h:978
@ kMove
Definition embedder.h:983
FlutterEngineResult
Definition embedder.h:72
@ kSuccess
Definition embedder.h:73
int64_t FlutterViewId
Definition embedder.h:273
void * FlutterVulkanInstanceHandle
Alias for VkInstance.
Definition embedder.h:706
#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
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
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
FlutterEngine engine
Definition main.cc:68
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
sk_sp< SkImage > image
Definition examples.cpp:29
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent * event
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
uint32_t uint32_t * format
#define GLFW_TRUE
char ** argv
Definition library.h:9
double y
double x
Definition main.py:1
int32_t height
int32_t width
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition embedder.h:1034
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
Definition embedder.h:2138
FlutterVulkanRendererConfig vulkan
Definition embedder.h:830
FlutterRendererType type
Definition embedder.h:825
FlutterVulkanQueueHandle queue
Definition embedder.h:771
FlutterVulkanDeviceHandle device
Definition embedder.h:762
FlutterVulkanInstanceProcAddressCallback get_instance_proc_address_callback
Definition embedder.h:800
size_t struct_size
The size of this struct. Must be sizeof(FlutterVulkanRendererConfig).
Definition embedder.h:750
size_t enabled_instance_extension_count
Definition embedder.h:774
uint32_t queue_family_index
The queue family index of the VkQueue supplied in the next field.
Definition embedder.h:764
FlutterVulkanImageCallback get_next_image_callback
Definition embedder.h:804
const char ** enabled_instance_extensions
Definition embedder.h:781
const char ** enabled_device_extensions
Definition embedder.h:792
FlutterVulkanInstanceHandle instance
Definition embedder.h:757
FlutterVulkanPresentCallback present_image_callback
Definition embedder.h:810
FlutterVulkanPhysicalDeviceHandle physical_device
VkPhysicalDevice handle.
Definition embedder.h:759
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:841
VkStructureType sType
VkStructureType sType
uint32_t enabledExtensionCount
const VkDeviceQueueCreateInfo * pQueueCreateInfos
const VkPhysicalDeviceFeatures * pEnabledFeatures
const char *const * ppEnabledExtensionNames
uint32_t queueCreateInfoCount
VkStructureType sType
const float * pQueuePriorities
VkStructureType sType
uint32_t width
uint32_t height
VkStructureType sType
VkStructureType sType
VkStructureType sType
VkPhysicalDeviceType deviceType
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]
VkPhysicalDeviceLimits limits
VkStructureType sType
VkStructureType sType
VkStructureType sType
VkSurfaceTransformFlagBitsKHR currentTransform
VkFlags VkPipelineStageFlags
@ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
@ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
@ VK_COMMAND_BUFFER_LEVEL_PRIMARY
@ VK_SHARING_MODE_EXCLUSIVE
@ VK_IMAGE_ASPECT_COLOR_BIT
@ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
@ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME
@ VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT
#define VK_MAKE_VERSION(major, minor, patch)
Definition vulkan_core.h:78
#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME
@ VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
@ VK_QUEUE_GRAPHICS_BIT
VkResult
@ VK_SUBOPTIMAL_KHR
@ VK_SUCCESS
@ VK_ERROR_OUT_OF_DATE_KHR
@ VK_ACCESS_MEMORY_READ_BIT
VkPresentModeKHR
@ VK_PRESENT_MODE_FIFO_KHR
@ VK_FORMAT_B8G8R8A8_UNORM
@ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
uint32_t VkBool32
Definition vulkan_core.h:94
#define VK_KHR_SWAPCHAIN_EXTENSION_NAME
@ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
@ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
#define VK_QUEUE_FAMILY_IGNORED
@ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
@ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
@ VK_STRUCTURE_TYPE_APPLICATION_INFO
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
@ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
@ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
@ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
@ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
@ VK_STRUCTURE_TYPE_SUBMIT_INFO
@ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO
@ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR
@ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER