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
 
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 391 of file main.cc.

394 {
395 auto* proc = glfwGetInstanceProcAddress(
396 reinterpret_cast<VkInstance>(instance), procname);
397 return reinterpret_cast<void*>(proc);
398}
VkInstance instance
Definition main.cc:47

◆ FlutterGetNextImageCallback()

FlutterVulkanImage FlutterGetNextImageCallback ( void *  user_data,
const FlutterFrameInfo frame_info 
)

Definition at line 327 of file main.cc.

329 {
330 // If the GLFW framebuffer has been resized, discard the swapchain and create
331 // a new one.
332 if (g_state.resize_pending) {
334 }
335
336 d.vkAcquireNextImageKHR(g_state.device, g_state.swapchain, UINT64_MAX,
337 nullptr, g_state.image_ready_fence,
338 &g_state.last_image_index);
339
340 // Flutter Engine expects the image to be available for transitioning and
341 // attaching immediately, and so we need to force a host sync here before
342 // returning.
343 d.vkWaitForFences(g_state.device, 1, &g_state.image_ready_fence, true,
344 UINT64_MAX);
345 d.vkResetFences(g_state.device, 1, &g_state.image_ready_fence);
346
347 return {
348 .struct_size = sizeof(FlutterVulkanImage),
349 .image = reinterpret_cast<uint64_t>(
350 g_state.swapchain_images[g_state.last_image_index]),
351 .format = g_state.surface_format.format,
352 };
353}
bool InitializeSwapchain()
Definition main.cc:142
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 355 of file main.cc.

355 {
356 VkPipelineStageFlags stage_flags =
358 VkSubmitInfo submit_info = {
360 .waitSemaphoreCount = 0,
361 .pWaitSemaphores = nullptr,
362 .pWaitDstStageMask = &stage_flags,
363 .commandBufferCount = 1,
364 .pCommandBuffers =
365 &g_state.present_transition_buffers[g_state.last_image_index],
366 .signalSemaphoreCount = 1,
367 .pSignalSemaphores = &g_state.present_transition_semaphore,
368 };
369 d.vkQueueSubmit(g_state.queue, 1, &submit_info, nullptr);
370
371 VkPresentInfoKHR present_info = {
373 .waitSemaphoreCount = 1,
374 .pWaitSemaphores = &g_state.present_transition_semaphore,
375 .swapchainCount = 1,
376 .pSwapchains = &g_state.swapchain,
377 .pImageIndices = &g_state.last_image_index,
378 };
379 VkResult result = d.vkQueuePresentKHR(g_state.queue, &present_info);
380
381 // If the swapchain is no longer compatible with the surface, discard the
382 // swapchain and create a new one.
385 }
386 d.vkQueueWaitIdle(g_state.queue);
387
388 return result == VK_SUCCESS;
389}
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 72 of file main.cc.

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

◆ GLFWcursorPositionCallback()

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

Definition at line 92 of file main.cc.

92 {
94}
@ kMove
Definition embedder.h:885
GLFWwindow * window
Definition main.cc:44
void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, FlutterPointerPhase phase, double x, double y)
Definition main.cc:76
double y
double x

◆ GLFWcursorPositionCallbackAtPhase()

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

Definition at line 76 of file main.cc.

79 {
80 FlutterPointerEvent event = {};
81 event.struct_size = sizeof(event);
82 event.phase = phase;
83 event.x = x * g_pixelRatio;
84 event.y = y * g_pixelRatio;
85 event.timestamp =
86 std::chrono::duration_cast<std::chrono::microseconds>(
87 std::chrono::high_resolution_clock::now().time_since_epoch())
88 .count();
90}
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition embedder.cc:2280
static double g_pixelRatio
Definition main.cc:28
FlKeyEvent * event
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition embedder.h:936

◆ GLFWframebufferSizeCallback()

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

Definition at line 125 of file main.cc.

125 {
126 g_state.resize_pending = true;
127
128 FlutterWindowMetricsEvent event = {};
129 event.struct_size = sizeof(event);
130 event.width = width;
131 event.height = height;
132 event.pixel_ratio = g_pixelRatio;
134}
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition embedder.cc:2140
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 115 of file main.cc.

