Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Macros | Functions | Variables
main.cc File Reference
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <optional>
#include <tuple>
#include <vector>
#include "vulkan/vulkan.hpp"
#include "GLFW/glfw3.h"
#include "embedder.h"

Go to the source code of this file.

Macros

#define VULKAN_HPP_NO_EXCEPTIONS   1
 
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC   1
 

Functions

void GLFW_ErrorCallback (int error, const char *description)
 
void GLFWcursorPositionCallbackAtPhase (GLFWwindow *window, FlutterPointerPhase phase, double x, double y)
 
void GLFWcursorPositionCallback (GLFWwindow *window, double x, double y)
 
void GLFWmouseButtonCallback (GLFWwindow *window, int key, int action, int mods)
 
void GLFWKeyCallback (GLFWwindow *window, int key, int scancode, int action, int mods)
 
void GLFWframebufferSizeCallback (GLFWwindow *window, int width, int height)
 
void PrintUsage ()
 
bool InitializeSwapchain ()
 
FlutterVulkanImage FlutterGetNextImageCallback (void *user_data, const FlutterFrameInfo *frame_info)
 
bool FlutterPresentCallback (void *user_data, const FlutterVulkanImage *image)
 
void * FlutterGetInstanceProcAddressCallback (void *user_data, FlutterVulkanInstanceHandle instance, const char *procname)
 
int main (int argc, char **argv)
 

Variables

VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d = vk::defaultDispatchLoaderDynamic
 
static const bool g_enable_validation_layers = true
 
static double g_pixelRatio = 1.0
 
static const size_t kInitialWindowWidth = 800
 
static const size_t kInitialWindowHeight = 600
 
static const VkPresentModeKHR kPreferredPresentMode = VK_PRESENT_MODE_FIFO_KHR
 
static constexpr FlutterViewId kImplicitViewId = 0
 
struct { 
 
   GLFWwindow *   window 
 
   std::vector< const char * >   enabled_instance_extensions 
 
   VkInstance   instance 
 
   VkSurfaceKHR   surface 
 
   VkPhysicalDevice   physical_device 
 
   std::vector< const char * >   enabled_device_extensions 
 
   VkDevice   device 
 
   uint32_t   queue_family_index 
 
   VkQueue   queue 
 
   VkCommandPool   swapchain_command_pool 
 
   std::vector< VkCommandBuffer >   present_transition_buffers 
 
   VkFence   image_ready_fence 
 
   VkSemaphore   present_transition_semaphore 
 
   VkSurfaceFormatKHR   surface_format 
 
   VkSwapchainKHR   swapchain 
 
   std::vector< VkImage >   swapchain_images 
 
   uint32_t   last_image_index 
 
   FlutterEngine   engine 
 
   bool   resize_pending = false 
 
g_state 
 Global struct for holding the Window+Vulkan state.
 

Macro Definition Documentation

◆ VULKAN_HPP_DISPATCH_LOADER_DYNAMIC

#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC   1

Definition at line 14 of file main.cc.

◆ VULKAN_HPP_NO_EXCEPTIONS

#define VULKAN_HPP_NO_EXCEPTIONS   1

Definition at line 13 of file main.cc.

Function Documentation

◆ FlutterGetInstanceProcAddressCallback()

void * FlutterGetInstanceProcAddressCallback ( void *  user_data,
FlutterVulkanInstanceHandle  instance,
const char *  procname 
)

Definition at line 398 of file main.cc.

401 {
402 auto* proc = glfwGetInstanceProcAddress(
403 reinterpret_cast<VkInstance>(instance), procname);
404 return reinterpret_cast<void*>(proc);
405}
VkInstance instance
Definition main.cc:48

◆ FlutterGetNextImageCallback()

FlutterVulkanImage FlutterGetNextImageCallback ( void *  user_data,
const FlutterFrameInfo frame_info 
)

Definition at line 334 of file main.cc.

336 {
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}
bool InitializeSwapchain()
Definition main.cc:149
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
struct @13 g_state
Global struct for holding the Window+Vulkan state.
sk_sp< SkImage > image
Definition examples.cpp:29

◆ FlutterPresentCallback()

bool FlutterPresentCallback ( void *  user_data,
const FlutterVulkanImage image 
)

Definition at line 362 of file main.cc.

362 {
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}
GAsyncResult * result
VkStructureType sType
VkStructureType sType
VkFlags VkPipelineStageFlags
VkResult
@ VK_SUBOPTIMAL_KHR
@ VK_SUCCESS
@ VK_ERROR_OUT_OF_DATE_KHR
@ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
@ VK_STRUCTURE_TYPE_SUBMIT_INFO
@ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR

◆ GLFW_ErrorCallback()

void GLFW_ErrorCallback ( int  error,
const char *  description 
)

Definition at line 73 of file main.cc.

73 {
74 std::cerr << "GLFW Error: (" << error << ") " << description << std::endl;
75}
const uint8_t uint32_t uint32_t GError ** error

◆ GLFWcursorPositionCallback()

void GLFWcursorPositionCallback ( GLFWwindow *  window,
double  x,
double  y 
)

Definition at line 96 of file main.cc.

96 {
98}
@ kMove
Definition embedder.h:983
GLFWwindow * window
Definition main.cc:45
void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, FlutterPointerPhase phase, double x, double y)
Definition main.cc:77
double y
double x

