Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
FlutterEmbedderGLFW.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <cassert>
6#include <chrono>
7#include <iostream>
8
9#define GLFW_EXPOSE_NATIVE_EGL
10#define GLFW_INCLUDE_GLEXT
11
12#include <EGL/egl.h>
13#include <EGL/eglext.h>
14#include <array>
15#include <cstring>
16#include <list>
17#include <unordered_map>
18#include "GLFW/glfw3.h"
19#include "GLFW/glfw3native.h"
20#include "embedder.h"
21
22// This value is calculated after the window is created.
23static double g_pixelRatio = 1.0;
24static const size_t kInitialWindowWidth = 800;
25static const size_t kInitialWindowHeight = 600;
26// Maximum damage history - for triple buffering we need to store damage for
27// last two frames; Some Android devices (Pixel 4) use quad buffering.
28static const int kMaxHistorySize = 10;
29static constexpr FlutterViewId kImplicitViewId = 0;
30
31// Keeps track of the most recent frame damages so that existing damage can
32// be easily computed.
33std::list<FlutterRect> damage_history_;
34
35// Keeps track of the existing damage associated with each FBO ID
36std::unordered_map<intptr_t, FlutterRect*> existing_damage_map_;
37
38EGLDisplay display_;
39EGLSurface surface_;
40
41static_assert(FLUTTER_ENGINE_VERSION == 1,
42 "This Flutter Embedder was authored against the stable Flutter "
43 "API at version 1. There has been a serious breakage in the "
44 "API. Please read the ChangeLog and take appropriate action "
45 "before updating this assertion");
46
49 double x,
50 double y) {
51 FlutterPointerEvent event = {};
52 event.struct_size = sizeof(event);
53 event.phase = phase;
54 event.x = x * g_pixelRatio;
55 event.y = y * g_pixelRatio;
56 event.timestamp =
57 std::chrono::duration_cast<std::chrono::microseconds>(
58 std::chrono::high_resolution_clock::now().time_since_epoch())
59 .count();
60 // This example only supports a single window, therefore we assume the pointer
61 // event occurred in the only view, the implicit view.
62 event.view_id = kImplicitViewId;
64 reinterpret_cast<FlutterEngine>(glfwGetWindowUserPointer(window)), &event,
65 1);
66}
67
68void GLFWcursorPositionCallback(GLFWwindow* window, double x, double y) {
70}
71
73 int key,
74 int action,
75 int mods) {
76 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) {
77 double x, y;
78 glfwGetCursorPos(window, &x, &y);
80 glfwSetCursorPosCallback(window, GLFWcursorPositionCallback);
81 }
82
83 if (key == GLFW_MOUSE_BUTTON_1 && action == GLFW_RELEASE) {
84 double x, y;
85 glfwGetCursorPos(window, &x, &y);
87 glfwSetCursorPosCallback(window, nullptr);
88 }
89}
90
91static void GLFWKeyCallback(GLFWwindow* window,
92 int key,
93 int scancode,
94 int action,
95 int mods) {
96 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
97 glfwSetWindowShouldClose(window, GLFW_TRUE);
98 }
99}
100
101void GLFWwindowSizeCallback(GLFWwindow* window, int width, int height) {
102 FlutterWindowMetricsEvent event = {};
103 event.struct_size = sizeof(event);
104 event.width = width * g_pixelRatio;
105 event.height = height * g_pixelRatio;
106 event.pixel_ratio = g_pixelRatio;
107 // This example only supports a single window, therefore we assume the event
108 // occurred in the only view, the implicit view.
109 event.view_id = kImplicitViewId;
111 reinterpret_cast<FlutterEngine>(glfwGetWindowUserPointer(window)),
112 &event);
113}
114
115// Auxiliary function used to transform a FlutterRect into the format that is
116// expected by the EGL functions (i.e. array of EGLint).
117static std::array<EGLint, 4> RectToInts(const FlutterRect rect) {
118 EGLint height;
119 eglQuerySurface(display_, surface_, EGL_HEIGHT, &height);
120
121 std::array<EGLint, 4> res{
122 static_cast<int>(rect.left), height - static_cast<int>(rect.bottom),
123 static_cast<int>(rect.right) - static_cast<int>(rect.left),
124 static_cast<int>(rect.bottom) - static_cast<int>(rect.top)};
125 return res;
126}
127
128// Auxiliary function to union the damage regions comprised by two FlutterRect
129// element. It saves the result of this join in the rect variable.
130static void JoinFlutterRect(FlutterRect* rect, FlutterRect additional_rect) {
131 rect->left = std::min(rect->left, additional_rect.left);
132 rect->top = std::min(rect->top, additional_rect.top);
133 rect->right = std::max(rect->right, additional_rect.right);
134 rect->bottom = std::max(rect->bottom, additional_rect.bottom);
135}
136
137// Auxiliary function used to check if the given list of extensions contains the
138// requested extension name.
139static bool HasExtension(const char* extensions, const char* name) {
140 const char* r = strstr(extensions, name);
141 auto len = strlen(name);
142 // check that the extension name is terminated by space or null terminator
143 return r != nullptr && (r[len] == ' ' || r[len] == 0);
144}
145
146bool RunFlutter(GLFWwindow* window,
147 const std::string& project_path,
148 const std::string& icudtl_path) {
149 FlutterRendererConfig config = {};
150 config.type = kOpenGL;
151 config.open_gl.struct_size = sizeof(config.open_gl);
152 config.open_gl.make_current = [](void* userdata) -> bool {
153 glfwMakeContextCurrent(static_cast<GLFWwindow*>(userdata));
154 return true;
155 };
156 config.open_gl.clear_current = [](void*) -> bool {
157 glfwMakeContextCurrent(nullptr); // is this even a thing?
158 return true;
159 };
161 [](void* userdata, const FlutterPresentInfo* info) -> bool {
162 // Free the existing damage that was allocated to this frame.
163 if (existing_damage_map_[info->fbo_id] != nullptr) {
164 free(existing_damage_map_[info->fbo_id]);
165 existing_damage_map_[info->fbo_id] = nullptr;
166 }
167
168 // Get list of extensions.
169 const char* extensions = eglQueryString(display_, EGL_EXTENSIONS);
170
171 // Retrieve the set damage region function.
172 PFNEGLSETDAMAGEREGIONKHRPROC set_damage_region_ = nullptr;
173 if (HasExtension(extensions, "EGL_KHR_partial_update")) {
174 set_damage_region_ = reinterpret_cast<PFNEGLSETDAMAGEREGIONKHRPROC>(
175 eglGetProcAddress("eglSetDamageRegionKHR"));
176 }
177
178 // Retrieve the swap buffers with damage function.
179 PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage_ = nullptr;
180 if (HasExtension(extensions, "EGL_EXT_swap_buffers_with_damage")) {
181 swap_buffers_with_damage_ =
182 reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(
183 eglGetProcAddress("eglSwapBuffersWithDamageEXT"));
184 } else if (HasExtension(extensions, "EGL_KHR_swap_buffers_with_damage")) {
185 swap_buffers_with_damage_ =
186 reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(
187 eglGetProcAddress("eglSwapBuffersWithDamageKHR"));
188 }
189
190 if (set_damage_region_) {
191 // Set the buffer damage as the damage region.
192 auto buffer_rects = RectToInts(info->buffer_damage.damage[0]);
193 set_damage_region_(display_, surface_, buffer_rects.data(), 1);
194 }
195
196 // Add frame damage to damage history
197 damage_history_.push_back(info->frame_damage.damage[0]);
198 if (damage_history_.size() > kMaxHistorySize) {
199 damage_history_.pop_front();
200 }
201
202 if (swap_buffers_with_damage_) {
203 // Swap buffers with frame damage.
204 auto frame_rects = RectToInts(info->frame_damage.damage[0]);
205 return swap_buffers_with_damage_(display_, surface_, frame_rects.data(),
206 1);
207 } else {
208 // If the required extensions for partial repaint were not provided, do
209 // full repaint.
210 return eglSwapBuffers(display_, surface_);
211 }
212 };
213 config.open_gl.fbo_callback = [](void*) -> uint32_t {
214 return 0; // FBO0
215 };
217 [](void* userdata, intptr_t fbo_id,
218 FlutterDamage* existing_damage) -> void {
219 // Given the FBO age, create existing damage region by joining all frame
220 // damages since FBO was last used
221 EGLint age;
222 if (glfwExtensionSupported("GL_EXT_buffer_age") == GLFW_TRUE) {
223 eglQuerySurface(display_, surface_, EGL_BUFFER_AGE_EXT, &age);
224 } else {
225 age = 4; // Virtually no driver should have a swapchain length > 4.
226 }
227
228 existing_damage->num_rects = 1;
229
230 // Allocate the array of rectangles for the existing damage.
231 existing_damage_map_[fbo_id] = static_cast<FlutterRect*>(
232 malloc(sizeof(FlutterRect) * existing_damage->num_rects));
233 existing_damage_map_[fbo_id][0] =
235 existing_damage->damage = existing_damage_map_[fbo_id];
236
237 if (age > 1) {
238 --age;
239 // join up to (age - 1) last rects from damage history
240 for (auto i = damage_history_.rbegin();
241 i != damage_history_.rend() && age > 0; ++i, --age) {
242 if (i == damage_history_.rbegin()) {
243 if (i != damage_history_.rend()) {
244 existing_damage->damage[0] = {i->left, i->top, i->right, i->bottom};
245 }
246 } else {
247 JoinFlutterRect(&(existing_damage->damage[0]), *i);
248 }
249 }
250 }
251 };
252 config.open_gl.gl_proc_resolver = [](void*, const char* name) -> void* {
253 return reinterpret_cast<void*>(glfwGetProcAddress(name));
254 };
255 config.open_gl.fbo_reset_after_present = true;
256
257 // This directory is generated by `flutter build bundle`.
258 std::string assets_path = project_path + "/build/flutter_assets";
260 .struct_size = sizeof(FlutterProjectArgs),
261 .assets_path = assets_path.c_str(),
262 .icu_data_path =
263 icudtl_path.c_str(), // Find this in your bin/cache directory.
264 };
265 FlutterEngine engine = nullptr;
267 FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, // renderer
268 &args, window, &engine);
269 if (result != kSuccess || engine == nullptr) {
270 std::cout << "Could not run the Flutter Engine." << std::endl;
271 return false;
272 }
273
274 glfwSetWindowUserPointer(window, engine);
276
277 return true;
278}
279
281 std::cout
282 << "usage: embedder_example_drm <path to project> <path to icudtl.dat>"
283 << std::endl;
284}
285
286void GLFW_ErrorCallback(int error, const char* description) {
287 std::cout << "GLFW Error: (" << error << ") " << description << std::endl;
288}
289
290int main(int argc, const char* argv[]) {
291 if (argc != 3) {
292 printUsage();
293 return 1;
294 }
295
296 std::string project_path = argv[1];
297 std::string icudtl_path = argv[2];
298
299 glfwSetErrorCallback(GLFW_ErrorCallback);
300
301 int result = glfwInit();
302 if (result != GLFW_TRUE) {
303 std::cout << "Could not initialize GLFW." << std::endl;
304 return EXIT_FAILURE;
305 }
306
307#if defined(__linux__)
308 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
309#endif
310
311 GLFWwindow* window = glfwCreateWindow(
312 kInitialWindowWidth, kInitialWindowHeight, "Flutter", NULL, NULL);
313 if (window == nullptr) {
314 std::cout << "Could not create GLFW window." << std::endl;
315 return EXIT_FAILURE;
316 }
317
318 int framebuffer_width, framebuffer_height;
319 glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
320 g_pixelRatio = framebuffer_width / kInitialWindowWidth;
321
322 // Get the display and surface variables.
323 display_ = glfwGetEGLDisplay();
324 surface_ = glfwGetEGLSurface(window);
325
326 bool run_result = RunFlutter(window, project_path, icudtl_path);
327 if (!run_result) {
328 std::cout << "Could not run the Flutter engine." << std::endl;
329 return EXIT_FAILURE;
330 }
331
332 glfwSetKeyCallback(window, GLFWKeyCallback);
333 glfwSetWindowSizeCallback(window, GLFWwindowSizeCallback);
334 glfwSetMouseButtonCallback(window, GLFWmouseButtonCallback);
335
336 while (!glfwWindowShouldClose(window)) {
337 glfwWaitEvents();
338 }
339
340 glfwDestroyWindow(window);
341 glfwTerminate();
342
343 return EXIT_SUCCESS;
344}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
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 FlutterEngineSendWindowMetricsEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent *flutter_metrics)
Definition: embedder.cc:2320
FlutterEngineResult FlutterEngineSendPointerEvent(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPointerEvent *pointers, size_t events_count)
Definition: embedder.cc:2429
@ kOpenGL
Definition: embedder.h:80
FlutterPointerPhase
The phase of the pointer event.
Definition: embedder.h:965
@ kUp
Definition: embedder.h:973
@ kDown
Definition: embedder.h:980
@ kMove
Definition: embedder.h:985
FlutterEngineResult
Definition: embedder.h:72
@ kSuccess
Definition: embedder.h:73
int64_t FlutterViewId
Definition: embedder.h:275
#define FLUTTER_ENGINE_VERSION
Definition: embedder.h:70
GLFWwindow * window
Definition: main.cc:45
FlutterEngine engine
Definition: main.cc:68
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent * event
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
#define GLFW_TRUE
Definition: flutter_glfw.cc:33
bool RunFlutter(GLFWwindow *window, const std::string &project_path, const std::string &icudtl_path)
void GLFWcursorPositionCallbackAtPhase(GLFWwindow *window, FlutterPointerPhase phase, double x, double y)
int main(int argc, const char *argv[])
void GLFWwindowSizeCallback(GLFWwindow *window, int width, int height)
void GLFWcursorPositionCallback(GLFWwindow *window, double x, double y)
void GLFWmouseButtonCallback(GLFWwindow *window, int key, int action, int mods)
void printUsage()
void GLFW_ErrorCallback(int error, const char *description)
static std::array< EGLint, 4 > RectToInts(const FlutterRect rect)
EGLDisplay display_
static const int kMaxHistorySize
std::unordered_map< intptr_t, FlutterRect * > existing_damage_map_
static void GLFWKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
static void JoinFlutterRect(FlutterRect *rect, FlutterRect additional_rect)
static double g_pixelRatio
static constexpr FlutterViewId kImplicitViewId
std::list< FlutterRect > damage_history_
static bool HasExtension(const char *extensions, const char *name)
EGLSurface surface_
static const size_t kInitialWindowHeight
static const size_t kInitialWindowWidth
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
char ** argv
Definition: library.h:9
double y
double x
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
void * malloc(size_t size)
Definition: allocation.cc:19
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
int32_t height
int32_t width
A structure to represent a damage region.
Definition: embedder.h:460
ProcResolver gl_proc_resolver
Definition: embedder.h:554
BoolCallback make_current
Definition: embedder.h:516
BoolPresentInfoCallback present_with_info
Definition: embedder.h:578
UIntCallback fbo_callback
Definition: embedder.h:530
size_t struct_size
The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig).
Definition: embedder.h:515
FlutterFrameBufferWithDamageCallback populate_existing_damage
Definition: embedder.h:592
BoolCallback clear_current
Definition: embedder.h:517
size_t struct_size
The size of this struct. Must be sizeof(FlutterPointerEvent).
Definition: embedder.h:1036
A structure to represent a rectangle.
Definition: embedder.h:437
double bottom
Definition: embedder.h:441
double top
Definition: embedder.h:439
double left
Definition: embedder.h:438
double right
Definition: embedder.h:440
FlutterOpenGLRendererConfig open_gl
Definition: embedder.h:829
FlutterRendererType type
Definition: embedder.h:827
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition: embedder.h:843