119 {
120 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
121 glfwSetWindowShouldClose(window, GLFW_TRUE);
122 }
123}
#define GLFW_TRUE

◆ GLFWmouseButtonCallback()

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

Definition at line 96 of file main.cc.

99 {
100 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) {
101 double x, y;
102 glfwGetCursorPos(window, &x, &y);
104 glfwSetCursorPosCallback(window, GLFWcursorPositionCallback);
105 }
106
107 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_RELEASE) {
108 double x, y;
109 glfwGetCursorPos(window, &x, &y);
111 glfwSetCursorPosCallback(window, nullptr);
112 }
113}
@ kUp
Definition embedder.h:873
@ kDown
Definition embedder.h:880
void GLFWcursorPositionCallback(GLFWwindow *window, double x, double y)
Definition main.cc:92

◆ 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 142 of file main.cc.

142 {
143 if (g_state.resize_pending) {
144 g_state.resize_pending = false;
145 d.vkDestroySwapchainKHR(g_state.device, g_state.swapchain, nullptr);
146
147 d.vkQueueWaitIdle(g_state.queue);
148 d.vkResetCommandPool(g_state.device, g_state.swapchain_command_pool,
150 }
151
152 /// --------------------------------------------------------------------------
153 /// Choose an image format that can be presented to the surface, preferring
154 /// the common BGRA+sRGB if available.
155 /// --------------------------------------------------------------------------
156
157 uint32_t format_count;
158 d.vkGetPhysicalDeviceSurfaceFormatsKHR(
159 g_state.physical_device, g_state.surface, &format_count, nullptr);
160 std::vector<VkSurfaceFormatKHR> formats(format_count);
161 d.vkGetPhysicalDeviceSurfaceFormatsKHR(
162 g_state.physical_device, g_state.surface, &format_count, formats.data());
163 assert(!formats.empty()); // Shouldn't be possible.
164
165 g_state.surface_format = formats[0];
166 for (const auto& format : formats) {
167 if (format.format == VK_FORMAT_B8G8R8A8_UNORM &&
169 g_state.surface_format = format;
170 }
171 }
172
173 /// --------------------------------------------------------------------------
174 /// Choose the presentable image size that's as close as possible to the
175 /// window size.
176 /// --------------------------------------------------------------------------
177
178 VkExtent2D extent;
179
180 VkSurfaceCapabilitiesKHR surface_capabilities;
181 d.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
182 g_state.physical_device, g_state.surface, &surface_capabilities);
183
184 if (surface_capabilities.currentExtent.width != UINT32_MAX) {
185 // If the surface reports a specific extent, we must use it.
186 extent = surface_capabilities.currentExtent;
187 } else {
188 // `glfwGetWindowSize` returns the window size in screen coordinates, so we
189 // instead use `glfwGetFramebufferSize` to get the size in pixels in order
190 // to properly support high DPI displays.
191 int width, height;
192 glfwGetFramebufferSize(g_state.window, &width, &height);
193
194 VkExtent2D actual_extent = {
195 .width = static_cast<uint32_t>(width),
196 .height = static_cast<uint32_t>(height),
197 };
198 actual_extent.width =
199 std::max(surface_capabilities.minImageExtent.width,
200 std::min(surface_capabilities.maxImageExtent.width,
201 actual_extent.width));
202 actual_extent.height =
203 std::max(surface_capabilities.minImageExtent.height,
204 std::min(surface_capabilities.maxImageExtent.height,
205 actual_extent.height));
206 }
207
208 /// --------------------------------------------------------------------------
209 /// Choose the present mode.
210 /// --------------------------------------------------------------------------
211
212 uint32_t mode_count;
213 d.vkGetPhysicalDeviceSurfacePresentModesKHR(
214 g_state.physical_device, g_state.surface, &mode_count, nullptr);
215 std::vector<VkPresentModeKHR> modes(mode_count);
216 d.vkGetPhysicalDeviceSurfacePresentModesKHR(
217 g_state.physical_device, g_state.surface, &mode_count, modes.data());
218 assert(!formats.empty()); // Shouldn't be possible.
219
220 // If the preferred mode isn't available, just choose the first one.
221 VkPresentModeKHR present_mode = modes[0];
222 for (const auto& mode : modes) {
223 if (mode == kPreferredPresentMode) {
224 present_mode = mode;
225 break;
226 }
227 }
228
229 /// --------------------------------------------------------------------------
230 /// Create the swapchain.
231 /// --------------------------------------------------------------------------
232
235 .surface = g_state.surface,
236 .minImageCount = surface_capabilities.minImageCount + 1,
237 .imageFormat = g_state.surface_format.format,
238 .imageColorSpace = g_state.surface_format.colorSpace,
239 .imageExtent = extent,
240 .imageArrayLayers = 1,
242 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
243 .queueFamilyIndexCount = 0,
244 .pQueueFamilyIndices = nullptr,
245 .preTransform = surface_capabilities.currentTransform,
246 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
247 .presentMode = present_mode,
248 .clipped = true,
249 };
250 if (d.vkCreateSwapchainKHR(g_state.device, &info, nullptr,
251 &g_state.swapchain) != VK_SUCCESS) {
252 return false;
253 }
254
255 /// --------------------------------------------------------------------------
256 /// Fetch swapchain images.
257 /// --------------------------------------------------------------------------
258
259 uint32_t image_count;
260 d.vkGetSwapchainImagesKHR(g_state.device, g_state.swapchain, &image_count,
261 nullptr);
262 g_state.swapchain_images.resize(image_count);
263 d.vkGetSwapchainImagesKHR(g_state.device, g_state.swapchain, &image_count,
264 g_state.swapchain_images.data());
265
266 /// --------------------------------------------------------------------------
267 /// Record a command buffer for each of the images to be executed prior to
268 /// presenting.
269 /// --------------------------------------------------------------------------
270
271 g_state.present_transition_buffers.resize(g_state.swapchain_images.size());
272
273 VkCommandBufferAllocateInfo buffers_info = {
275 .commandPool = g_state.swapchain_command_pool,
277 .commandBufferCount =
278 static_cast<uint32_t>(g_state.present_transition_buffers.size()),
279 };
280 d.vkAllocateCommandBuffers(g_state.device, &buffers_info,
281 g_state.present_transition_buffers.data());
282
283 for (size_t i = 0; i < g_state.swapchain_images.size(); i++) {
284 auto image = g_state.swapchain_images[i];
285 auto buffer = g_state.present_transition_buffers[i];
286
287 VkCommandBufferBeginInfo begin_info = {
289 d.vkBeginCommandBuffer(buffer, &begin_info);
290
291 // Flutter Engine hands back the image after writing to it
292 VkImageMemoryBarrier barrier = {
294 .srcAccessMask = 0,
295 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
298 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
299 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
300 .image = image,
301 .subresourceRange = {
302 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
303 .baseMipLevel = 0,
304 .levelCount = 1,
305 .baseArrayLayer = 0,
306 .layerCount = 1,
307 }};
308 d.vkCmdPipelineBarrier(
309 buffer, // commandBuffer
312 0, // dependencyFlags
313 0, // memoryBarrierCount
314 nullptr, // pMemoryBarriers
315 0, // bufferMemoryBarrierCount
316 nullptr, // pBufferMemoryBarriers
317 1, // imageMemoryBarrierCount
318 &barrier // pImageMemoryBarriers
319 );
320
321 d.vkEndCommandBuffer(buffer);
322 }
323
324 return true; // \o/
325}
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 400 of file main.cc.

