Flutter Engine
The Flutter Engine
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. More...
 

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< const SkImage > image
Definition: SkRecords.h:269

◆ 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
Definition: vulkan_core.h:7748
VkStructureType sType
Definition: vulkan_core.h:3283
VkFlags VkPipelineStageFlags
Definition: vulkan_core.h:2470
VkResult
Definition: vulkan_core.h:140
@ VK_SUBOPTIMAL_KHR
Definition: vulkan_core.h:167
@ VK_SUCCESS
Definition: vulkan_core.h:141
@ VK_ERROR_OUT_OF_DATE_KHR
Definition: vulkan_core.h:168
@ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
Definition: vulkan_core.h:2445
@ VK_STRUCTURE_TYPE_SUBMIT_INFO
Definition: vulkan_core.h:206
@ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR
Definition: vulkan_core.h:419

◆ 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:985
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:2429
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:1036

◆ 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:2320
int32_t height
int32_t width
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition: embedder.h:843

◆ 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
Definition: flutter_glfw.cc:33

◆ 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:973
@ kDown
Definition: embedder.h:980
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) {
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
uint32_t uint32_t * format
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
VkStructureType sType
Definition: vulkan_core.h:3891
uint32_t width
Definition: vulkan_core.h:2858
uint32_t height
Definition: vulkan_core.h:2859
VkStructureType sType
Definition: vulkan_core.h:2936
VkSurfaceTransformFlagBitsKHR currentTransform
Definition: vulkan_core.h:7656
@ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
Definition: vulkan_core.h:1348
@ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
Definition: vulkan_core.h:1333
@ VK_COMMAND_BUFFER_LEVEL_PRIMARY
Definition: vulkan_core.h:2178
@ VK_SHARING_MODE_EXCLUSIVE
Definition: vulkan_core.h:1813
@ VK_IMAGE_ASPECT_COLOR_BIT
Definition: vulkan_core.h:2238
@ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
Definition: vulkan_core.h:2356
@ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
Definition: vulkan_core.h:7640
@ VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT
Definition: vulkan_core.h:2824
@ VK_ACCESS_MEMORY_READ_BIT
Definition: vulkan_core.h:2215
VkPresentModeKHR
Definition: vulkan_core.h:7594
@ VK_FORMAT_B8G8R8A8_UNORM
Definition: vulkan_core.h:1503
@ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
Definition: vulkan_core.h:7605
@ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
Definition: vulkan_core.h:2448
#define VK_QUEUE_FAMILY_IGNORED
Definition: vulkan_core.h:127
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
Definition: vulkan_core.h:242
@ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
Definition: vulkan_core.h:244
@ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
Definition: vulkan_core.h:418
@ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER
Definition: vulkan_core.h:247

◆ 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(
642 }
643 }
644
645 if (g_state.physical_device == nullptr) {
646 std::cerr << "Failed to find a compatible Vulkan physical device."
647 << std::endl;
648 return EXIT_FAILURE;
649 }
650 }
651
652 /// --------------------------------------------------------------------------
653 /// Create a logical device and a graphics queue handle.
654 /// --------------------------------------------------------------------------
655
656 std::cout << "Enabling " << g_state.enabled_device_extensions.size()
657 << " device extensions:" << std::endl;
658 for (const char* extension : g_state.enabled_device_extensions) {
659 std::cout << " - " << extension << std::endl;
660 }
661
662 {
663 VkPhysicalDeviceFeatures device_features = {};
664
665 VkDeviceQueueCreateInfo graphics_queue = {};
667 graphics_queue.queueFamilyIndex = g_state.queue_family_index;
668 graphics_queue.queueCount = 1;
669 float priority = 1.0f;
670 graphics_queue.pQueuePriorities = &priority;
671
672 VkDeviceCreateInfo device_info = {};
674 device_info.enabledExtensionCount =
675 g_state.enabled_device_extensions.size();
676 device_info.ppEnabledExtensionNames =
677 g_state.enabled_device_extensions.data();
678 device_info.pEnabledFeatures = &device_features;
679 device_info.queueCreateInfoCount = 1;
680 device_info.pQueueCreateInfos = &graphics_queue;
681
682 if (d.vkCreateDevice(g_state.physical_device, &device_info, nullptr,
683 &g_state.device) != VK_SUCCESS) {
684 std::cerr << "Failed to create Vulkan logical device." << std::endl;
685 return EXIT_FAILURE;
686 }
687 }
688
689 d.vkGetDeviceQueue(g_state.device, g_state.queue_family_index, 0,
690 &g_state.queue);
691
692 /// --------------------------------------------------------------------------
693 /// Create sync primitives and command pool to use in the render loop
694 /// callbacks.
695 /// --------------------------------------------------------------------------
696
697 {
699 d.vkCreateFence(g_state.device, &f_info, nullptr,
700 &g_state.image_ready_fence);
701
702 VkSemaphoreCreateInfo s_info = {
704 d.vkCreateSemaphore(g_state.device, &s_info, nullptr,
705 &g_state.present_transition_semaphore);
706
707 VkCommandPoolCreateInfo pool_info = {
709 .queueFamilyIndex = g_state.queue_family_index,
710 };
711 d.vkCreateCommandPool(g_state.device, &pool_info, nullptr,
712 &g_state.swapchain_command_pool);
713 }
714
715 /// --------------------------------------------------------------------------
716 /// Create swapchain.
717 /// --------------------------------------------------------------------------
718
719 if (!InitializeSwapchain()) {
720 std::cerr << "Failed to create swapchain." << std::endl;
721 return EXIT_FAILURE;
722 }
723
724 /// --------------------------------------------------------------------------
725 /// Start Flutter Engine.
726 /// --------------------------------------------------------------------------
727
728 {
729 FlutterRendererConfig config = {};
730 config.type = kVulkan;
731 config.vulkan.struct_size = sizeof(config.vulkan);
732 config.vulkan.version = VK_MAKE_VERSION(1, 1, 0);
733 config.vulkan.instance = g_state.instance;
734 config.vulkan.physical_device = g_state.physical_device;
735 config.vulkan.device = g_state.device;
736 config.vulkan.queue_family_index = g_state.queue_family_index;
737 config.vulkan.queue = g_state.queue;
739 g_state.enabled_instance_extensions.size();
741 g_state.enabled_instance_extensions.data();
743 g_state.enabled_device_extensions.size();
745 g_state.enabled_device_extensions.data();
750
751 // This directory is generated by `flutter build bundle`.
752 std::string assets_path = project_path + "/build/flutter_assets";
754 .struct_size = sizeof(FlutterProjectArgs),
755 .assets_path = assets_path.c_str(),
756 .icu_data_path =
757 icudtl_path.c_str(), // Find this in your bin/cache directory.
758 };
761 &g_state.engine);
762 if (result != kSuccess || g_state.engine == nullptr) {
763 std::cerr << "Failed to start Flutter Engine." << std::endl;
764 return EXIT_FAILURE;
765 }
766
767 // Trigger a FlutterEngineSendWindowMetricsEvent to communicate the initial
768 // size of the window.
769 int width, height;
770 glfwGetFramebufferSize(g_state.window, &width, &height);
772 g_state.resize_pending = false;
773 }
774
775 /// --------------------------------------------------------------------------
776 /// GLFW render loop.
777 /// --------------------------------------------------------------------------
778
779 glfwSetKeyCallback(g_state.window, GLFWKeyCallback);
780 glfwSetFramebufferSizeCallback(g_state.window, GLFWframebufferSizeCallback);
781 glfwSetMouseButtonCallback(g_state.window, GLFWmouseButtonCallback);
782
783 while (!glfwWindowShouldClose(g_state.window)) {
784 glfwWaitEvents();
785 }
786
787 /// --------------------------------------------------------------------------
788 /// Cleanup.
789 /// --------------------------------------------------------------------------
790
791 if (FlutterEngineShutdown(g_state.engine) != kSuccess) {
792 std::cerr << "Flutter Engine shutdown failed." << std::endl;
793 }
794
795 d.vkDestroyCommandPool(g_state.device, g_state.swapchain_command_pool,
796 nullptr);
797 d.vkDestroySemaphore(g_state.device, g_state.present_transition_semaphore,
798 nullptr);
799 d.vkDestroyFence(g_state.device, g_state.image_ready_fence, nullptr);
800
801 d.vkDestroyDevice(g_state.device, nullptr);
802 d.vkDestroySurfaceKHR(g_state.instance, g_state.surface, nullptr);
803 d.vkDestroyInstance(g_state.instance, nullptr);
804
805 glfwDestroyWindow(g_state.window);
806 glfwTerminate();
807
808 return 0;
809}
int count
Definition: FontMgrTest.cpp:50
FlutterEngineResult FlutterEngineRun(size_t version, const FlutterRendererConfig *config, const FlutterProjectArgs *args, void *user_data, FLUTTER_API_SYMBOL(FlutterEngine) *engine_out)
Initialize and run a Flutter engine instance and return a handle to it. This is a convenience method ...
Definition: embedder.cc:1715
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) engine)
Shuts down a Flutter engine instance. The engine handle is no longer valid for any calls in the embed...
Definition: embedder.cc:2309
@ 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
static const bool g_enable_validation_layers
Definition: main.cc:26
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
char ** argv
Definition: library.h:9
FlutterVulkanRendererConfig vulkan
Definition: embedder.h:832
FlutterRendererType type
Definition: embedder.h:827
FlutterVulkanQueueHandle queue
Definition: embedder.h:773
FlutterVulkanDeviceHandle device
Definition: embedder.h:764
FlutterVulkanInstanceProcAddressCallback get_instance_proc_address_callback
Definition: embedder.h:802
size_t struct_size
The size of this struct. Must be sizeof(FlutterVulkanRendererConfig).
Definition: embedder.h:752
size_t enabled_instance_extension_count
Definition: embedder.h:776
uint32_t queue_family_index
The queue family index of the VkQueue supplied in the next field.
Definition: embedder.h:766
FlutterVulkanImageCallback get_next_image_callback
Definition: embedder.h:806
const char ** enabled_instance_extensions
Definition: embedder.h:783
const char ** enabled_device_extensions
Definition: embedder.h:794
size_t enabled_device_extension_count
Definition: embedder.h:786
FlutterVulkanInstanceHandle instance
Definition: embedder.h:759
FlutterVulkanPresentCallback present_image_callback
Definition: embedder.h:812
FlutterVulkanPhysicalDeviceHandle physical_device
VkPhysicalDevice handle.
Definition: embedder.h:761
VkStructureType sType
Definition: vulkan_core.h:3003
VkStructureType sType
Definition: vulkan_core.h:3865
uint32_t enabledExtensionCount
Definition: vulkan_core.h:3265
const VkDeviceQueueCreateInfo * pQueueCreateInfos
Definition: vulkan_core.h:3262
const VkPhysicalDeviceFeatures * pEnabledFeatures
Definition: vulkan_core.h:3267
const char *const * ppEnabledExtensionNames
Definition: vulkan_core.h:3266
uint32_t queueCreateInfoCount
Definition: vulkan_core.h:3261
VkStructureType sType
Definition: vulkan_core.h:3258
const float * pQueuePriorities
Definition: vulkan_core.h:3254
VkStructureType sType
Definition: vulkan_core.h:3249
VkStructureType sType
Definition: vulkan_core.h:3386
VkPhysicalDeviceType deviceType
Definition: vulkan_core.h:3234
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]
Definition: vulkan_core.h:3235
VkPhysicalDeviceLimits limits
Definition: vulkan_core.h:3237
VkStructureType sType
Definition: vulkan_core.h:3392
#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
Definition: vulkan_core.h:9396
@ VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
Definition: vulkan_core.h:1783
@ VK_QUEUE_GRAPHICS_BIT
Definition: vulkan_core.h:2413
uint32_t VkBool32
Definition: vulkan_core.h:94
#define VK_KHR_SWAPCHAIN_EXTENSION_NAME
Definition: vulkan_core.h:7707
@ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
Definition: vulkan_core.h:211
@ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO
Definition: vulkan_core.h:204
@ VK_STRUCTURE_TYPE_APPLICATION_INFO
Definition: vulkan_core.h:202
@ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
Definition: vulkan_core.h:203
@ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
Definition: vulkan_core.h:210
@ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
Definition: vulkan_core.h:205
@ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO
Definition: vulkan_core.h:241

◆ 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

FlutterEngine 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 { ... } 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.