◆ GLFWcursorPositionCallbackAtPhase()

void GLFWcursorPositionCallbackAtPhase ( GLFWwindow *  window,
FlutterPointerPhase  phase,
double  x,
double  y 
)

Definition at line 77 of file main.cc.

80 {
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}
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition embedder.cc:2423
static double g_pixelRatio
Definition main.cc:28
static constexpr FlutterViewId kImplicitViewId
Definition main.cc:35
FlKeyEvent * event
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition embedder.h:1034

◆ GLFWframebufferSizeCallback()

void GLFWframebufferSizeCallback ( GLFWwindow *  window,
int  width,
int  height 
)

Definition at line 129 of file main.cc.

129 {
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}
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2314
int32_t height
int32_t width
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:841

◆ GLFWKeyCallback()

void GLFWKeyCallback ( GLFWwindow *  window,
int  key,
int  scancode,
int  action,
int  mods 
)

Definition at line 119 of file main.cc.

123 {
124 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
125 glfwSetWindowShouldClose(window, GLFW_TRUE);
126 }
127}
#define GLFW_TRUE

◆ GLFWmouseButtonCallback()

void GLFWmouseButtonCallback ( GLFWwindow *  window,
int  key,
int  action,
int  mods 
)

Definition at line 100 of file main.cc.

103 {
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}
@ kUp
Definition embedder.h:971
@ kDown
Definition embedder.h:978
void GLFWcursorPositionCallback(GLFWwindow *window, double x, double y)
Definition main.cc:96

◆ InitializeSwapchain()

bool InitializeSwapchain ( )

Choose an image format that can be presented to the surface, preferring

the common BGRA+sRGB if available.


Choose the presentable image size that's as close as possible to the

window size.


Choose the present mode.


Create the swapchain.


Fetch swapchain images.


Record a command buffer for each of the images to be executed prior to

presenting.

Definition at line 149 of file main.cc.

149 {
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}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static const VkPresentModeKHR kPreferredPresentMode
Definition main.cc:34
static const uint8_t buffer[]
uint32_t uint32_t * format
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition switches.h:228
uint32_t width
uint32_t height
VkStructureType sType
VkSurfaceTransformFlagBitsKHR currentTransform
@ 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
@ VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT
@ VK_ACCESS_MEMORY_READ_BIT
VkPresentModeKHR
@ VK_FORMAT_B8G8R8A8_UNORM
@ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
@ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
#define VK_QUEUE_FAMILY_IGNORED
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
@ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
@ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER

◆ main()

int main ( int  argc,
char **  argv 
)

Create a GLFW window.


Dynamically load the Vulkan loader with GLFW and use it to populate GLAD's

proc table.


Create a Vulkan instance.


Create the window surface.


Select a compatible physical device.


Create a logical device and a graphics queue handle.


Create sync primitives and command pool to use in the render loop

callbacks.


Create swapchain.


Start Flutter Engine.


GLFW render loop.


Cleanup.

Definition at line 407 of file main.cc.