400 {
401 if (argc != 3) {
402 PrintUsage();
403 return 1;
404 }
405
406 std::string project_path = argv[1];
407 std::string icudtl_path = argv[2];
408
409 /// --------------------------------------------------------------------------
410 /// Create a GLFW window.
411 /// --------------------------------------------------------------------------
412
413 {
414 if (!glfwInit()) {
415 std::cerr << "Failed to initialize GLFW." << std::endl;
416 return EXIT_FAILURE;
417 }
418
419 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
420 g_state.window = glfwCreateWindow(kInitialWindowWidth, kInitialWindowHeight,
421 "Flutter", nullptr, nullptr);
422 if (!g_state.window) {
423 std::cerr << "Failed to create GLFW window." << std::endl;
424 return EXIT_FAILURE;
425 }
426
427 int framebuffer_width, framebuffer_height;
428 glfwGetFramebufferSize(g_state.window, &framebuffer_width,
429 &framebuffer_height);
430 g_pixelRatio = framebuffer_width / kInitialWindowWidth;
431
432 glfwSetErrorCallback(GLFW_ErrorCallback);
433 }
434
435 /// --------------------------------------------------------------------------
436 /// Dynamically load the Vulkan loader with GLFW and use it to populate GLAD's
437 /// proc table.
438 /// --------------------------------------------------------------------------
439
440 if (!glfwVulkanSupported()) {
441 std::cerr << "GLFW was unable to resolve either a Vulkan loader or a "
442 "compatible physical device!"
443 << std::endl;
444#if defined(__APPLE__)
445 std::cerr
446 << "NOTE: Apple platforms don't ship with a Vulkan loader or any "
447 "Vulkan drivers. Follow this guide to set up a Vulkan loader on "
448 "macOS and use the MoltenVK ICD: "
449 "https://vulkan.lunarg.com/doc/sdk/latest/mac/getting_started.html"
450 << std::endl;
451#endif
452 return EXIT_FAILURE;
453 }
454
455 VULKAN_HPP_DEFAULT_DISPATCHER.init(glfwGetInstanceProcAddress);
456
457 /// --------------------------------------------------------------------------
458 /// Create a Vulkan instance.
459 /// --------------------------------------------------------------------------
460
461 {
462 uint32_t extension_count;
463 const char** glfw_extensions =
464 glfwGetRequiredInstanceExtensions(&extension_count);
465 g_state.enabled_instance_extensions.resize(extension_count);
466 memcpy(g_state.enabled_instance_extensions.data(), glfw_extensions,
467 extension_count * sizeof(char*));
468
470 g_state.enabled_instance_extensions.push_back(
472 }
473
474 std::cout << "Enabling " << g_state.enabled_instance_extensions.size()
475 << " instance extensions:" << std::endl;
476 for (const auto& extension : g_state.enabled_instance_extensions) {
477 std::cout << " - " << extension << std::endl;
478 }
479
480 VkApplicationInfo app_info = {
482 .pNext = nullptr,
483 .pApplicationName = "Flutter",
484 .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
485 .pEngineName = "No Engine",
486 .engineVersion = VK_MAKE_VERSION(1, 0, 0),
487 .apiVersion = VK_MAKE_VERSION(1, 1, 0),
488 };
491 info.flags = 0;
492 info.pApplicationInfo = &app_info;
493 info.enabledExtensionCount = g_state.enabled_instance_extensions.size();
494 info.ppEnabledExtensionNames = g_state.enabled_instance_extensions.data();
496 auto available_layers = vk::enumerateInstanceLayerProperties();
497
498 const char* layer = "VK_LAYER_KHRONOS_validation";
499 for (const auto& l : available_layers.value) {
500 if (strcmp(l.layerName, layer) == 0) {
501 info.enabledLayerCount = 1;
502 info.ppEnabledLayerNames = &layer;
503 break;
504 }
505 }
506 }
507
508 if (d.vkCreateInstance(&info, nullptr, &g_state.instance) != VK_SUCCESS) {
509 std::cerr << "Failed to create Vulkan instance." << std::endl;
510 return EXIT_FAILURE;
511 }
512 }
513
514 // Load instance procs.
515 VULKAN_HPP_DEFAULT_DISPATCHER.init(vk::Instance(g_state.instance));
516
517 /// --------------------------------------------------------------------------
518 /// Create the window surface.
519 /// --------------------------------------------------------------------------
520
521 if (glfwCreateWindowSurface(g_state.instance, g_state.window, NULL,
522 &g_state.surface) != VK_SUCCESS) {
523 std::cerr << "Failed to create window surface." << std::endl;
524 return EXIT_FAILURE;
525 }
526
527 /// --------------------------------------------------------------------------
528 /// Select a compatible physical device.
529 /// --------------------------------------------------------------------------
530
531 {
532 uint32_t count;
533 d.vkEnumeratePhysicalDevices(g_state.instance, &count, nullptr);
534 std::vector<VkPhysicalDevice> physical_devices(count);
535 d.vkEnumeratePhysicalDevices(g_state.instance, &count,
536 physical_devices.data());
537
538 std::cout << "Enumerating " << count << " physical device(s)." << std::endl;
539
540 uint32_t selected_score = 0;
541 for (const auto& pdevice : physical_devices) {
544 d.vkGetPhysicalDeviceProperties(pdevice, &properties);
545 d.vkGetPhysicalDeviceFeatures(pdevice, &features);
546
547 std::cout << "Checking device: " << properties.deviceName << std::endl;
548
549 uint32_t score = 0;
550 std::vector<const char*> supported_extensions;
551
552 uint32_t qfp_count;
553 d.vkGetPhysicalDeviceQueueFamilyProperties(pdevice, &qfp_count, nullptr);
554 std::vector<VkQueueFamilyProperties> qfp(qfp_count);
555 d.vkGetPhysicalDeviceQueueFamilyProperties(pdevice, &qfp_count,
556 qfp.data());
557 std::optional<uint32_t> graphics_queue_family;
558 for (uint32_t i = 0; i < qfp.size(); i++) {
559 // Only pick graphics queues that can also present to the surface.
560 // Graphics queues that can't present are rare if not nonexistent, but
561 // the spec allows for this, so check it anyways.
562 VkBool32 surface_present_supported;
563 d.vkGetPhysicalDeviceSurfaceSupportKHR(pdevice, i, g_state.surface,
564 &surface_present_supported);
565
566 if (!graphics_queue_family.has_value() &&
567 qfp[i].queueFlags & VK_QUEUE_GRAPHICS_BIT &&
568 surface_present_supported) {
569 graphics_queue_family = i;
570 }
571 }
572
573 // Skip physical devices that don't have a graphics queue.
574 if (!graphics_queue_family.has_value()) {
575 std::cout << " - Skipping due to no suitable graphics queues."
576 << std::endl;
577 continue;
578 }
579
580 // Prefer discrete GPUs.
582 score += 1 << 30;
583 }
584
585 uint32_t extension_count;
586 d.vkEnumerateDeviceExtensionProperties(pdevice, nullptr, &extension_count,
587 nullptr);
588 std::vector<VkExtensionProperties> available_extensions(extension_count);
589 d.vkEnumerateDeviceExtensionProperties(pdevice, nullptr, &extension_count,
590 available_extensions.data());
591
592 bool supports_swapchain = false;
593 for (const auto& available_extension : available_extensions) {
595 available_extension.extensionName) == 0) {
596 supports_swapchain = true;
597 supported_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
598 }
599 // The spec requires VK_KHR_portability_subset be enabled whenever it's
600 // available on a device. It's present on compatibility ICDs like
601 // MoltenVK.
602 else if (strcmp("VK_KHR_portability_subset",
603 available_extension.extensionName) == 0) {
604 supported_extensions.push_back("VK_KHR_portability_subset");
605 }
606
607 // Prefer GPUs that support VK_KHR_get_memory_requirements2.
609 available_extension.extensionName) == 0) {
610 score += 1 << 29;
611 supported_extensions.push_back(
613 }
614 }
615
616 // Skip physical devices that don't have swapchain support.
617 if (!supports_swapchain) {
618 std::cout << " - Skipping due to lack of swapchain support."
619 << std::endl;
620 continue;
621 }
622
623 // Prefer GPUs with larger max texture sizes.
624 score += properties.limits.maxImageDimension2D;
625
626 if (selected_score < score) {
627 std::cout << " - This is the best device so far. Score: 0x" << std::hex
628 << score << std::dec << std::endl;
629
630 selected_score = score;
631 g_state.physical_device = pdevice;
632 g_state.enabled_device_extensions = supported_extensions;
633 g_state.queue_family_index = graphics_queue_family.value_or(
634 std::numeric_limits<uint32_t>::max());
635 }
636 }
637
638 if (g_state.physical_device == nullptr) {
639 std::cerr << "Failed to find a compatible Vulkan physical device."
640 << std::endl;
641 return EXIT_FAILURE;
642 }
643 }
644
645 /// --------------------------------------------------------------------------
646 /// Create a logical device and a graphics queue handle.
647 /// --------------------------------------------------------------------------
648
649 std::cout << "Enabling " << g_state.enabled_device_extensions.size()
650 << " device extensions:" << std::endl;
651 for (const char* extension : g_state.enabled_device_extensions) {
652 std::cout << " - " << extension << std::endl;
653 }
654
655 {
656 VkPhysicalDeviceFeatures device_features = {};
657
658 VkDeviceQueueCreateInfo graphics_queue = {};
660 graphics_queue.queueFamilyIndex = g_state.queue_family_index;
661 graphics_queue.queueCount = 1;
662 float priority = 1.0f;
663 graphics_queue.pQueuePriorities = &priority;
664
665 VkDeviceCreateInfo device_info = {};
667 device_info.enabledExtensionCount =
668 g_state.enabled_device_extensions.size();
669 device_info.ppEnabledExtensionNames =
670 g_state.enabled_device_extensions.data();
671 device_info.pEnabledFeatures = &device_features;
672 device_info.queueCreateInfoCount = 1;
673 device_info.pQueueCreateInfos = &graphics_queue;
674
675 if (d.vkCreateDevice(g_state.physical_device, &device_info, nullptr,
676 &g_state.device) != VK_SUCCESS) {
677 std::cerr << "Failed to create Vulkan logical device." << std::endl;
678 return EXIT_FAILURE;
679 }
680 }
681
682 d.vkGetDeviceQueue(g_state.device, g_state.queue_family_index, 0,
683 &g_state.queue);
684
685 /// --------------------------------------------------------------------------
686 /// Create sync primitives and command pool to use in the render loop
687 /// callbacks.
688 /// --------------------------------------------------------------------------
689
690 {
692 d.vkCreateFence(g_state.device, &f_info, nullptr,
693 &g_state.image_ready_fence);
694
695 VkSemaphoreCreateInfo s_info = {
697 d.vkCreateSemaphore(g_state.device, &s_info, nullptr,
698 &g_state.present_transition_semaphore);
699
700 VkCommandPoolCreateInfo pool_info = {
702 .queueFamilyIndex = g_state.queue_family_index,
703 };
704 d.vkCreateCommandPool(g_state.device, &pool_info, nullptr,
705 &g_state.swapchain_command_pool);
706 }
707
708 /// --------------------------------------------------------------------------
709 /// Create swapchain.
710 /// --------------------------------------------------------------------------
711
712 if (!InitializeSwapchain()) {
713 std::cerr << "Failed to create swapchain." << std::endl;
714 return EXIT_FAILURE;
715 }
716
717 /// --------------------------------------------------------------------------
718 /// Start Flutter Engine.
719 /// --------------------------------------------------------------------------
720
721 {
722 FlutterRendererConfig config = {};
723 config.type = kVulkan;
724 config.vulkan.struct_size = sizeof(config.vulkan);
725 config.vulkan.version = VK_MAKE_VERSION(1, 1, 0);
726 config.vulkan.instance = g_state.instance;
727 config.vulkan.physical_device = g_state.physical_device;
728 config.vulkan.device = g_state.device;
729 config.vulkan.queue_family_index = g_state.queue_family_index;
730 config.vulkan.queue = g_state.queue;
732 g_state.enabled_instance_extensions.size();
734 g_state.enabled_instance_extensions.data();
736 g_state.enabled_device_extensions.size();
738 g_state.enabled_device_extensions.data();
743
744 // This directory is generated by `flutter build bundle`.
745 std::string assets_path = project_path + "/build/flutter_assets";
748 .assets_path = assets_path.c_str(),
749 .icu_data_path =
750 icudtl_path.c_str(), // Find this in your bin/cache directory.
751 };
754 &g_state.engine);
755 if (result != kSuccess || g_state.engine == nullptr) {
756 std::cerr << "Failed to start Flutter Engine." << std::endl;
757 return EXIT_FAILURE;
758 }
759
760 // Trigger a FlutterEngineSendWindowMetricsEvent to communicate the initial
761 // size of the window.
762 int width, height;
763 glfwGetFramebufferSize(g_state.window, &width, &height);
765 g_state.resize_pending = false;
766 }
767
768 /// --------------------------------------------------------------------------
769 /// GLFW render loop.
770 /// --------------------------------------------------------------------------
771
772 glfwSetKeyCallback(g_state.window, GLFWKeyCallback);
773 glfwSetFramebufferSizeCallback(g_state.window, GLFWframebufferSizeCallback);
774 glfwSetMouseButtonCallback(g_state.window, GLFWmouseButtonCallback);
775
776 while (!glfwWindowShouldClose(g_state.window)) {
777 glfwWaitEvents();
778 }
779
780 /// --------------------------------------------------------------------------
781 /// Cleanup.
782 /// --------------------------------------------------------------------------
783
784 if (FlutterEngineShutdown(g_state.engine) != kSuccess) {
785 std::cerr << "Flutter Engine shutdown failed." << std::endl;
786 }
787
788 d.vkDestroyCommandPool(g_state.device, g_state.swapchain_command_pool,
789 nullptr);
790 d.vkDestroySemaphore(g_state.device, g_state.present_transition_semaphore,
791 nullptr);
792 d.vkDestroyFence(g_state.device, g_state.image_ready_fence, nullptr);
793
794 d.vkDestroyDevice(g_state.device, nullptr);
795 d.vkDestroySurfaceKHR(g_state.instance, g_state.surface, nullptr);
796 d.vkDestroyInstance(g_state.instance, nullptr);
797
798 glfwDestroyWindow(g_state.window);
799 glfwTerminate();
800
801 return 0;
802}
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:1641
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:2129
@ 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:327
bool FlutterPresentCallback(void *user_data, const FlutterVulkanImage *image)
Definition main.cc:355
void PrintUsage()
Definition main.cc:136
void GLFWframebufferSizeCallback(GLFWwindow *window, int width, int height)
Definition main.cc:125
std::vector< const char * > enabled_device_extensions
Definition main.cc:51
static const bool g_enable_validation_layers
Definition main.cc:26
std::vector< const char * > enabled_instance_extensions
Definition main.cc:46
static const size_t kInitialWindowHeight
Definition main.cc:30
void * FlutterGetInstanceProcAddressCallback(void *user_data, FlutterVulkanInstanceHandle instance, const char *procname)
Definition main.cc:391
static const size_t kInitialWindowWidth
Definition main.cc:29
void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
Definition main.cc:115
void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, int mods)
Definition main.cc:96
void GLFW_ErrorCallback(int error, const char *description)
Definition main.cc:72
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:1985
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 136 of file main.cc.

