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.
35
36static_assert(FLUTTER_ENGINE_VERSION == 1,
37 "This Flutter Embedder was authored against the stable Flutter "
38 "API at version 1. There has been a serious breakage in the "
39 "API. Please read the ChangeLog and take appropriate action "
40 "before updating this assertion");
41
42/// Global struct for holding the Window+Vulkan state.
43struct {
44 GLFWwindow* window;
45
46 std::vector<const char*> enabled_instance_extensions;
47 VkInstance instance;
48 VkSurfaceKHR surface;
49
50 VkPhysicalDevice physical_device;
51 std::vector<const char*> enabled_device_extensions;
52 VkDevice device;
54 VkQueue queue;
55
57 std::vector<VkCommandBuffer> present_transition_buffers;
58
61
63 VkSwapchainKHR swapchain;
64 std::vector<VkImage> swapchain_images;
66
68
69 bool resize_pending = false;
71
72void GLFW_ErrorCallback(int error, const char* description) {
73 std::cerr << "GLFW Error: (" << error << ") " << description << std::endl;
74}
75
78 double x,
79 double y) {
80 FlutterPointerEvent event = {};
81 event.struct_size = sizeof(event);
82 event.phase = phase;
83 event.x = x * g_pixelRatio;
84 event.y = y * g_pixelRatio;
85 event.timestamp =
86 std::chrono::duration_cast<std::chrono::microseconds>(
87 std::chrono::high_resolution_clock::now().time_since_epoch())
88 .count();
90}
91
92void GLFWcursorPositionCallback(GLFWwindow* window, double x, double y) {
94}
95
97 int key,
98 int action,
99 int mods) {
100 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) {
101 double x, y;
102 glfwGetCursorPos(window, &x, &y);
104 glfwSetCursorPosCallback(window, GLFWcursorPositionCallback);
105 }
106
107 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_RELEASE) {
108 double x, y;
109 glfwGetCursorPos(window, &x, &y);
111 glfwSetCursorPosCallback(window, nullptr);
112 }
113}
114
115void GLFWKeyCallback(GLFWwindow* window,
116 int key,
117 int scancode,
118 int action,
119 int mods) {
120 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
121 glfwSetWindowShouldClose(window, GLFW_TRUE);
122 }
123}
124
125void GLFWframebufferSizeCallback(GLFWwindow* window, int width, int height) {
126 g_state.resize_pending = true;
127
128 FlutterWindowMetricsEvent event = {};
129 event.struct_size = sizeof(event);
130 event.width = width;
131 event.height = height;
132 event.pixel_ratio = g_pixelRatio;
134}
135
137 std::cerr
138 << "usage: embedder_example_vulkan <path to project> <path to icudtl.dat>"
139 << std::endl;
140}
141
143 if (g_state.resize_pending) {
144 g_state.resize_pending = false;
145 d.vkDestroySwapchainKHR(g_state.device, g_state.swapchain, nullptr);
146
147 d.vkQueueWaitIdle(g_state.queue);
148 d.vkResetCommandPool(g_state.device, g_state.swapchain_command_pool,
150 }
151
152 /// --------------------------------------------------------------------------
153 /// Choose an image format that can be presented to the surface, preferring
154 /// the common BGRA+sRGB if available.
155 /// --------------------------------------------------------------------------
156
157 uint32_t format_count;
158 d.vkGetPhysicalDeviceSurfaceFormatsKHR(
159 g_state.physical_device, g_state.surface, &format_count, nullptr);
160 std::vector<VkSurfaceFormatKHR> formats(format_count);
161 d.vkGetPhysicalDeviceSurfaceFormatsKHR(
162 g_state.physical_device, g_state.surface, &format_count, formats.data());
163 assert(!formats.empty()); // Shouldn't be possible.
164
165 g_state.surface_format = formats[0];
166 for (const auto& format : formats) {
167 if (format.format == VK_FORMAT_B8G8R8A8_UNORM &&
169 g_state.surface_format = format;
170 }
171 }
172
173 /// --------------------------------------------------------------------------
174 /// Choose the presentable image size that's as close as possible to the
175 /// window size.
176 /// --------------------------------------------------------------------------
177
178 VkExtent2D extent;
179
180 VkSurfaceCapabilitiesKHR surface_capabilities;
181 d.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
182 g_state.physical_device, g_state.surface, &surface_capabilities);
183
184 if (surface_capabilities.currentExtent.width != UINT32_MAX) {
185 // If the surface reports a specific extent, we must use it.
186 extent = surface_capabilities.currentExtent;
187 } else {
188 // `glfwGetWindowSize` returns the window size in screen coordinates, so we
189 // instead use `glfwGetFramebufferSize` to get the size in pixels in order
190 // to properly support high DPI displays.
191 int width, height;
192 glfwGetFramebufferSize(g_state.window, &width, &height);
193
194 VkExtent2D actual_extent = {
195 .width = static_cast<uint32_t>(width),
196 .height = static_cast<uint32_t>(height),
197 };
198 actual_extent.width =
199 std::max(surface_capabilities.minImageExtent.width,
200 std::min(surface_capabilities.maxImageExtent.width,
201 actual_extent.width));
202 actual_extent.height =
203 std::max(surface_capabilities.minImageExtent.height,
204 std::min(surface_capabilities.maxImageExtent.height,
205 actual_extent.height));
206 }
207
208 /// --------------------------------------------------------------------------
209 /// Choose the present mode.
210 /// --------------------------------------------------------------------------
211
212 uint32_t mode_count;
213 d.vkGetPhysicalDeviceSurfacePresentModesKHR(
214 g_state.physical_device, g_state.surface, &mode_count, nullptr);
215 std::vector<VkPresentModeKHR> modes(mode_count);
216 d.vkGetPhysicalDeviceSurfacePresentModesKHR(
217 g_state.physical_device, g_state.surface, &mode_count, modes.data());
218 assert(!formats.empty()); // Shouldn't be possible.
219
220 // If the preferred mode isn't available, just choose the first one.
221 VkPresentModeKHR present_mode = modes[0];
222 for (const auto& mode : modes) {
223 if (mode == kPreferredPresentMode) {
224 present_mode = mode;
225 break;
226 }
227 }
228
229 /// --------------------------------------------------------------------------
230 /// Create the swapchain.
231 /// --------------------------------------------------------------------------
232
235 .surface = g_state.surface,
236 .minImageCount = surface_capabilities.minImageCount + 1,
237 .imageFormat = g_state.surface_format.format,
238 .imageColorSpace = g_state.surface_format.colorSpace,
239 .imageExtent = extent,
240 .imageArrayLayers = 1,
242 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
243 .queueFamilyIndexCount = 0,
244 .pQueueFamilyIndices = nullptr,
245 .preTransform = surface_capabilities.currentTransform,
246 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
247 .presentMode = present_mode,
248 .clipped = true,
249 };
250 if (d.vkCreateSwapchainKHR(g_state.device, &info, nullptr,
251 &g_state.swapchain) != VK_SUCCESS) {
252 return false;
253 }
254
255 /// --------------------------------------------------------------------------
256 /// Fetch swapchain images.
257 /// --------------------------------------------------------------------------
258
259 uint32_t image_count;
260 d.vkGetSwapchainImagesKHR(g_state.device, g_state.swapchain, &image_count,
261 nullptr);
262 g_state.swapchain_images.resize(image_count);
263 d.vkGetSwapchainImagesKHR(g_state.device, g_state.swapchain, &image_count,
264 g_state.swapchain_images.data());
265
266 /// --------------------------------------------------------------------------
267 /// Record a command buffer for each of the images to be executed prior to
268 /// presenting.
269 /// --------------------------------------------------------------------------
270
271 g_state.present_transition_buffers.resize(g_state.swapchain_images.size());
272
273 VkCommandBufferAllocateInfo buffers_info = {
275 .commandPool = g_state.swapchain_command_pool,
277 .commandBufferCount =
278 static_cast<uint32_t>(g_state.present_transition_buffers.size()),
279 };
280 d.vkAllocateCommandBuffers(g_state.device, &buffers_info,
281 g_state.present_transition_buffers.data());
282
283 for (size_t i = 0; i < g_state.swapchain_images.size(); i++) {
284 auto image = g_state.swapchain_images[i];
285 auto buffer = g_state.present_transition_buffers[i];
286
287 VkCommandBufferBeginInfo begin_info = {
289 d.vkBeginCommandBuffer(buffer, &begin_info);
290
291 // Flutter Engine hands back the image after writing to it
292 VkImageMemoryBarrier barrier = {
294 .srcAccessMask = 0,
295 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
298 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
299 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
300 .image = image,
301 .subresourceRange = {
302 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
303 .baseMipLevel = 0,
304 .levelCount = 1,
305 .baseArrayLayer = 0,
306 .layerCount = 1,
307 }};
308 d.vkCmdPipelineBarrier(
309 buffer, // commandBuffer
312 0, // dependencyFlags
313 0, // memoryBarrierCount
314 nullptr, // pMemoryBarriers
315 0, // bufferMemoryBarrierCount
316 nullptr, // pBufferMemoryBarriers
317 1, // imageMemoryBarrierCount
318 &barrier // pImageMemoryBarriers
319 );
320
321 d.vkEndCommandBuffer(buffer);
322 }
323
324 return true; // \o/
325}
326
328 void* user_data,
329 const FlutterFrameInfo* frame_info) {
330 // If the GLFW framebuffer has been resized, discard the swapchain and create
331 // a new one.
332 if (g_state.resize_pending) {
334 }
335
336 d.vkAcquireNextImageKHR(g_state.device, g_state.swapchain, UINT64_MAX,
337 nullptr, g_state.image_ready_fence,
338 &g_state.last_image_index);
339
340 // Flutter Engine expects the image to be available for transitioning and
341 // attaching immediately, and so we need to force a host sync here before
342 // returning.
343 d.vkWaitForFences(g_state.device, 1, &g_state.image_ready_fence, true,
344 UINT64_MAX);
345 d.vkResetFences(g_state.device, 1, &g_state.image_ready_fence);
346
347 return {
348 .struct_size = sizeof(FlutterVulkanImage),
349 .image = reinterpret_cast<uint64_t>(
350 g_state.swapchain_images[g_state.last_image_index]),
351 .format = g_state.surface_format.format,
352 };
353}
354
356 VkPipelineStageFlags stage_flags =
358 VkSubmitInfo submit_info = {
360 .waitSemaphoreCount = 0,
361 .pWaitSemaphores = nullptr,
362 .pWaitDstStageMask = &stage_flags,
363 .commandBufferCount = 1,
364 .pCommandBuffers =
365 &g_state.present_transition_buffers[g_state.last_image_index],
366 .signalSemaphoreCount = 1,
367 .pSignalSemaphores = &g_state.present_transition_semaphore,
368 };
369 d.vkQueueSubmit(g_state.queue, 1, &submit_info, nullptr);
370
371 VkPresentInfoKHR present_info = {
373 .waitSemaphoreCount = 1,
374 .pWaitSemaphores = &g_state.present_transition_semaphore,
375 .swapchainCount = 1,
376 .pSwapchains = &g_state.swapchain,
377 .pImageIndices = &g_state.last_image_index,
378 };
379 VkResult result = d.vkQueuePresentKHR(g_state.queue, &present_info);
380
381 // If the swapchain is no longer compatible with the surface, discard the
382 // swapchain and create a new one.
385 }
386 d.vkQueueWaitIdle(g_state.queue);
387
388 return result == VK_SUCCESS;
389}
390
392 void* user_data,
394 const char* procname) {
395 auto* proc = glfwGetInstanceProcAddress(
396 reinterpret_cast<VkInstance>(instance), procname);
397 return reinterpret_cast<void*>(proc);
398}
399
400int main(int argc, char** argv) {
401 if (argc != 3) {
402 PrintUsage();
403 return 1;
404 }
405
406 std::string project_path = argv[1];
407 std::string icudtl_path = argv[2];
408
409 /// --------------------------------------------------------------------------
410 /// Create a GLFW window.
411 /// --------------------------------------------------------------------------
412
413 {
414 if (!glfwInit()) {
415 std::cerr << "Failed to initialize GLFW." << std::endl;
416 return EXIT_FAILURE;
417 }
418
419 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
420 g_state.window = glfwCreateWindow(kInitialWindowWidth, kInitialWindowHeight,
421 "Flutter", nullptr, nullptr);
422 if (!g_state.window) {
423 std::cerr << "Failed to create GLFW window." << std::endl;
424 return EXIT_FAILURE;
425 }
426
427 int framebuffer_width, framebuffer_height;
428 glfwGetFramebufferSize(g_state.window, &framebuffer_width,
429 &framebuffer_height);
430 g_pixelRatio = framebuffer_width / kInitialWindowWidth;
431
432 glfwSetErrorCallback(GLFW_ErrorCallback);
433 }
434
435 /// --------------------------------------------------------------------------
436 /// Dynamically load the Vulkan loader with GLFW and use it to populate GLAD's
437 /// proc table.
438 /// --------------------------------------------------------------------------
439
440 if (!glfwVulkanSupported()) {
441 std::cerr << "GLFW was unable to resolve either a Vulkan loader or a "
442 "compatible physical device!"
443 << std::endl;
444#if defined(__APPLE__)
445 std::cerr
446 << "NOTE: Apple platforms don't ship with a Vulkan loader or any "
447 "Vulkan drivers. Follow this guide to set up a Vulkan loader on "
448 "macOS and use the MoltenVK ICD: "
449 "https://vulkan.lunarg.com/doc/sdk/latest/mac/getting_started.html"
450 << std::endl;
451#endif
452 return EXIT_FAILURE;
453 }
454
455 VULKAN_HPP_DEFAULT_DISPATCHER.init(glfwGetInstanceProcAddress);
456
457 /// --------------------------------------------------------------------------
458 /// Create a Vulkan instance.
459 /// --------------------------------------------------------------------------
460
461 {
462 uint32_t extension_count;
463 const char** glfw_extensions =
464 glfwGetRequiredInstanceExtensions(&extension_count);
465 g_state.enabled_instance_extensions.resize(extension_count);
466 memcpy(g_state.enabled_instance_extensions.data(), glfw_extensions,
467 extension_count * sizeof(char*));
468
470 g_state.enabled_instance_extensions.push_back(
472 }
473
474 std::cout << "Enabling " << g_state.enabled_instance_extensions.size()
475 << " instance extensions:" << std::endl;
476 for (const auto& extension : g_state.enabled_instance_extensions) {
477 std::cout << " - " << extension << std::endl;
478 }
479
480 VkApplicationInfo app_info = {
482 .pNext = nullptr,
483 .pApplicationName = "Flutter",
484 .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
485 .pEngineName = "No Engine",
486 .engineVersion = VK_MAKE_VERSION(1, 0, 0),
487 .apiVersion = VK_MAKE_VERSION(1, 1, 0),
488 };
491 info.flags = 0;
492 info.pApplicationInfo = &app_info;
493 info.enabledExtensionCount = g_state.enabled_instance_extensions.size();
494 info.ppEnabledExtensionNames = g_state.enabled_instance_extensions.data();
496 auto available_layers = vk::enumerateInstanceLayerProperties();
497
498 const char* layer = "VK_LAYER_KHRONOS_validation";
499 for (const auto& l : available_layers.value) {
500 if (strcmp(l.layerName, layer) == 0) {
501 info.enabledLayerCount = 1;
502 info.ppEnabledLayerNames = &layer;
503 break;
504 }
505 }
506 }
507
508 if (d.vkCreateInstance(&info, nullptr, &g_state.instance) != VK_SUCCESS) {
509 std::cerr << "Failed to create Vulkan instance." << std::endl;
510 return EXIT_FAILURE;
511 }
512 }
513
514 // Load instance procs.
515 VULKAN_HPP_DEFAULT_DISPATCHER.init(g_state.instance);
516
517 /// --------------------------------------------------------------------------
518 /// Create the window surface.
519 /// --------------------------------------------------------------------------
520
521 if (glfwCreateWindowSurface(g_state.instance, g_state.window, NULL,
522 &g_state.surface) != VK_SUCCESS) {
523 std::cerr << "Failed to create window surface." << std::endl;
524 return EXIT_FAILURE;
525 }
526
527 /// --------------------------------------------------------------------------
528 /// Select a compatible physical device.
529 /// --------------------------------------------------------------------------
530
531 {
532 uint32_t count;
533 d.vkEnumeratePhysicalDevices(g_state.instance, &count, nullptr);
534 std::vector<VkPhysicalDevice> physical_devices(count);
535 d.vkEnumeratePhysicalDevices(g_state.instance, &count,
536 physical_devices.data());
537
538 std::cout << "Enumerating " << count << " physical device(s)." << std::endl;
539
540 uint32_t selected_score = 0;
541 for (const auto& pdevice : physical_devices) {
544 d.vkGetPhysicalDeviceProperties(pdevice, &properties);
545 d.vkGetPhysicalDeviceFeatures(pdevice, &features);
546
547 std::cout << "Checking device: " << properties.deviceName << std::endl;
548
549 uint32_t score = 0;
550 std::vector<const char*> supported_extensions;
551
552 uint32_t qfp_count;
553 d.vkGetPhysicalDeviceQueueFamilyProperties(pdevice, &qfp_count, nullptr);
554 std::vector<VkQueueFamilyProperties> qfp(qfp_count);
555 d.vkGetPhysicalDeviceQueueFamilyProperties(pdevice, &qfp_count,
556 qfp.data());
557 std::optional<uint32_t> graphics_queue_family;
558 for (uint32_t i = 0; i < qfp.size(); i++) {
559 // Only pick graphics queues that can also present to the surface.
560 // Graphics queues that can't present are rare if not nonexistent, but
561 // the spec allows for this, so check it anyways.
562 VkBool32 surface_present_supported;
563 d.vkGetPhysicalDeviceSurfaceSupportKHR(pdevice, i, g_state.surface,
564 &surface_present_supported);
565
566 if (!graphics_queue_family.has_value() &&
567 qfp[i].queueFlags & VK_QUEUE_GRAPHICS_BIT &&
568 surface_present_supported) {
569 graphics_queue_family = i;
570 }
571 }
572
573 // Skip physical devices that don't have a graphics queue.
574 if (!graphics_queue_family.has_value()) {
575 std::cout << " - Skipping due to no suitable graphics queues."
576 << std::endl;
577 continue;
578 }
579
580 // Prefer discrete GPUs.
582 score += 1 << 30;
583 }
584
585 uint32_t extension_count;
586 d.vkEnumerateDeviceExtensionProperties(pdevice, nullptr, &extension_count,
587 nullptr);
588 std::vector<VkExtensionProperties> available_extensions(extension_count);
589 d.vkEnumerateDeviceExtensionProperties(pdevice, nullptr, &extension_count,
590 available_extensions.data());
591
592 bool supports_swapchain = false;
593 for (const auto& available_extension : available_extensions) {
595 available_extension.extensionName) == 0) {
596 supports_swapchain = true;
597 supported_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
598 }
599 // The spec requires VK_KHR_portability_subset be enabled whenever it's
600 // available on a device. It's present on compatibility ICDs like
601 // MoltenVK.
602 else if (strcmp("VK_KHR_portability_subset",
603 available_extension.extensionName) == 0) {
604 supported_extensions.push_back("VK_KHR_portability_subset");
605 }
606
607 // Prefer GPUs that support VK_KHR_get_memory_requirements2.
609 available_extension.extensionName) == 0) {
610 score += 1 << 29;
611 supported_extensions.push_back(
613 }
614 }
615
616 // Skip physical devices that don't have swapchain support.
617 if (!supports_swapchain) {
618 std::cout << " - Skipping due to lack of swapchain support."
619 << std::endl;
620 continue;
621 }
622
623 // Prefer GPUs with larger max texture sizes.
624 score += properties.limits.maxImageDimension2D;
625
626 if (selected_score < score) {
627 std::cout << " - This is the best device so far. Score: 0x" << std::hex
628 << score << std::dec << std::endl;
629
630 selected_score = score;
631 g_state.physical_device = pdevice;
632 g_state.enabled_device_extensions = supported_extensions;
633 g_state.queue_family_index = graphics_queue_family.value_or(
634 std::numeric_limits<uint32_t>::max());
635 }
636 }
637
638 if (g_state.physical_device == nullptr) {
639 std::cerr << "Failed to find a compatible Vulkan physical device."
640 << std::endl;
641 return EXIT_FAILURE;
642 }
643 }
644
645 /// --------------------------------------------------------------------------
646 /// Create a logical device and a graphics queue handle.
647 /// --------------------------------------------------------------------------
648
649 std::cout << "Enabling " << g_state.enabled_device_extensions.size()
650 << " device extensions:" << std::endl;
651 for (const char* extension : g_state.enabled_device_extensions) {
652 std::cout << " - " << extension << std::endl;
653 }
654
655 {
656 VkPhysicalDeviceFeatures device_features = {};
657
658 VkDeviceQueueCreateInfo graphics_queue = {};
660 graphics_queue.queueFamilyIndex = g_state.queue_family_index;
661 graphics_queue.queueCount = 1;
662 float priority = 1.0f;
663 graphics_queue.pQueuePriorities = &priority;
664
665 VkDeviceCreateInfo device_info = {};
667 device_info.enabledExtensionCount =
668 g_state.enabled_device_extensions.size();
669 device_info.ppEnabledExtensionNames =
670 g_state.enabled_device_extensions.data();
671 device_info.pEnabledFeatures = &device_features;
672 device_info.queueCreateInfoCount = 1;
673 device_info.pQueueCreateInfos = &graphics_queue;
674
675 if (d.vkCreateDevice(g_state.physical_device, &device_info, nullptr,
676 &g_state.device) != VK_SUCCESS) {
677 std::cerr << "Failed to create Vulkan logical device." << std::endl;
678 return EXIT_FAILURE;
679 }
680 }
681
682 d.vkGetDeviceQueue(g_state.device, g_state.queue_family_index, 0,
683 &g_state.queue);
684
685 /// --------------------------------------------------------------------------
686 /// Create sync primitives and command pool to use in the render loop
687 /// callbacks.
688 /// --------------------------------------------------------------------------
689
690 {
692 d.vkCreateFence(g_state.device, &f_info, nullptr,
693 &g_state.image_ready_fence);
694
695 VkSemaphoreCreateInfo s_info = {
697 d.vkCreateSemaphore(g_state.device, &s_info, nullptr,
698 &g_state.present_transition_semaphore);
699
700 VkCommandPoolCreateInfo pool_info = {
702 .queueFamilyIndex = g_state.queue_family_index,
703 };
704 d.vkCreateCommandPool(g_state.device, &pool_info, nullptr,
705 &g_state.swapchain_command_pool);
706 }
707
708 /// --------------------------------------------------------------------------
709 /// Create swapchain.
710 /// --------------------------------------------------------------------------
711
712 if (!InitializeSwapchain()) {
713 std::cerr << "Failed to create swapchain." << std::endl;
714 return EXIT_FAILURE;
715 }
716
717 /// --------------------------------------------------------------------------
718 /// Start Flutter Engine.
719 /// --------------------------------------------------------------------------
720
721 {
722 FlutterRendererConfig config = {};
723 config.type = kVulkan;
724 config.vulkan.struct_size = sizeof(config.vulkan);
725 config.vulkan.version = VK_MAKE_VERSION(1, 1, 0);
726 config.vulkan.instance = g_state.instance;
727 config.vulkan.physical_device = g_state.physical_device;
728 config.vulkan.device = g_state.device;
729 config.vulkan.queue_family_index = g_state.queue_family_index;
730 config.vulkan.queue = g_state.queue;
732 g_state.enabled_instance_extensions.size();
734 g_state.enabled_instance_extensions.data();
736 g_state.enabled_device_extensions.size();
738 g_state.enabled_device_extensions.data();
743
744 // This directory is generated by `flutter build bundle`.
745 std::string assets_path = project_path + "/build/flutter_assets";
747 .struct_size = sizeof(FlutterProjectArgs),
748 .assets_path = assets_path.c_str(),
749 .icu_data_path =
750 icudtl_path.c_str(), // Find this in your bin/cache directory.
751 };
754 &g_state.engine);
755 if (result != kSuccess || g_state.engine == nullptr) {
756 std::cerr << "Failed to start Flutter Engine." << std::endl;
757 return EXIT_FAILURE;
758 }
759
760 // Trigger a FlutterEngineSendWindowMetricsEvent to communicate the initial
761 // size of the window.
762 int width, height;
763 glfwGetFramebufferSize(g_state.window, &width, &height);
765 g_state.resize_pending = false;
766 }
767
768 /// --------------------------------------------------------------------------
769 /// GLFW render loop.
770 /// --------------------------------------------------------------------------
771
772 glfwSetKeyCallback(g_state.window, GLFWKeyCallback);
773 glfwSetFramebufferSizeCallback(g_state.window, GLFWframebufferSizeCallback);
774 glfwSetMouseButtonCallback(g_state.window, GLFWmouseButtonCallback);
775
776 while (!glfwWindowShouldClose(g_state.window)) {
777 glfwWaitEvents();
778 }
779
780 /// --------------------------------------------------------------------------
781 /// Cleanup.
782 /// --------------------------------------------------------------------------
783
784 if (FlutterEngineShutdown(g_state.engine) != kSuccess) {
785 std::cerr << "Flutter Engine shutdown failed." << std::endl;
786 }
787
788 d.vkDestroyCommandPool(g_state.device, g_state.swapchain_command_pool,
789 nullptr);
790 d.vkDestroySemaphore(g_state.device, g_state.present_transition_semaphore,
791 nullptr);
792 d.vkDestroyFence(g_state.device, g_state.image_ready_fence, nullptr);
793
794 d.vkDestroyDevice(g_state.device, nullptr);
795 d.vkDestroySurfaceKHR(g_state.instance, g_state.surface, nullptr);
796 d.vkDestroyInstance(g_state.instance, nullptr);
797
798 glfwDestroyWindow(g_state.window);
799 glfwTerminate();
800
801 return 0;
802}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:233
int count
Definition: FontMgrTest.cpp:49
SemanticsAction action
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:1631
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition: embedder.cc:2130
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:2119
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition: embedder.cc:2270
@ kVulkan
Definition: embedder.h:86
FlutterPointerPhase
The phase of the pointer event.
Definition: embedder.h:859
@ kUp
Definition: embedder.h:867
@ kDown
Definition: embedder.h:874
@ kMove
Definition: embedder.h:879
FlutterEngineResult
Definition: embedder.h:72
@ kSuccess
Definition: embedder.h:73
void * FlutterVulkanInstanceHandle
Alias for VkInstance.
Definition: embedder.h:700
#define FLUTTER_ENGINE_VERSION
Definition: embedder.h:70
bool InitializeSwapchain()
Definition: main.cc:142
VkSwapchainKHR swapchain
Definition: main.cc:63
std::vector< VkCommandBuffer > present_transition_buffers
Definition: main.cc:57
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
bool resize_pending
Definition: main.cc:69
uint32_t last_image_index
Definition: main.cc:65
FlutterVulkanImage FlutterGetNextImageCallback(void *user_data, const FlutterFrameInfo *frame_info)
Definition: main.cc:327
bool FlutterPresentCallback(void *user_data, const FlutterVulkanImage *image)
Definition: main.cc:355
void PrintUsage()
Definition: main.cc:136
void GLFWframebufferSizeCallback(GLFWwindow *window, int width, int height)
Definition: main.cc:125
VkPhysicalDevice physical_device
Definition: main.cc:50
std::vector< const char * > enabled_device_extensions
Definition: main.cc:51
GLFWwindow * window
Definition: main.cc:44
static const bool g_enable_validation_layers
Definition: main.cc:26
VkDevice device
Definition: main.cc:52
static double g_pixelRatio
Definition: main.cc:28
VkCommandPool swapchain_command_pool
Definition: main.cc:56
static const VkPresentModeKHR kPreferredPresentMode
Definition: main.cc:34
void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, FlutterPointerPhase phase, double x, double y)
Definition: main.cc:76
VkInstance instance
Definition: main.cc:47
VkQueue queue
Definition: main.cc:54
std::vector< const char * > enabled_instance_extensions
Definition: main.cc:46
VkSurfaceFormatKHR surface_format
Definition: main.cc:62
static const size_t kInitialWindowHeight
Definition: main.cc:30
VkSemaphore present_transition_semaphore
Definition: main.cc:60
void * FlutterGetInstanceProcAddressCallback(void *user_data, FlutterVulkanInstanceHandle instance, const char *procname)
Definition: main.cc:391
static const size_t kInitialWindowWidth
Definition: main.cc:29
void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
Definition: main.cc:115
VkFence image_ready_fence
Definition: main.cc:59
FlutterEngine engine
Definition: main.cc:67
struct @13 g_state
Global struct for holding the Window+Vulkan state.
void GLFWcursorPositionCallback(GLFWwindow *window, double x, double y)
Definition: main.cc:92
uint32_t queue_family_index
Definition: main.cc:53
void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, int mods)
Definition: main.cc:96
std::vector< VkImage > swapchain_images
Definition: main.cc:64
VkSurfaceKHR surface
Definition: main.cc:48
void GLFW_ErrorCallback(int error, const char *description)
Definition: main.cc:72
sk_sp< SkImage > image
Definition: examples.cpp:13
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
Definition: flutter_glfw.cc:32
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:930
FlutterVulkanRendererConfig vulkan
Definition: embedder.h:824
FlutterRendererType type
Definition: embedder.h:819
FlutterVulkanQueueHandle queue
Definition: embedder.h:765
FlutterVulkanDeviceHandle device
Definition: embedder.h:756
FlutterVulkanInstanceProcAddressCallback get_instance_proc_address_callback
Definition: embedder.h:794
size_t struct_size
The size of this struct. Must be sizeof(FlutterVulkanRendererConfig).
Definition: embedder.h:744
size_t enabled_instance_extension_count
Definition: embedder.h:768
uint32_t queue_family_index
The queue family index of the VkQueue supplied in the next field.
Definition: embedder.h:758
FlutterVulkanImageCallback get_next_image_callback
Definition: embedder.h:798
const char ** enabled_instance_extensions
Definition: embedder.h:775
const char ** enabled_device_extensions
Definition: embedder.h:786
size_t enabled_device_extension_count
Definition: embedder.h:778
FlutterVulkanInstanceHandle instance
Definition: embedder.h:751
FlutterVulkanPresentCallback present_image_callback
Definition: embedder.h:804
FlutterVulkanPhysicalDeviceHandle physical_device
VkPhysicalDevice handle.
Definition: embedder.h:753
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition: embedder.h:835
VkStructureType sType
Definition: vulkan_core.h:2547
VkStructureType sType
Definition: vulkan_core.h:3435
VkStructureType sType
Definition: vulkan_core.h:3409
uint32_t enabledExtensionCount
Definition: vulkan_core.h:2809
const VkDeviceQueueCreateInfo * pQueueCreateInfos
Definition: vulkan_core.h:2806
const VkPhysicalDeviceFeatures * pEnabledFeatures
Definition: vulkan_core.h:2811
const char *const * ppEnabledExtensionNames
Definition: vulkan_core.h:2810
uint32_t queueCreateInfoCount
Definition: vulkan_core.h:2805
VkStructureType sType
Definition: vulkan_core.h:2802
const float * pQueuePriorities
Definition: vulkan_core.h:2798
VkStructureType sType
Definition: vulkan_core.h:2793
uint32_t width
Definition: vulkan_core.h:2402
uint32_t height
Definition: vulkan_core.h:2403
VkStructureType sType
Definition: vulkan_core.h:2930
VkStructureType sType
Definition: vulkan_core.h:2480
VkPhysicalDeviceType deviceType
Definition: vulkan_core.h:2778
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]
Definition: vulkan_core.h:2779
VkPhysicalDeviceLimits limits
Definition: vulkan_core.h:2781
VkStructureType sType
Definition: vulkan_core.h:6233
VkStructureType sType
Definition: vulkan_core.h:2936
VkStructureType sType
Definition: vulkan_core.h:2827
VkSurfaceTransformFlagBitsKHR currentTransform
Definition: vulkan_core.h:6143
void * user_data
VkFlags VkPipelineStageFlags
Definition: vulkan_core.h:2080
@ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
Definition: vulkan_core.h:1043
@ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
Definition: vulkan_core.h:1030
@ VK_COMMAND_BUFFER_LEVEL_PRIMARY
Definition: vulkan_core.h:1797
@ VK_SHARING_MODE_EXCLUSIVE
Definition: vulkan_core.h:1490
@ VK_IMAGE_ASPECT_COLOR_BIT
Definition: vulkan_core.h:1855
@ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
Definition: vulkan_core.h:1971
@ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
Definition: vulkan_core.h:6127
#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME
Definition: vulkan_core.h:8699
@ VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT
Definition: vulkan_core.h:2368
#define VK_MAKE_VERSION(major, minor, patch)
Definition: vulkan_core.h:62
#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME
Definition: vulkan_core.h:7480
@ VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
Definition: vulkan_core.h:1464
@ VK_QUEUE_GRAPHICS_BIT
Definition: vulkan_core.h:2025
VkResult
Definition: vulkan_core.h:139
@ VK_SUBOPTIMAL_KHR
Definition: vulkan_core.h:165
@ VK_SUCCESS
Definition: vulkan_core.h:140
@ VK_ERROR_OUT_OF_DATE_KHR
Definition: vulkan_core.h:166
@ VK_ACCESS_MEMORY_READ_BIT
Definition: vulkan_core.h:1833
VkPresentModeKHR
Definition: vulkan_core.h:6081
@ VK_PRESENT_MODE_FIFO_KHR
Definition: vulkan_core.h:6084
@ VK_FORMAT_B8G8R8A8_UNORM
Definition: vulkan_core.h:1207
@ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
Definition: vulkan_core.h:6092
uint32_t VkBool32
Definition: vulkan_core.h:93
#define VK_KHR_SWAPCHAIN_EXTENSION_NAME
Definition: vulkan_core.h:6193
@ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
Definition: vulkan_core.h:2058
@ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
Definition: vulkan_core.h:2061
#define VK_QUEUE_FAMILY_IGNORED
Definition: vulkan_core.h:127
@ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
Definition: vulkan_core.h:197
@ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO
Definition: vulkan_core.h:190
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
Definition: vulkan_core.h:228
@ VK_STRUCTURE_TYPE_APPLICATION_INFO
Definition: vulkan_core.h:188
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
Definition: vulkan_core.h:230
@ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
Definition: vulkan_core.h:189
@ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
Definition: vulkan_core.h:352
@ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
Definition: vulkan_core.h:196
@ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
Definition: vulkan_core.h:191
@ VK_STRUCTURE_TYPE_SUBMIT_INFO
Definition: vulkan_core.h:192
@ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO
Definition: vulkan_core.h:227
@ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR
Definition: vulkan_core.h:353
@ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER
Definition: vulkan_core.h:233