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#define S1(x) #x
19#define S2(x) S1(x)
20#define LOCATION __FILE__ " : " S2(__LINE__)
21
22#define CHECK_VK_RESULT(x) \
23 do { \
24 vk::resultCheck(static_cast<vk::Result>(x), LOCATION); \
25 } while (0)
26
27// Convenient reference to vulkan.hpp's global proc table.
28auto& d = VULKAN_HPP_DEFAULT_DISPATCHER;
29
30// GLFW needs to be included after Vulkan.
31#include "GLFW/glfw3.h"
32
33#include "embedder.h" // Flutter's Embedder ABI.
34
35struct {
37 bool utils_supported = false;
38 bool report_supported = false;
39 VkDebugReportCallbackEXT report_callback = VK_NULL_HANDLE;
40 VkDebugUtilsMessengerEXT utils_messenger_callback = VK_NULL_HANDLE;
42// This value is calculated after the window is created.
43static double g_pixelRatio = 1.0;
44static const size_t kInitialWindowWidth = 800;
45static const size_t kInitialWindowHeight = 600;
46// Use `VK_PRESENT_MODE_FIFO_KHR` for full vsync (one swap per screen refresh),
47// `VK_PRESENT_MODE_MAILBOX_KHR` for continual swap without horizontal tearing,
48// or `VK_PRESENT_MODE_IMMEDIATE_KHR` for no vsync.
49static const VkPresentModeKHR kPreferredPresentMode = VK_PRESENT_MODE_FIFO_KHR;
50static constexpr FlutterViewId kImplicitViewId = 0;
51
52static_assert(FLUTTER_ENGINE_VERSION == 1,
53 "This Flutter Embedder was authored against the stable Flutter "
54 "API at version 1. There has been a serious breakage in the "
55 "API. Please read the ChangeLog and take appropriate action "
56 "before updating this assertion");
57
58/// Global struct for holding the Window+Vulkan state.
59struct {
60 GLFWwindow* window;
61
62 std::vector<const char*> enabled_instance_extensions;
63 std::vector<const char*> enabled_layer_names;
64 VkInstance instance;
65 VkSurfaceKHR surface;
66
67 VkPhysicalDevice physical_device;
68 std::vector<const char*> enabled_device_extensions;
69 VkDevice device;
71 VkQueue queue;
72
74 std::vector<VkCommandBuffer> present_transition_buffers;
75
78
79 VkSurfaceFormatKHR surface_format;
80 VkSwapchainKHR swapchain;
81 std::vector<VkImage> swapchain_images;
83
85
86 bool resize_pending = false;
88
89void GLFW_ErrorCallback(int error, const char* description) {
90 std::cerr << "GLFW Error: (" << error << ") " << description << std::endl;
91}
92
95 double x,
96 double y) {
97 FlutterPointerEvent event = {};
98 event.struct_size = sizeof(event);
99 event.phase = phase;
100 event.x = x * g_pixelRatio;
101 event.y = y * g_pixelRatio;
102 event.timestamp =
103 std::chrono::duration_cast<std::chrono::microseconds>(
104 std::chrono::high_resolution_clock::now().time_since_epoch())
105 .count();
106 // This example only supports a single window, therefore we assume the event
107 // occurred in the only view, the implicit view.
108 event.view_id = kImplicitViewId;
109 FlutterEngineSendPointerEvent(g_state.engine, &event, 1);
110}
111
115
117 int key,
118 int action,
119 int mods) {
120 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) {
121 double x, y;
122 glfwGetCursorPos(window, &x, &y);
124 glfwSetCursorPosCallback(window, GLFWcursorPositionCallback);
125 }
126
127 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_RELEASE) {
128 double x, y;
129 glfwGetCursorPos(window, &x, &y);
131 glfwSetCursorPosCallback(window, nullptr);
132 }
133}
134
135void GLFWKeyCallback(GLFWwindow* window,
136 int key,
137 int scancode,
138 int action,
139 int mods) {
140 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
141 glfwSetWindowShouldClose(window, GLFW_TRUE);
142 }
143}
144
145void GLFWframebufferSizeCallback(GLFWwindow* window, int width, int height) {
146 g_state.resize_pending = true;
147
148 FlutterWindowMetricsEvent event = {};
149 event.struct_size = sizeof(event);
150 event.width = width;
151 event.height = height;
152 event.pixel_ratio = g_pixelRatio;
153 // This example only supports a single window, therefore we assume the event
154 // occurred in the only view, the implicit view.
155 event.view_id = kImplicitViewId;
157}
158
160 std::cerr
161 << "usage: embedder_example_vulkan <path to project> <path to icudtl.dat>"
162 << std::endl;
163}
164
166 if (g_state.resize_pending) {
167 g_state.resize_pending = false;
168 d.vkDestroySwapchainKHR(g_state.device, g_state.swapchain, nullptr);
169
170 d.vkQueueWaitIdle(g_state.queue);
171 d.vkResetCommandPool(g_state.device, g_state.swapchain_command_pool,
172 VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT);
173 }
174
175 /// --------------------------------------------------------------------------
176 /// Choose an image format that can be presented to the surface, preferring
177 /// the common BGRA+sRGB if available.
178 /// --------------------------------------------------------------------------
179
180 uint32_t format_count;
181 d.vkGetPhysicalDeviceSurfaceFormatsKHR(
182 g_state.physical_device, g_state.surface, &format_count, nullptr);
183 std::vector<VkSurfaceFormatKHR> formats(format_count);
184 d.vkGetPhysicalDeviceSurfaceFormatsKHR(
185 g_state.physical_device, g_state.surface, &format_count, formats.data());
186 assert(!formats.empty()); // Shouldn't be possible.
187
188 g_state.surface_format = formats[0];
189 for (const auto& format : formats) {
190 if (format.format == VK_FORMAT_B8G8R8A8_UNORM &&
191 format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
192 g_state.surface_format = format;
193 }
194 }
195
196 /// --------------------------------------------------------------------------
197 /// Choose the presentable image size that's as close as possible to the
198 /// window size.
199 /// --------------------------------------------------------------------------
200
201 VkExtent2D extent;
202
203 VkSurfaceCapabilitiesKHR surface_capabilities;
204 d.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
205 g_state.physical_device, g_state.surface, &surface_capabilities);
206
207 if (surface_capabilities.currentExtent.width != UINT32_MAX) {
208 // If the surface reports a specific extent, we must use it.
209 extent = surface_capabilities.currentExtent;
210 } else {
211 // `glfwGetWindowSize` returns the window size in screen coordinates, so we
212 // instead use `glfwGetFramebufferSize` to get the size in pixels in order
213 // to properly support high DPI displays.
214 int width, height;
215 glfwGetFramebufferSize(g_state.window, &width, &height);
216
217 VkExtent2D actual_extent = {
218 .width = static_cast<uint32_t>(width),
219 .height = static_cast<uint32_t>(height),
220 };
221 actual_extent.width =
222 std::max(surface_capabilities.minImageExtent.width,
223 std::min(surface_capabilities.maxImageExtent.width,
224 actual_extent.width));
225 actual_extent.height =
226 std::max(surface_capabilities.minImageExtent.height,
227 std::min(surface_capabilities.maxImageExtent.height,
228 actual_extent.height));
229 }
230
231 /// --------------------------------------------------------------------------
232 /// Choose the present mode.
233 /// --------------------------------------------------------------------------
234
235 uint32_t mode_count;
236 d.vkGetPhysicalDeviceSurfacePresentModesKHR(
237 g_state.physical_device, g_state.surface, &mode_count, nullptr);
238 std::vector<VkPresentModeKHR> modes(mode_count);
239 d.vkGetPhysicalDeviceSurfacePresentModesKHR(
240 g_state.physical_device, g_state.surface, &mode_count, modes.data());
241 assert(!formats.empty()); // Shouldn't be possible.
242
243 // If the preferred mode isn't available, just choose the first one.
244 VkPresentModeKHR present_mode = modes[0];
245 for (const auto& mode : modes) {
246 if (mode == kPreferredPresentMode) {
247 present_mode = mode;
248 break;
249 }
250 }
251
252 /// --------------------------------------------------------------------------
253 /// Create the swapchain.
254 /// --------------------------------------------------------------------------
255
256 VkSwapchainCreateInfoKHR info = {
257 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
258 .surface = g_state.surface,
259 .minImageCount = surface_capabilities.minImageCount + 1,
260 .imageFormat = g_state.surface_format.format,
261 .imageColorSpace = g_state.surface_format.colorSpace,
262 .imageExtent = extent,
263 .imageArrayLayers = 1,
264 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
265 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
266 .queueFamilyIndexCount = 0,
267 .pQueueFamilyIndices = nullptr,
268 .preTransform = surface_capabilities.currentTransform,
269 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
270 .presentMode = present_mode,
271 .clipped = true,
272 };
273 if (d.vkCreateSwapchainKHR(g_state.device, &info, nullptr,
274 &g_state.swapchain) != VK_SUCCESS) {
275 return false;
276 }
277
278 /// --------------------------------------------------------------------------
279 /// Fetch swapchain images.
280 /// --------------------------------------------------------------------------
281
282 uint32_t image_count;
283 d.vkGetSwapchainImagesKHR(g_state.device, g_state.swapchain, &image_count,
284 nullptr);
285 g_state.swapchain_images.resize(image_count);
286 d.vkGetSwapchainImagesKHR(g_state.device, g_state.swapchain, &image_count,
287 g_state.swapchain_images.data());
288
289 /// --------------------------------------------------------------------------
290 /// Record a command buffer for each of the images to be executed prior to
291 /// presenting.
292 /// --------------------------------------------------------------------------
293
294 g_state.present_transition_buffers.resize(g_state.swapchain_images.size());
295
296 VkCommandBufferAllocateInfo buffers_info = {
297 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
298 .commandPool = g_state.swapchain_command_pool,
299 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
300 .commandBufferCount =
301 static_cast<uint32_t>(g_state.present_transition_buffers.size()),
302 };
303 d.vkAllocateCommandBuffers(g_state.device, &buffers_info,
304 g_state.present_transition_buffers.data());
305
306 for (size_t i = 0; i < g_state.swapchain_images.size(); i++) {
307 auto image = g_state.swapchain_images[i];
308 auto buffer = g_state.present_transition_buffers[i];
309
310 VkCommandBufferBeginInfo begin_info = {
311 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
312 d.vkBeginCommandBuffer(buffer, &begin_info);
313
314 // Flutter Engine hands back the image after writing to it
315 VkImageMemoryBarrier barrier = {
316 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
317 .srcAccessMask = 0,
318 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
319 .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
320 .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
321 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
322 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
323 .image = image,
324 .subresourceRange = {
325 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
326 .baseMipLevel = 0,
327 .levelCount = 1,
328 .baseArrayLayer = 0,
329 .layerCount = 1,
330 }};
331 d.vkCmdPipelineBarrier(
332 buffer, // commandBuffer
333 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
334 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
335 0, // dependencyFlags
336 0, // memoryBarrierCount
337 nullptr, // pMemoryBarriers
338 0, // bufferMemoryBarrierCount
339 nullptr, // pBufferMemoryBarriers
340 1, // imageMemoryBarrierCount
341 &barrier // pImageMemoryBarriers
342 );
343
344 d.vkEndCommandBuffer(buffer);
345 }
346
347 return true; // \o/
348}
349
351 void* user_data,
352 const FlutterFrameInfo* frame_info) {
353 // If the GLFW framebuffer has been resized, discard the swapchain and create
354 // a new one.
355 if (g_state.resize_pending) {
357 }
358
359 d.vkAcquireNextImageKHR(g_state.device, g_state.swapchain, UINT64_MAX,
360 nullptr, g_state.image_ready_fence,
361 &g_state.last_image_index);
362
363 // Flutter Engine expects the image to be available for transitioning and
364 // attaching immediately, and so we need to force a host sync here before
365 // returning.
366 d.vkWaitForFences(g_state.device, 1, &g_state.image_ready_fence, true,
367 UINT64_MAX);
368 d.vkResetFences(g_state.device, 1, &g_state.image_ready_fence);
369
370 return {
371 .struct_size = sizeof(FlutterVulkanImage),
372 .image = reinterpret_cast<uint64_t>(
373 g_state.swapchain_images[g_state.last_image_index]),
374 .format = g_state.surface_format.format,
375 };
376}
377
379 VkPipelineStageFlags stage_flags =
380 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
381 VkSubmitInfo submit_info = {
382 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
383 .waitSemaphoreCount = 0,
384 .pWaitSemaphores = nullptr,
385 .pWaitDstStageMask = &stage_flags,
386 .commandBufferCount = 1,
387 .pCommandBuffers =
388 &g_state.present_transition_buffers[g_state.last_image_index],
389 .signalSemaphoreCount = 1,
390 .pSignalSemaphores = &g_state.present_transition_semaphore,
391 };
392 d.vkQueueSubmit(g_state.queue, 1, &submit_info, nullptr);
393
394 VkPresentInfoKHR present_info = {
395 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
396 .waitSemaphoreCount = 1,
397 .pWaitSemaphores = &g_state.present_transition_semaphore,
398 .swapchainCount = 1,
399 .pSwapchains = &g_state.swapchain,
400 .pImageIndices = &g_state.last_image_index,
401 };
402 VkResult result = d.vkQueuePresentKHR(g_state.queue, &present_info);
403
404 // If the swapchain is no longer compatible with the surface, discard the
405 // swapchain and create a new one.
406 if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) {
408 }
409 d.vkQueueWaitIdle(g_state.queue);
410
411 return result == VK_SUCCESS;
412}
413
415 void* user_data,
417 const char* procname) {
418 auto* proc = glfwGetInstanceProcAddress(
419 reinterpret_cast<VkInstance>(instance), procname);
420 return reinterpret_cast<void*>(proc);
421}
422
423VkBool32 DebugReportCallback(VkDebugReportFlagsEXT flags,
424 VkDebugReportObjectTypeEXT /* objectType */,
425 uint64_t /* object */,
426 size_t /* location */,
427 int32_t /* messageCode */,
428 const char* pLayerPrefix,
429 const char* pMessage,
430 void* /* pUserData */) {
431 if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
432 std::cout << "VULKAN: (" << pLayerPrefix << ") " << pMessage << std::endl;
433 } else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
434 std::cout << "VULKAN DEBUG: (" << pLayerPrefix << ") " << pMessage
435 << std::endl;
436 } else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
437 std::cout << "VULKAN PERF WARNING: (" << pLayerPrefix << ") " << pMessage
438 << std::endl;
439 } else if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
440 std::cerr << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage
441 << std::endl;
442 } else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
443 std::cout << "VULKAN DEBUG: (" << pLayerPrefix << ") " << pMessage
444 << std::endl;
445 }
446 return VK_FALSE;
447}
448
449VkBool32 DebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
450 VkDebugUtilsMessageTypeFlagsEXT /* types */,
451 const VkDebugUtilsMessengerCallbackDataEXT* cb_data,
452 void* /* pUserData */) {
453 if (strstr(cb_data->pMessage, "ALL_GRAPHICS_BIT") ||
454 strstr(cb_data->pMessage, "ALL_COMMANDS_BIT")) {
455 return VK_FALSE;
456 }
457 if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
458 std::cout << "VULKAN: (" << cb_data->pMessageIdName << ") "
459 << cb_data->pMessage << std::endl;
460 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
461 std::cout << "VULKAN INFO: (" << cb_data->pMessageIdName << ") "
462 << cb_data->pMessage << std::endl;
463 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
464 std::cout << "VULKAN WARN: (" << cb_data->pMessageIdName << ") "
465 << cb_data->pMessage << std::endl;
466 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
467 std::cerr << "VULKAN ERROR: (" << cb_data->pMessageIdName << ") "
468 << cb_data->pMessage << std::endl;
469 }
470 return VK_TRUE;
471}
472
473int main(int argc, char** argv) {
474 if (argc != 3) {
475 PrintUsage();
476 return 1;
477 }
478
479 std::string project_path = argv[1];
480 std::string icudtl_path = argv[2];
481
482 /// --------------------------------------------------------------------------
483 /// Create a GLFW window.
484 /// --------------------------------------------------------------------------
485
486 {
487 if (!glfwInit()) {
488 std::cerr << "Failed to initialize GLFW." << std::endl;
489 return EXIT_FAILURE;
490 }
491
492 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
493 g_state.window = glfwCreateWindow(kInitialWindowWidth, kInitialWindowHeight,
494 "Flutter", nullptr, nullptr);
495 if (!g_state.window) {
496 std::cerr << "Failed to create GLFW window." << std::endl;
497 return EXIT_FAILURE;
498 }
499
500 int framebuffer_width, framebuffer_height;
501 glfwGetFramebufferSize(g_state.window, &framebuffer_width,
502 &framebuffer_height);
503 g_pixelRatio = framebuffer_width / kInitialWindowWidth;
504
505 glfwSetErrorCallback(GLFW_ErrorCallback);
506 }
507
508 /// --------------------------------------------------------------------------
509 /// Dynamically load the Vulkan loader with GLFW and use it to populate GLAD's
510 /// proc table.
511 /// --------------------------------------------------------------------------
512
513 if (!glfwVulkanSupported()) {
514 std::cerr << "GLFW was unable to resolve either a Vulkan loader or a "
515 "compatible physical device!"
516 << std::endl;
517#if defined(__APPLE__)
518 std::cerr
519 << "NOTE: Apple platforms don't ship with a Vulkan loader or any "
520 "Vulkan drivers. Follow this guide to set up a Vulkan loader on "
521 "macOS and use the MoltenVK ICD: "
522 "https://vulkan.lunarg.com/doc/sdk/latest/mac/getting_started.html"
523 << std::endl;
524#endif
525 return EXIT_FAILURE;
526 }
527
528 VULKAN_HPP_DEFAULT_DISPATCHER.init(glfwGetInstanceProcAddress);
529
530 /// --------------------------------------------------------------------------
531 /// Create a Vulkan instance.
532 /// --------------------------------------------------------------------------
533
534 {
535 uint32_t extension_count;
536 const char** glfw_extensions =
537 glfwGetRequiredInstanceExtensions(&extension_count);
538 g_state.enabled_instance_extensions.resize(extension_count);
539 memcpy(g_state.enabled_instance_extensions.data(), glfw_extensions,
540 extension_count * sizeof(char*));
541
542 if (g_debug.enable_validation_layers) {
543 auto props = vk::enumerateInstanceExtensionProperties();
544 for (const auto& prop : props.value) {
545 if (strcmp(prop.extensionName,
546 VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) == 0) {
547 g_state.enabled_instance_extensions.push_back(
548 VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
549 }
550 if (strcmp(prop.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) ==
551 0) {
552 g_debug.utils_supported = true;
553 }
554 if (strcmp(prop.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME) ==
555 0) {
556 g_debug.report_supported = true;
557 }
558 }
559 if (g_debug.utils_supported) {
560 g_state.enabled_instance_extensions.push_back(
561 VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
562 g_debug.report_supported = false;
563 } else if (g_debug.report_supported) {
564 g_state.enabled_instance_extensions.push_back(
565 VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
566 }
567
568 auto available_layers = vk::enumerateInstanceLayerProperties();
569 for (const auto& l : available_layers.value) {
570 if (strcmp(l.layerName, "VK_LAYER_KHRONOS_validation") == 0) {
571 g_state.enabled_layer_names.push_back("VK_LAYER_KHRONOS_validation");
572 }
573 }
574 }
575
576 std::cout << "Enabling " << g_state.enabled_instance_extensions.size()
577 << " instance extensions:" << std::endl;
578 for (const auto& extension : g_state.enabled_instance_extensions) {
579 std::cout << " - " << extension << std::endl;
580 }
581 std::cout << "Enabling " << g_state.enabled_layer_names.size()
582 << " layers:" << std::endl;
583 for (const auto& layer : g_state.enabled_layer_names) {
584 std::cout << " - " << layer << std::endl;
585 }
586
587 VkApplicationInfo app_info = {
588 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
589 .pNext = nullptr,
590 .pApplicationName = "Flutter",
591 .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
592 .pEngineName = "No Engine",
593 .engineVersion = VK_MAKE_VERSION(1, 0, 0),
594 .apiVersion = VK_MAKE_VERSION(1, 1, 0),
595 };
596 VkInstanceCreateInfo info = {};
597 info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
598 info.flags = 0;
599 info.pApplicationInfo = &app_info;
600 info.enabledExtensionCount = g_state.enabled_instance_extensions.size();
601 info.ppEnabledExtensionNames = g_state.enabled_instance_extensions.data();
602 info.enabledLayerCount = g_state.enabled_layer_names.size();
603 info.ppEnabledLayerNames = g_state.enabled_layer_names.data();
604
605 if (d.vkCreateInstance(&info, nullptr, &g_state.instance) != VK_SUCCESS) {
606 std::cerr << "Failed to create Vulkan instance." << std::endl;
607 return EXIT_FAILURE;
608 }
609 }
610
611 // Load instance procs.
612 VULKAN_HPP_DEFAULT_DISPATCHER.init(vk::Instance(g_state.instance));
613
614 /// --------------------------------------------------------------------------
615 /// Create the window surface.
616 /// --------------------------------------------------------------------------
617
618 if (glfwCreateWindowSurface(g_state.instance, g_state.window, NULL,
619 &g_state.surface) != VK_SUCCESS) {
620 std::cerr << "Failed to create window surface." << std::endl;
621 return EXIT_FAILURE;
622 }
623
624 /// --------------------------------------------------------------------------
625 /// Select a compatible physical device.
626 /// --------------------------------------------------------------------------
627
628 {
629 uint32_t count;
630 d.vkEnumeratePhysicalDevices(g_state.instance, &count, nullptr);
631 std::vector<VkPhysicalDevice> physical_devices(count);
632 d.vkEnumeratePhysicalDevices(g_state.instance, &count,
633 physical_devices.data());
634
635 std::cout << "Enumerating " << count << " physical device(s)." << std::endl;
636
637 uint32_t selected_score = 0;
638 for (const auto& pdevice : physical_devices) {
639 VkPhysicalDeviceProperties properties;
640 VkPhysicalDeviceFeatures features;
641 d.vkGetPhysicalDeviceProperties(pdevice, &properties);
642 d.vkGetPhysicalDeviceFeatures(pdevice, &features);
643
644 std::cout << "Checking device: " << properties.deviceName << std::endl;
645
646 uint32_t score = 0;
647 std::vector<const char*> supported_extensions;
648
649 uint32_t qfp_count;
650 d.vkGetPhysicalDeviceQueueFamilyProperties(pdevice, &qfp_count, nullptr);
651 std::vector<VkQueueFamilyProperties> qfp(qfp_count);
652 d.vkGetPhysicalDeviceQueueFamilyProperties(pdevice, &qfp_count,
653 qfp.data());
654 std::optional<uint32_t> graphics_queue_family;
655 for (uint32_t i = 0; i < qfp.size(); i++) {
656 // Only pick graphics queues that can also present to the surface.
657 // Graphics queues that can't present are rare if not nonexistent, but
658 // the spec allows for this, so check it anyways.
659 VkBool32 surface_present_supported;
660 d.vkGetPhysicalDeviceSurfaceSupportKHR(pdevice, i, g_state.surface,
661 &surface_present_supported);
662
663 if (!graphics_queue_family.has_value() &&
664 qfp[i].queueFlags & VK_QUEUE_GRAPHICS_BIT &&
665 surface_present_supported) {
666 graphics_queue_family = i;
667 }
668 }
669
670 // Skip physical devices that don't have a graphics queue.
671 if (!graphics_queue_family.has_value()) {
672 std::cout << " - Skipping due to no suitable graphics queues."
673 << std::endl;
674 continue;
675 }
676
677 // Prefer discrete GPUs.
678 if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
679 score += 1 << 30;
680 }
681
682 uint32_t extension_count;
683 d.vkEnumerateDeviceExtensionProperties(pdevice, nullptr, &extension_count,
684 nullptr);
685 std::vector<VkExtensionProperties> available_extensions(extension_count);
686 d.vkEnumerateDeviceExtensionProperties(pdevice, nullptr, &extension_count,
687 available_extensions.data());
688
689 bool supports_swapchain = false;
690 for (const auto& available_extension : available_extensions) {
691 if (strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME,
692 available_extension.extensionName) == 0) {
693 supports_swapchain = true;
694 supported_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
695 }
696 // The spec requires VK_KHR_portability_subset be enabled whenever it's
697 // available on a device. It's present on compatibility ICDs like
698 // MoltenVK.
699 else if (strcmp("VK_KHR_portability_subset",
700 available_extension.extensionName) == 0) {
701 supported_extensions.push_back("VK_KHR_portability_subset");
702 }
703
704 // Prefer GPUs that support VK_KHR_get_memory_requirements2.
705 else if (strcmp(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
706 available_extension.extensionName) == 0) {
707 score += 1 << 29;
708 supported_extensions.push_back(
709 VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
710 }
711 }
712
713 // Skip physical devices that don't have swapchain support.
714 if (!supports_swapchain) {
715 std::cout << " - Skipping due to lack of swapchain support."
716 << std::endl;
717 continue;
718 }
719
720 // Prefer GPUs with larger max texture sizes.
721 score += properties.limits.maxImageDimension2D;
722
723 if (selected_score < score) {
724 std::cout << " - This is the best device so far. Score: 0x" << std::hex
725 << score << std::dec << std::endl;
726
727 selected_score = score;
728 g_state.physical_device = pdevice;
729 g_state.enabled_device_extensions = supported_extensions;
730 g_state.queue_family_index = graphics_queue_family.value_or(
731 std::numeric_limits<uint32_t>::max());
732 }
733 }
734
735 if (g_state.physical_device == nullptr) {
736 std::cerr << "Failed to find a compatible Vulkan physical device."
737 << std::endl;
738 return EXIT_FAILURE;
739 }
740 }
741
742 /// --------------------------------------------------------------------------
743 /// Create a logical device and a graphics queue handle.
744 /// --------------------------------------------------------------------------
745
746 std::cout << "Enabling " << g_state.enabled_device_extensions.size()
747 << " device extensions:" << std::endl;
748 for (const char* extension : g_state.enabled_device_extensions) {
749 std::cout << " - " << extension << std::endl;
750 }
751
752 {
753 VkPhysicalDeviceFeatures device_features = {};
754
755 VkDeviceQueueCreateInfo graphics_queue = {};
756 graphics_queue.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
757 graphics_queue.queueFamilyIndex = g_state.queue_family_index;
758 graphics_queue.queueCount = 1;
759 float priority = 1.0f;
760 graphics_queue.pQueuePriorities = &priority;
761
762 VkDeviceCreateInfo device_info = {};
763 device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
764 device_info.enabledExtensionCount =
765 g_state.enabled_device_extensions.size();
766 device_info.ppEnabledExtensionNames =
767 g_state.enabled_device_extensions.data();
768 device_info.pEnabledFeatures = &device_features;
769 device_info.queueCreateInfoCount = 1;
770 device_info.pQueueCreateInfos = &graphics_queue;
771
772 if (d.vkCreateDevice(g_state.physical_device, &device_info, nullptr,
773 &g_state.device) != VK_SUCCESS) {
774 std::cerr << "Failed to create Vulkan logical device." << std::endl;
775 return EXIT_FAILURE;
776 }
777 }
778
779 d.vkGetDeviceQueue(g_state.device, g_state.queue_family_index, 0,
780 &g_state.queue);
781
782 /// --------------------------------------------------------------------------
783 /// Create sync primitives and command pool to use in the render loop
784 /// callbacks.
785 /// --------------------------------------------------------------------------
786
787 {
788 VkFenceCreateInfo f_info = {.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
789 d.vkCreateFence(g_state.device, &f_info, nullptr,
790 &g_state.image_ready_fence);
791
792 VkSemaphoreCreateInfo s_info = {
793 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
794 d.vkCreateSemaphore(g_state.device, &s_info, nullptr,
795 &g_state.present_transition_semaphore);
796
797 VkCommandPoolCreateInfo pool_info = {
798 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
799 .queueFamilyIndex = g_state.queue_family_index,
800 };
801 d.vkCreateCommandPool(g_state.device, &pool_info, nullptr,
802 &g_state.swapchain_command_pool);
803 }
804
805 /// --------------------------------------------------------------------------
806 /// Create swapchain.
807 /// --------------------------------------------------------------------------
808
809 if (!InitializeSwapchain()) {
810 std::cerr << "Failed to create swapchain." << std::endl;
811 return EXIT_FAILURE;
812 }
813
814 /// --------------------------------------------------------------------------
815 /// Start Flutter Engine.
816 /// --------------------------------------------------------------------------
817
818 {
819 FlutterRendererConfig config = {};
820 config.type = kVulkan;
821 config.vulkan.struct_size = sizeof(config.vulkan);
822 config.vulkan.version = VK_MAKE_VERSION(1, 1, 0);
823 config.vulkan.instance = g_state.instance;
824 config.vulkan.physical_device = g_state.physical_device;
825 config.vulkan.device = g_state.device;
826 config.vulkan.queue_family_index = g_state.queue_family_index;
827 config.vulkan.queue = g_state.queue;
829 g_state.enabled_instance_extensions.size();
831 g_state.enabled_instance_extensions.data();
833 g_state.enabled_device_extensions.size();
835 g_state.enabled_device_extensions.data();
840
841 // This directory is generated by `flutter build bundle`.
842 std::string assets_path = project_path + "/build/flutter_assets";
845 .assets_path = assets_path.c_str(),
846 .icu_data_path =
847 icudtl_path.c_str(), // Find this in your bin/cache directory.
848 };
849 FlutterEngineResult result =
851 &g_state.engine);
852 if (result != kSuccess || g_state.engine == nullptr) {
853 std::cerr << "Failed to start Flutter Engine." << std::endl;
854 return EXIT_FAILURE;
855 }
856
857 // Trigger a FlutterEngineSendWindowMetricsEvent to communicate the initial
858 // size of the window.
859 int width, height;
860 glfwGetFramebufferSize(g_state.window, &width, &height);
862 g_state.resize_pending = false;
863 }
864
865 /// --------------------------------------------------------------------------
866 /// GLFW render loop.
867 /// --------------------------------------------------------------------------
868
869 glfwSetKeyCallback(g_state.window, GLFWKeyCallback);
870 glfwSetFramebufferSizeCallback(g_state.window, GLFWframebufferSizeCallback);
871 glfwSetMouseButtonCallback(g_state.window, GLFWmouseButtonCallback);
872
873 while (!glfwWindowShouldClose(g_state.window)) {
874 glfwWaitEvents();
875 }
876
877 /// --------------------------------------------------------------------------
878 /// Cleanup.
879 /// --------------------------------------------------------------------------
880
881 if (FlutterEngineShutdown(g_state.engine) != kSuccess) {
882 std::cerr << "Flutter Engine shutdown failed." << std::endl;
883 }
884
885 d.vkDestroyCommandPool(g_state.device, g_state.swapchain_command_pool,
886 nullptr);
887 d.vkDestroySemaphore(g_state.device, g_state.present_transition_semaphore,
888 nullptr);
889 d.vkDestroyFence(g_state.device, g_state.image_ready_fence, nullptr);
890
891 if (g_state.swapchain) {
892 d.vkDestroySwapchainKHR(g_state.device, g_state.swapchain, nullptr);
893 }
894
895 d.vkDestroyDevice(g_state.device, nullptr);
896 d.vkDestroySurfaceKHR(g_state.instance, g_state.surface, nullptr);
897
898 if (g_debug.enable_validation_layers) {
899 if (g_debug.report_callback) {
900 d.vkDestroyDebugReportCallbackEXT(g_state.instance,
901 g_debug.report_callback, nullptr);
902 }
903 if (g_debug.utils_messenger_callback) {
904 d.vkDestroyDebugUtilsMessengerEXT(
905 g_state.instance, g_debug.utils_messenger_callback, nullptr);
906 }
907 }
908
909 d.vkDestroyInstance(g_state.instance, nullptr);
910
911 glfwDestroyWindow(g_state.window);
912 glfwTerminate();
913
914 return 0;
915}
#define UINT32_MAX
Definition comments.cc:87
int32_t x
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:1971
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2660
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:2649
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition embedder.cc:2769
@ kVulkan
Definition embedder.h:86
FlutterPointerPhase
The phase of the pointer event.
Definition embedder.h:1237
@ kUp
Definition embedder.h:1245
@ kDown
Definition embedder.h:1252
@ kMove
Definition embedder.h:1257
FlutterEngineResult
Definition embedder.h:72
@ kSuccess
Definition embedder.h:73
int64_t FlutterViewId
Definition embedder.h:386
void * FlutterVulkanInstanceHandle
Alias for VkInstance.
Definition embedder.h:912
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
FlutterVulkanImage * image
bool InitializeSwapchain()
Definition main.cc:165
VkSwapchainKHR swapchain
Definition main.cc:80
std::vector< VkCommandBuffer > present_transition_buffers
Definition main.cc:74
auto & d
Definition main.cc:28
VkBool32 DebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT *cb_data, void *)
Definition main.cc:449
int main(int argc, char **argv)
Definition main.cc:473
bool resize_pending
Definition main.cc:86
uint32_t last_image_index
Definition main.cc:82
FlutterVulkanImage FlutterGetNextImageCallback(void *user_data, const FlutterFrameInfo *frame_info)
Definition main.cc:350
bool FlutterPresentCallback(void *user_data, const FlutterVulkanImage *image)
Definition main.cc:378
void PrintUsage()
Definition main.cc:159
VkBool32 DebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char *pLayerPrefix, const char *pMessage, void *)
Definition main.cc:423
void GLFWframebufferSizeCallback(GLFWwindow *window, int width, int height)
Definition main.cc:145
VkPhysicalDevice physical_device
Definition main.cc:67
std::vector< const char * > enabled_device_extensions
Definition main.cc:68
GLFWwindow * window
Definition main.cc:60
struct @14 g_state
Global struct for holding the Window+Vulkan state.
struct @13 g_debug
VkDevice device
Definition main.cc:69
static double g_pixelRatio
Definition main.cc:43
FlutterEngine engine
Definition main.cc:84
bool enable_validation_layers
Definition main.cc:36
VkCommandPool swapchain_command_pool
Definition main.cc:73
bool report_supported
Definition main.cc:38
static constexpr FlutterViewId kImplicitViewId
Definition main.cc:50
static const VkPresentModeKHR kPreferredPresentMode
Definition main.cc:49
void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, FlutterPointerPhase phase, double x, double y)
Definition main.cc:93
VkInstance instance
Definition main.cc:64
VkQueue queue
Definition main.cc:71
VkDebugReportCallbackEXT report_callback
Definition main.cc:39
std::vector< const char * > enabled_instance_extensions
Definition main.cc:62
VkSurfaceFormatKHR surface_format
Definition main.cc:79
static const size_t kInitialWindowHeight
Definition main.cc:45
bool utils_supported
Definition main.cc:37
VkSemaphore present_transition_semaphore
Definition main.cc:77
void * FlutterGetInstanceProcAddressCallback(void *user_data, FlutterVulkanInstanceHandle instance, const char *procname)
Definition main.cc:414
static const size_t kInitialWindowWidth
Definition main.cc:44
void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
Definition main.cc:135
VkDebugUtilsMessengerEXT utils_messenger_callback
Definition main.cc:40
VkFence image_ready_fence
Definition main.cc:76
void GLFWcursorPositionCallback(GLFWwindow *window, double x, double y)
Definition main.cc:112
uint32_t queue_family_index
Definition main.cc:70
void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, int mods)
Definition main.cc:116
std::vector< const char * > enabled_layer_names
Definition main.cc:63
std::vector< VkImage > swapchain_images
Definition main.cc:81
VkSurfaceKHR surface
Definition main.cc:65
void GLFW_ErrorCallback(int error, const char *description)
Definition main.cc:89
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const uint8_t uint32_t uint32_t GError ** error
uint32_t uint32_t * format
#define GLFW_TRUE
char ** argv
Definition library.h:9
double y
int32_t height
int32_t width
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition embedder.h:1308
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
Definition embedder.h:2458
FlutterVulkanRendererConfig vulkan
Definition embedder.h:1036
FlutterRendererType type
Definition embedder.h:1031
uint32_t format
The VkFormat of the image (for example: VK_FORMAT_R8G8B8A8_UNORM).
Definition embedder.h:933
FlutterVulkanQueueHandle queue
Definition embedder.h:977
FlutterVulkanDeviceHandle device
Definition embedder.h:968
FlutterVulkanInstanceProcAddressCallback get_instance_proc_address_callback
Definition embedder.h:1006
size_t struct_size
The size of this struct. Must be sizeof(FlutterVulkanRendererConfig).
Definition embedder.h:956
size_t enabled_instance_extension_count
Definition embedder.h:980
uint32_t queue_family_index
The queue family index of the VkQueue supplied in the next field.
Definition embedder.h:970
FlutterVulkanImageCallback get_next_image_callback
Definition embedder.h:1010
const char ** enabled_instance_extensions
Definition embedder.h:987
const char ** enabled_device_extensions
Definition embedder.h:998
FlutterVulkanInstanceHandle instance
Definition embedder.h:963
FlutterVulkanPresentCallback present_image_callback
Definition embedder.h:1016
FlutterVulkanPhysicalDeviceHandle physical_device
VkPhysicalDevice handle.
Definition embedder.h:965
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:1047