136 {
137 std::cerr
138 << "usage: embedder_example_vulkan <path to project> <path to icudtl.dat>"
139 << std::endl;
140}

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 52 of file main.cc.

◆ enabled_device_extensions

std::vector<const char*> enabled_device_extensions

Definition at line 51 of file main.cc.

◆ enabled_instance_extensions

std::vector<const char*> enabled_instance_extensions

Definition at line 46 of file main.cc.

◆ engine

self engine = engine

Definition at line 67 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 59 of file main.cc.

◆ instance

VkInstance instance

Definition at line 47 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 65 of file main.cc.

◆ physical_device

VkPhysicalDevice physical_device

Definition at line 50 of file main.cc.

◆ present_transition_buffers

std::vector<VkCommandBuffer> present_transition_buffers

Definition at line 57 of file main.cc.

◆ present_transition_semaphore

VkSemaphore present_transition_semaphore

Definition at line 60 of file main.cc.

◆ queue

VkQueue queue

Definition at line 54 of file main.cc.

◆ queue_family_index

uint32_t queue_family_index

Definition at line 53 of file main.cc.

◆ resize_pending

bool resize_pending = false

Definition at line 69 of file main.cc.

◆ surface

VkSurfaceKHR surface

Definition at line 48 of file main.cc.

◆ surface_format

VkSurfaceFormatKHR surface_format

Definition at line 62 of file main.cc.

◆ swapchain

VkSwapchainKHR swapchain

Definition at line 63 of file main.cc.

◆ swapchain_command_pool

VkCommandPool swapchain_command_pool

Definition at line 56 of file main.cc.

◆ swapchain_images

std::vector<VkImage> swapchain_images

Definition at line 64 of file main.cc.

◆ window

GLFWwindow* window

Definition at line 44 of file main.cc.