407 {
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}
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 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
@ kVulkan
Definition embedder.h:86
FlutterEngineResult
Definition embedder.h:72
@ kSuccess
Definition embedder.h:73
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
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
std::vector< const char * > enabled_device_extensions
Definition main.cc:52
static const bool g_enable_validation_layers
Definition main.cc:26
std::vector< const char * > enabled_instance_extensions
Definition main.cc:47
static const size_t kInitialWindowHeight
Definition main.cc:30
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
void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, int mods)
Definition main.cc:100
void GLFW_ErrorCallback(int error, const char *description)
Definition main.cc:73
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint8_t value
char ** argv
Definition library.h:9
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
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
VkStructureType sType
VkStructureType sType
VkPhysicalDeviceType deviceType
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]
VkPhysicalDeviceLimits limits
VkStructureType sType
#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME
#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
uint32_t VkBool32
Definition vulkan_core.h:94
#define VK_KHR_SWAPCHAIN_EXTENSION_NAME
@ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
@ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO
@ VK_STRUCTURE_TYPE_APPLICATION_INFO
@ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
@ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
@ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
@ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO

◆ PrintUsage()

void PrintUsage ( )

Definition at line 143 of file main.cc.

143 {
144 std::cerr
145 << "usage: embedder_example_vulkan <path to project> <path to icudtl.dat>"
146 << std::endl;
147}

Variable Documentation

◆ d

VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto& d = vk::defaultDispatchLoaderDynamic

Definition at line 19 of file main.cc.

◆ device

VkDevice device

Definition at line 53 of file main.cc.

◆ enabled_device_extensions

std::vector<const char*> enabled_device_extensions

Definition at line 52 of file main.cc.

◆ enabled_instance_extensions

std::vector<const char*> enabled_instance_extensions

Definition at line 47 of file main.cc.

◆ engine

self engine = engine

Definition at line 68 of file main.cc.

◆ g_enable_validation_layers

const bool g_enable_validation_layers = true
static

Definition at line 26 of file main.cc.

◆ g_pixelRatio

double g_pixelRatio = 1.0
static

Definition at line 28 of file main.cc.

◆ [struct]

struct { ... } g_state

Global struct for holding the Window+Vulkan state.

◆ image_ready_fence

VkFence image_ready_fence

Definition at line 60 of file main.cc.

◆ instance

VkInstance instance

Definition at line 48 of file main.cc.

◆ kImplicitViewId

constexpr FlutterViewId kImplicitViewId = 0
staticconstexpr

Definition at line 35 of file main.cc.

◆ kInitialWindowHeight

const size_t kInitialWindowHeight = 600
static

Definition at line 30 of file main.cc.

◆ kInitialWindowWidth

const size_t kInitialWindowWidth = 800
static

Definition at line 29 of file main.cc.

◆ kPreferredPresentMode

const VkPresentModeKHR kPreferredPresentMode = VK_PRESENT_MODE_FIFO_KHR
static

Definition at line 34 of file main.cc.

◆ last_image_index

uint32_t last_image_index

Definition at line 66 of file main.cc.

◆ physical_device

VkPhysicalDevice physical_device

Definition at line 51 of file main.cc.

◆ present_transition_buffers

std::vector<VkCommandBuffer> present_transition_buffers

Definition at line 58 of file main.cc.

◆ present_transition_semaphore

VkSemaphore present_transition_semaphore

Definition at line 61 of file main.cc.

◆ queue

VkQueue queue

Definition at line 55 of file main.cc.

◆ queue_family_index

uint32_t queue_family_index

Definition at line 54 of file main.cc.

◆ resize_pending

bool resize_pending = false

Definition at line 70 of file main.cc.

◆ surface

VkSurfaceKHR surface

Definition at line 49 of file main.cc.

◆ surface_format

VkSurfaceFormatKHR surface_format

Definition at line 63 of file main.cc.

◆ swapchain

VkSwapchainKHR swapchain

Definition at line 64 of file main.cc.

◆ swapchain_command_pool

VkCommandPool swapchain_command_pool

Definition at line 57 of file main.cc.

◆ swapchain_images

std::vector<VkImage> swapchain_images

Definition at line 65 of file main.cc.

◆ window

GLFWwindow* window

Definition at line 45 of file main.cc.