| Flutter Linux Embedder |

fl_compositor_opengl.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 "fl_compositor_opengl.h"
6
7 #include <epoxy/egl.h>
8 #include <epoxy/gl.h>
9
10 #include "flutter/common/constants.h"
11 #include "flutter/shell/platform/embedder/embedder.h"
12 #include "flutter/shell/platform/linux/fl_engine_private.h"
13 #include "flutter/shell/platform/linux/fl_framebuffer.h"
14
15 // Vertex shader to draw Flutter window contents.
16 static const char* vertex_shader_src =
17 "attribute vec2 position;\n"
18 "attribute vec2 in_texcoord;\n"
19 "uniform vec2 offset;\n"
20 "uniform vec2 scale;\n"
21 "varying vec2 texcoord;\n"
22 "\n"
23 "void main() {\n"
24 " gl_Position = vec4(offset + position * scale, 0, 1);\n"
25 " texcoord = in_texcoord;\n"
26 "}\n";
27
28 // Fragment shader to draw Flutter window contents.
29 static const char* fragment_shader_src =
30 "#ifdef GL_ES\n"
31 "precision mediump float;\n"
32 "#endif\n"
33 "\n"
34 "uniform sampler2D texture;\n"
35 "varying vec2 texcoord;\n"
36 "\n"
37 "void main() {\n"
38 " gl_FragColor = texture2D(texture, texcoord);\n"
39 "}\n";
40
41 struct _FlCompositorOpenGL {
42 FlCompositor parent_instance;
43
44 // Engine we are rendering.
45 GWeakRef engine;
46
47 // TRUE if can share framebuffers between contexts.
48 gboolean shareable;
49
50 // Flutter OpenGL contexts.
51 FlOpenGLManager* opengl_manager;
52
53 // Last rendered frame.
54 FlFramebuffer* framebuffer;
55
56 // Last rendered frame pixels (only set if shareable is TRUE).
57 uint8_t* pixels;
58
59 // whether the renderer waits for frame render
60 bool blocking_main_thread;
61
62 // true if frame was completed; resizing is not synchronized until first frame
63 // was rendered
64 bool had_first_frame;
65
66 // Shader program.
67 GLuint program;
68
69 // Location of layer offset in [program].
70 GLuint offset_location;
71
72 // Location of layer scale in [program].
73 GLuint scale_location;
74
75 // Verticies for the uniform square.
76 GLuint vertex_buffer;
77
78 // Ensure Flutter and GTK can access the frame data (framebuffer or pixels).
79 GMutex frame_mutex;
80
81 // Allow GTK to wait for Flutter to generate a suitable frame.
82 GCond frame_cond;
83 };
84
85 G_DEFINE_TYPE(FlCompositorOpenGL,
86 fl_compositor_opengl,
87 fl_compositor_get_type())
88
89 // Returns the log for the given OpenGL shader. Must be freed by the caller.
90 static gchar* get_shader_log(GLuint shader) {
91 GLint log_length;
92 gchar* log;
93
94 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
95
96 log = static_cast<gchar*>(g_malloc(log_length + 1));
97 glGetShaderInfoLog(shader, log_length, nullptr, log);
98
99 return log;
100 }
101
102 // Returns the log for the given OpenGL program. Must be freed by the caller.
103 static gchar* get_program_log(GLuint program) {
104 GLint log_length;
105 gchar* log;
106
107 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
108
109 log = static_cast<gchar*>(g_malloc(log_length + 1));
110 glGetProgramInfoLog(program, log_length, nullptr, log);
111
112 return log;
113 }
114
115 static void setup_shader(FlCompositorOpenGL* self) {
116 fl_opengl_manager_make_current(self->opengl_manager);
117
118 GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
119 glShaderSource(vertex_shader, 1, &vertex_shader_src, nullptr);
120 glCompileShader(vertex_shader);
121 GLint vertex_compile_status;
122 glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &vertex_compile_status);
123 if (vertex_compile_status == GL_FALSE) {
124 g_autofree gchar* shader_log = get_shader_log(vertex_shader);
125 g_warning("Failed to compile vertex shader: %s", shader_log);
126 }
127
128 GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
129 glShaderSource(fragment_shader, 1, &fragment_shader_src, nullptr);
130 glCompileShader(fragment_shader);
131 GLint fragment_compile_status;
132 glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &fragment_compile_status);
133 if (fragment_compile_status == GL_FALSE) {
134 g_autofree gchar* shader_log = get_shader_log(fragment_shader);
135 g_warning("Failed to compile fragment shader: %s", shader_log);
136 }
137
138 self->program = glCreateProgram();
139 glAttachShader(self->program, vertex_shader);
140 glAttachShader(self->program, fragment_shader);
141 glLinkProgram(self->program);
142
143 GLint link_status;
144 glGetProgramiv(self->program, GL_LINK_STATUS, &link_status);
145 if (link_status == GL_FALSE) {
146 g_autofree gchar* program_log = get_program_log(self->program);
147 g_warning("Failed to link program: %s", program_log);
148 }
149
150 self->offset_location = glGetUniformLocation(self->program, "offset");
151 self->scale_location = glGetUniformLocation(self->program, "scale");
152
153 glDeleteShader(vertex_shader);
154 glDeleteShader(fragment_shader);
155
156 // The uniform square abcd in two triangles cba + cdb
157 // a--b
158 // | |
159 // c--d
160 GLfloat vertex_data[] = {-1, -1, 0, 0, 1, 1, 1, 1, -1, 1, 0, 1,
161 -1, -1, 0, 0, 1, -1, 1, 0, 1, 1, 1, 1};
162
163 glGenBuffers(1, &self->vertex_buffer);
164 glBindBuffer(GL_ARRAY_BUFFER, self->vertex_buffer);
165 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data,
166 GL_STATIC_DRAW);
167 }
168
169 static void cleanup_shader(FlCompositorOpenGL* self) {
170 fl_opengl_manager_make_current(self->opengl_manager);
171
172 if (self->program != 0) {
173 glDeleteProgram(self->program);
174 }
175 if (self->vertex_buffer != 0) {
176 glDeleteBuffers(1, &self->vertex_buffer);
177 }
178 }
179
180 static void composite_layer(FlCompositorOpenGL* self,
181 FlFramebuffer* framebuffer,
182 double x,
183 double y,
184 int width,
185 int height) {
186 size_t texture_width = fl_framebuffer_get_width(framebuffer);
187 size_t texture_height = fl_framebuffer_get_height(framebuffer);
188 glUniform2f(self->offset_location, (2 * x / width) - 1.0,
189 (2 * y / width) - 1.0);
190 glUniform2f(self->scale_location, texture_width / width,
191 texture_height / height);
192
193 GLuint texture_id = fl_framebuffer_get_texture_id(framebuffer);
194 glBindTexture(GL_TEXTURE_2D, texture_id);
195
196 glDrawArrays(GL_TRIANGLES, 0, 6);
197 }
198
199 static gboolean fl_compositor_opengl_present_layers(FlCompositor* compositor,
200 const FlutterLayer** layers,
201 size_t layers_count) {
202 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(compositor);
203
204 g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
205
206 g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
207
208 if (layers_count == 0) {
209 return TRUE;
210 }
211
212 GLint general_format = GL_RGBA;
213 if (epoxy_has_gl_extension("GL_EXT_texture_format_BGRA8888")) {
214 general_format = GL_BGRA_EXT;
215 }
216
217 // Save bindings that are set by this function. All bindings must be restored
218 // to their original values because Skia expects that its bindings have not
219 // been altered.
220 GLint saved_texture_binding;
221 glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding);
222 GLint saved_vao_binding;
223 glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao_binding);
224 GLint saved_array_buffer_binding;
225 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &saved_array_buffer_binding);
226 GLint saved_framebuffer_binding;
227 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &saved_framebuffer_binding);
228
229 // Update framebuffer to write into.
230 size_t width = layers[0]->size.width;
231 size_t height = layers[0]->size.height;
232 if (self->framebuffer == nullptr ||
233 fl_framebuffer_get_width(self->framebuffer) != width ||
234 fl_framebuffer_get_height(self->framebuffer) != height) {
235 g_clear_object(&self->framebuffer);
236 self->framebuffer =
237 fl_framebuffer_new(general_format, width, height, self->shareable);
238
239 // If not shareable make buffer to copy frame pixels into.
240 if (!self->shareable) {
241 size_t data_length = width * height * 4;
242 self->pixels = static_cast<uint8_t*>(realloc(self->pixels, data_length));
243 }
244 }
245
246 self->had_first_frame = true;
247
248 // FIXME(robert-ancell): The vertex array is the same for all views, but
249 // cannot be shared in OpenGL. Find a way to not generate this every time.
250 GLuint vao;
251 glGenVertexArrays(1, &vao);
252 glBindVertexArray(vao);
253 glBindBuffer(GL_ARRAY_BUFFER, self->vertex_buffer);
254 GLint position_location = glGetAttribLocation(self->program, "position");
255 glEnableVertexAttribArray(position_location);
256 glVertexAttribPointer(position_location, 2, GL_FLOAT, GL_FALSE,
257 sizeof(GLfloat) * 4, 0);
258 GLint texcoord_location = glGetAttribLocation(self->program, "in_texcoord");
259 glEnableVertexAttribArray(texcoord_location);
260 glVertexAttribPointer(texcoord_location, 2, GL_FLOAT, GL_FALSE,
261 sizeof(GLfloat) * 4,
262 reinterpret_cast<void*>(sizeof(GLfloat) * 2));
263
264 glEnable(GL_BLEND);
265 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
266
267 glUseProgram(self->program);
268
269 // Disable the scissor test as it can affect blit operations.
270 // Prevents regressions like: https://github.com/flutter/flutter/issues/140828
271 // See OpenGL specification version 4.6, section 18.3.1.
272 glDisable(GL_SCISSOR_TEST);
273
274 glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
275 fl_framebuffer_get_id(self->framebuffer));
276 gboolean first_layer = TRUE;
277 for (size_t i = 0; i < layers_count; ++i) {
278 const FlutterLayer* layer = layers[i];
279 switch (layer->type) {
280 case kFlutterLayerContentTypeBackingStore: {
281 const FlutterBackingStore* backing_store = layer->backing_store;
282 FlFramebuffer* framebuffer =
283 FL_FRAMEBUFFER(backing_store->open_gl.framebuffer.user_data);
284 glBindFramebuffer(GL_READ_FRAMEBUFFER,
285 fl_framebuffer_get_id(framebuffer));
286 // The first layer can be blitted, and following layers composited with
287 // this.
288 if (first_layer) {
289 glBlitFramebuffer(layer->offset.x, layer->offset.y, layer->size.width,
290 layer->size.height, layer->offset.x,
291 layer->offset.y, layer->size.width,
292 layer->size.height, GL_COLOR_BUFFER_BIT,
293 GL_NEAREST);
294 first_layer = FALSE;
295 } else {
296 composite_layer(self, framebuffer, layer->offset.x, layer->offset.y,
297 width, height);
298 }
299 } break;
300 case kFlutterLayerContentTypePlatformView: {
301 // TODO(robert-ancell) Not implemented -
302 // https://github.com/flutter/flutter/issues/41724
303 } break;
304 }
305 }
306 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
307 glFlush();
308
309 glDeleteVertexArrays(1, &vao);
310
311 glDisable(GL_BLEND);
312
313 glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
314 glBindVertexArray(saved_vao_binding);
315 glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding);
316 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, saved_framebuffer_binding);
317
318 if (!self->shareable) {
319 glBindFramebuffer(GL_READ_FRAMEBUFFER,
320 fl_framebuffer_get_id(self->framebuffer));
321 glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, self->pixels);
322 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
323 }
324
325 // Signal a frame is ready.
326 g_cond_signal(&self->frame_cond);
327
328 return TRUE;
329 }
330
331 static gboolean fl_compositor_opengl_render(FlCompositor* compositor,
332 cairo_t* cr,
333 GdkWindow* window) {
334 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(compositor);
335
336 g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
337
338 if (self->framebuffer == nullptr) {
339 return FALSE;
340 }
341
342 // If frame not ready, then wait for it.
343 gint scale_factor = gdk_window_get_scale_factor(window);
344 size_t width = gdk_window_get_width(window) * scale_factor;
345 size_t height = gdk_window_get_height(window) * scale_factor;
346 while (fl_framebuffer_get_width(self->framebuffer) != width ||
347 fl_framebuffer_get_height(self->framebuffer) != height) {
348 g_cond_wait(&self->frame_cond, &self->frame_mutex);
349 }
350
351 if (fl_framebuffer_get_shareable(self->framebuffer)) {
352 g_autoptr(FlFramebuffer) sibling =
353 fl_framebuffer_create_sibling(self->framebuffer);
354 gdk_cairo_draw_from_gl(cr, window, fl_framebuffer_get_texture_id(sibling),
355 GL_TEXTURE, scale_factor, 0, 0, width, height);
356 } else {
357 GLuint texture_id;
358 glGenTextures(1, &texture_id);
359 glBindTexture(GL_TEXTURE_2D, texture_id);
360 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
361 GL_UNSIGNED_BYTE, self->pixels);
362
363 gdk_cairo_draw_from_gl(cr, window, texture_id, GL_TEXTURE, scale_factor, 0,
364 0, width, height);
365
366 glDeleteTextures(1, &texture_id);
367 }
368
369 glFlush();
370
371 return TRUE;
372 }
373
374 static void fl_compositor_opengl_dispose(GObject* object) {
375 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(object);
376
377 cleanup_shader(self);
378
379 g_weak_ref_clear(&self->engine);
380 g_clear_object(&self->opengl_manager);
381 g_clear_object(&self->framebuffer);
382 g_clear_pointer(&self->pixels, g_free);
383 g_mutex_clear(&self->frame_mutex);
384 g_cond_clear(&self->frame_cond);
385
386 G_OBJECT_CLASS(fl_compositor_opengl_parent_class)->dispose(object);
387 }
388
389 static void fl_compositor_opengl_class_init(FlCompositorOpenGLClass* klass) {
390 FL_COMPOSITOR_CLASS(klass)->present_layers =
391 fl_compositor_opengl_present_layers;
392 FL_COMPOSITOR_CLASS(klass)->render = fl_compositor_opengl_render;
393
394 G_OBJECT_CLASS(klass)->dispose = fl_compositor_opengl_dispose;
395 }
396
397 static void fl_compositor_opengl_init(FlCompositorOpenGL* self) {
398 g_mutex_init(&self->frame_mutex);
399 g_cond_init(&self->frame_cond);
400 }
401
402 FlCompositorOpenGL* fl_compositor_opengl_new(FlEngine* engine,
403 gboolean shareable) {
404 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(
405 g_object_new(fl_compositor_opengl_get_type(), nullptr));
406
407 g_weak_ref_init(&self->engine, engine);
408 self->shareable = shareable;
409 self->opengl_manager =
410 FL_OPENGL_MANAGER(g_object_ref(fl_engine_get_opengl_manager(engine)));
411
412 setup_shader(self);
413
414 return self;
415 }
window
return window
Definition: fl_application.cc:41
fl_compositor_opengl_dispose
static void fl_compositor_opengl_dispose(GObject *object)
Definition: fl_compositor_opengl.cc:374
fl_compositor_opengl_present_layers
static gboolean fl_compositor_opengl_present_layers(FlCompositor *compositor, const FlutterLayer **layers, size_t layers_count)
Definition: fl_compositor_opengl.cc:199
fragment_shader_src
static const char * fragment_shader_src
Definition: fl_compositor_opengl.cc:29
fl_compositor_opengl_new
FlCompositorOpenGL * fl_compositor_opengl_new(FlEngine *engine, gboolean shareable)
Definition: fl_compositor_opengl.cc:402
fl_compositor_opengl_init
static void fl_compositor_opengl_init(FlCompositorOpenGL *self)
Definition: fl_compositor_opengl.cc:397
cleanup_shader
static void cleanup_shader(FlCompositorOpenGL *self)
Definition: fl_compositor_opengl.cc:169
get_program_log
static gchar * get_program_log(GLuint program)
Definition: fl_compositor_opengl.cc:103
setup_shader
static void setup_shader(FlCompositorOpenGL *self)
Definition: fl_compositor_opengl.cc:115
G_DEFINE_TYPE
G_DEFINE_TYPE(FlCompositorOpenGL, fl_compositor_opengl, fl_compositor_get_type()) static gchar *get_shader_log(GLuint shader)
Definition: fl_compositor_opengl.cc:85
fl_compositor_opengl_class_init
static void fl_compositor_opengl_class_init(FlCompositorOpenGLClass *klass)
Definition: fl_compositor_opengl.cc:389
composite_layer
static void composite_layer(FlCompositorOpenGL *self, FlFramebuffer *framebuffer, double x, double y, int width, int height)
Definition: fl_compositor_opengl.cc:180
vertex_shader_src
static const char * vertex_shader_src
Definition: fl_compositor_opengl.cc:16
fl_compositor_opengl_render
static gboolean fl_compositor_opengl_render(FlCompositor *compositor, cairo_t *cr, GdkWindow *window)
Definition: fl_compositor_opengl.cc:331
fl_compositor_opengl.h
shareable
G_BEGIN_DECLS gboolean shareable
Definition: fl_compositor_opengl.h:40
layers_count
const FlutterLayer size_t layers_count
Definition: fl_compositor_software.cc:33
layers
const FlutterLayer ** layers
Definition: fl_compositor_software.cc:32
height
self height
Definition: fl_compositor_software.cc:43
g_autoptr
g_autoptr(GMutexLocker) locker
width
self width
Definition: fl_compositor_software.cc:42
TRUE
return TRUE
Definition: fl_compositor_software.cc:69
self
g_cond_signal & self
Definition: fl_compositor_software.cc:67
fl_engine_get_opengl_manager
FlOpenGLManager * fl_engine_get_opengl_manager(FlEngine *self)
Definition: fl_engine.cc:711
fl_engine_private.h
fl_framebuffer_get_height
size_t fl_framebuffer_get_height(FlFramebuffer *self)
Definition: fl_framebuffer.cc:160
fl_framebuffer_get_id
GLuint fl_framebuffer_get_id(FlFramebuffer *self)
Definition: fl_framebuffer.cc:148
fl_framebuffer_get_shareable
gboolean fl_framebuffer_get_shareable(FlFramebuffer *self)
Definition: fl_framebuffer.cc:115
fl_framebuffer_get_width
size_t fl_framebuffer_get_width(FlFramebuffer *self)
Definition: fl_framebuffer.cc:156
fl_framebuffer_get_texture_id
GLuint fl_framebuffer_get_texture_id(FlFramebuffer *self)
Definition: fl_framebuffer.cc:152
fl_framebuffer_create_sibling
FlFramebuffer * fl_framebuffer_create_sibling(FlFramebuffer *self)
Definition: fl_framebuffer.cc:120
fl_framebuffer_new
FlFramebuffer * fl_framebuffer_new(GLint format, size_t width, size_t height, gboolean shareable)
Definition: fl_framebuffer.cc:69
fl_framebuffer.h
fl_opengl_manager_make_current
void fl_opengl_manager_make_current(FlOpenGLManager *self)
Definition: fl_opengl_manager.cc:76
i
int i
Definition: fl_socket_accessible.cc:18
_FlCompositorOpenGL
Definition: fl_compositor_opengl.cc:41
_FlCompositorOpenGL::program
GLuint program
Definition: fl_compositor_opengl.cc:67
_FlCompositorOpenGL::pixels
uint8_t * pixels
Definition: fl_compositor_opengl.cc:57
_FlCompositorOpenGL::parent_instance
FlCompositor parent_instance
Definition: fl_compositor_opengl.cc:42
_FlCompositorOpenGL::engine
GWeakRef engine
Definition: fl_compositor_opengl.cc:45
_FlCompositorOpenGL::frame_cond
GCond frame_cond
Definition: fl_compositor_opengl.cc:82
_FlCompositorOpenGL::blocking_main_thread
bool blocking_main_thread
Definition: fl_compositor_opengl.cc:60
_FlCompositorOpenGL::offset_location
GLuint offset_location
Definition: fl_compositor_opengl.cc:70
_FlCompositorOpenGL::opengl_manager
FlOpenGLManager * opengl_manager
Definition: fl_compositor_opengl.cc:51
_FlCompositorOpenGL::scale_location
GLuint scale_location
Definition: fl_compositor_opengl.cc:73
_FlCompositorOpenGL::framebuffer
FlFramebuffer * framebuffer
Definition: fl_compositor_opengl.cc:54
_FlCompositorOpenGL::shareable
gboolean shareable
Definition: fl_compositor_opengl.cc:48
_FlCompositorOpenGL::vertex_buffer
GLuint vertex_buffer
Definition: fl_compositor_opengl.cc:76
_FlCompositorOpenGL::had_first_frame
bool had_first_frame
Definition: fl_compositor_opengl.cc:64
_FlCompositorOpenGL::frame_mutex
GMutex frame_mutex
Definition: fl_compositor_opengl.cc:79
texture_id
int64_t texture_id
Definition: texture_registrar_unittests.cc:24