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"
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 
42  FlCompositor parent_instance;
43 
44  // Task runner to wait for frames on.
45  FlTaskRunner* task_runner;
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
61 
62  // true if frame was completed; resizing is not synchronized until first frame
63  // was rendered
65 
66  // Shader program.
67  GLuint program;
68 
69  // Location of layer offset in [program].
71 
72  // Location of layer scale in [program].
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 
82 G_DEFINE_TYPE(FlCompositorOpenGL,
83  fl_compositor_opengl,
84  fl_compositor_get_type())
85 
86 // Returns the log for the given OpenGL shader. Must be freed by the caller.
87 static gchar* get_shader_log(GLuint shader) {
88  GLint log_length;
89  gchar* log;
90 
91  glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
92 
93  log = static_cast<gchar*>(g_malloc(log_length + 1));
94  glGetShaderInfoLog(shader, log_length, nullptr, log);
95 
96  return log;
97 }
98 
99 // Returns the log for the given OpenGL program. Must be freed by the caller.
100 static gchar* get_program_log(GLuint program) {
101  GLint log_length;
102  gchar* log;
103 
104  glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
105 
106  log = static_cast<gchar*>(g_malloc(log_length + 1));
107  glGetProgramInfoLog(program, log_length, nullptr, log);
108 
109  return log;
110 }
111 
112 static void setup_shader(FlCompositorOpenGL* self) {
113  if (!fl_opengl_manager_make_current(self->opengl_manager)) {
114  g_warning(
115  "Failed to setup compositor shaders, unable to make OpenGL context "
116  "current");
117  return;
118  }
119 
120  GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
121  glShaderSource(vertex_shader, 1, &vertex_shader_src, nullptr);
122  glCompileShader(vertex_shader);
123  GLint vertex_compile_status;
124  glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &vertex_compile_status);
125  if (vertex_compile_status == GL_FALSE) {
126  g_autofree gchar* shader_log = get_shader_log(vertex_shader);
127  g_warning("Failed to compile vertex shader: %s", shader_log);
128  }
129 
130  GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
131  glShaderSource(fragment_shader, 1, &fragment_shader_src, nullptr);
132  glCompileShader(fragment_shader);
133  GLint fragment_compile_status;
134  glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &fragment_compile_status);
135  if (fragment_compile_status == GL_FALSE) {
136  g_autofree gchar* shader_log = get_shader_log(fragment_shader);
137  g_warning("Failed to compile fragment shader: %s", shader_log);
138  }
139 
140  self->program = glCreateProgram();
141  glAttachShader(self->program, vertex_shader);
142  glAttachShader(self->program, fragment_shader);
143  glLinkProgram(self->program);
144 
145  GLint link_status;
146  glGetProgramiv(self->program, GL_LINK_STATUS, &link_status);
147  if (link_status == GL_FALSE) {
148  g_autofree gchar* program_log = get_program_log(self->program);
149  g_warning("Failed to link program: %s", program_log);
150  }
151 
152  self->offset_location = glGetUniformLocation(self->program, "offset");
153  self->scale_location = glGetUniformLocation(self->program, "scale");
154 
155  glDeleteShader(vertex_shader);
156  glDeleteShader(fragment_shader);
157 
158  // The uniform square abcd in two triangles cba + cdb
159  // a--b
160  // | |
161  // c--d
162  GLfloat vertex_data[] = {-1, -1, 0, 0, 1, 1, 1, 1, -1, 1, 0, 1,
163  -1, -1, 0, 0, 1, -1, 1, 0, 1, 1, 1, 1};
164 
165  glGenBuffers(1, &self->vertex_buffer);
166  glBindBuffer(GL_ARRAY_BUFFER, self->vertex_buffer);
167  glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data,
168  GL_STATIC_DRAW);
169 }
170 
171 static void cleanup_shader(FlCompositorOpenGL* self) {
172  if (!fl_opengl_manager_make_current(self->opengl_manager)) {
173  g_warning(
174  "Failed to cleanup compositor shaders, unable to make OpenGL context "
175  "current");
176  return;
177  }
178 
179  if (self->program != 0) {
180  glDeleteProgram(self->program);
181  }
182  if (self->vertex_buffer != 0) {
183  glDeleteBuffers(1, &self->vertex_buffer);
184  }
185 }
186 
187 static void composite_layer(FlCompositorOpenGL* self,
188  FlFramebuffer* framebuffer,
189  double x,
190  double y,
191  int width,
192  int height) {
193  size_t texture_width = fl_framebuffer_get_width(framebuffer);
194  size_t texture_height = fl_framebuffer_get_height(framebuffer);
195  glUniform2f(self->offset_location, (2 * x / width) - 1.0,
196  (2 * y / width) - 1.0);
197  glUniform2f(self->scale_location, texture_width / width,
198  texture_height / height);
199 
200  GLuint texture_id = fl_framebuffer_get_texture_id(framebuffer);
201  glBindTexture(GL_TEXTURE_2D, texture_id);
202 
203  glDrawArrays(GL_TRIANGLES, 0, 6);
204 }
205 
206 static gboolean fl_compositor_opengl_present_layers(FlCompositor* compositor,
207  const FlutterLayer** layers,
208  size_t layers_count) {
209  FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(compositor);
210 
211  g_mutex_lock(&self->frame_mutex);
212  if (layers_count == 0) {
213  g_mutex_unlock(&self->frame_mutex);
214  return TRUE;
215  }
216 
217  GLint general_format = GL_RGBA;
218  if (epoxy_has_gl_extension("GL_EXT_texture_format_BGRA8888")) {
219  general_format = GL_BGRA_EXT;
220  }
221 
222  // Save bindings that are set by this function. All bindings must be restored
223  // to their original values because Skia expects that its bindings have not
224  // been altered.
225  GLint saved_texture_binding;
226  glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding);
227  GLint saved_vao_binding;
228  glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao_binding);
229  GLint saved_array_buffer_binding;
230  glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &saved_array_buffer_binding);
231  GLint saved_draw_framebuffer_binding;
232  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &saved_draw_framebuffer_binding);
233  GLint saved_read_framebuffer_binding;
234  glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &saved_read_framebuffer_binding);
235  GLint saved_current_program;
236  glGetIntegerv(GL_CURRENT_PROGRAM, &saved_current_program);
237  GLboolean saved_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
238  GLboolean saved_blend = glIsEnabled(GL_BLEND);
239  GLint saved_src_rgb;
240  glGetIntegerv(GL_BLEND_SRC_RGB, &saved_src_rgb);
241  GLint saved_src_alpha;
242  glGetIntegerv(GL_BLEND_SRC_ALPHA, &saved_src_alpha);
243  GLint saved_dst_rgb;
244  glGetIntegerv(GL_BLEND_DST_RGB, &saved_dst_rgb);
245  GLint saved_dst_alpha;
246  glGetIntegerv(GL_BLEND_DST_ALPHA, &saved_dst_alpha);
247 
248  // Update framebuffer to write into.
249  size_t width = layers[0]->size.width;
250  size_t height = layers[0]->size.height;
251  if (self->framebuffer == nullptr ||
252  fl_framebuffer_get_width(self->framebuffer) != width ||
253  fl_framebuffer_get_height(self->framebuffer) != height) {
254  g_clear_object(&self->framebuffer);
255  self->framebuffer =
256  fl_framebuffer_new(general_format, width, height, self->shareable);
257 
258  // If not shareable make buffer to copy frame pixels into.
259  if (!self->shareable) {
260  size_t data_length = width * height * 4;
261  self->pixels = static_cast<uint8_t*>(realloc(self->pixels, data_length));
262  }
263  }
264 
265  self->had_first_frame = true;
266 
267  // FIXME(robert-ancell): The vertex array is the same for all views, but
268  // cannot be shared in OpenGL. Find a way to not generate this every time.
269  GLuint vao;
270  glGenVertexArrays(1, &vao);
271  glBindVertexArray(vao);
272  glBindBuffer(GL_ARRAY_BUFFER, self->vertex_buffer);
273  GLint position_location = glGetAttribLocation(self->program, "position");
274  glEnableVertexAttribArray(position_location);
275  glVertexAttribPointer(position_location, 2, GL_FLOAT, GL_FALSE,
276  sizeof(GLfloat) * 4, 0);
277  GLint texcoord_location = glGetAttribLocation(self->program, "in_texcoord");
278  glEnableVertexAttribArray(texcoord_location);
279  glVertexAttribPointer(texcoord_location, 2, GL_FLOAT, GL_FALSE,
280  sizeof(GLfloat) * 4,
281  reinterpret_cast<void*>(sizeof(GLfloat) * 2));
282 
283  glEnable(GL_BLEND);
284  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
285 
286  glUseProgram(self->program);
287 
288  // Disable the scissor test as it can affect blit operations.
289  // Prevents regressions like: https://github.com/flutter/flutter/issues/140828
290  // See OpenGL specification version 4.6, section 18.3.1.
291  glDisable(GL_SCISSOR_TEST);
292 
293  glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
294  fl_framebuffer_get_id(self->framebuffer));
295  gboolean first_layer = TRUE;
296  for (size_t i = 0; i < layers_count; ++i) {
297  const FlutterLayer* layer = layers[i];
298  switch (layer->type) {
299  case kFlutterLayerContentTypeBackingStore: {
300  const FlutterBackingStore* backing_store = layer->backing_store;
301  FlFramebuffer* framebuffer =
302  FL_FRAMEBUFFER(backing_store->open_gl.framebuffer.user_data);
303  glBindFramebuffer(GL_READ_FRAMEBUFFER,
304  fl_framebuffer_get_id(framebuffer));
305  // The first layer can be blitted, and following layers composited with
306  // this.
307  if (first_layer) {
308  glBlitFramebuffer(layer->offset.x, layer->offset.y, layer->size.width,
309  layer->size.height, layer->offset.x,
310  layer->offset.y, layer->size.width,
311  layer->size.height, GL_COLOR_BUFFER_BIT,
312  GL_NEAREST);
313  first_layer = FALSE;
314  } else {
315  composite_layer(self, framebuffer, layer->offset.x, layer->offset.y,
316  width, height);
317  }
318  } break;
319  case kFlutterLayerContentTypePlatformView: {
320  // TODO(robert-ancell) Not implemented -
321  // https://github.com/flutter/flutter/issues/41724
322  } break;
323  }
324  }
325  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
326  glFlush();
327 
328  glDeleteVertexArrays(1, &vao);
329 
330  if (saved_blend) {
331  glEnable(GL_BLEND);
332  } else {
333  glDisable(GL_BLEND);
334  }
335  if (saved_scissor_test) {
336  glEnable(GL_SCISSOR_TEST);
337  } else {
338  glDisable(GL_SCISSOR_TEST);
339  }
340 
341  glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
342  glBindVertexArray(saved_vao_binding);
343  glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding);
344  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, saved_draw_framebuffer_binding);
345  glUseProgram(saved_current_program);
346  glBlendFuncSeparate(saved_src_rgb, saved_dst_rgb, saved_src_alpha,
347  saved_dst_alpha);
348 
349  if (!self->shareable) {
350  glBindFramebuffer(GL_READ_FRAMEBUFFER,
351  fl_framebuffer_get_id(self->framebuffer));
352  glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, self->pixels);
353  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
354  }
355  glBindFramebuffer(GL_READ_FRAMEBUFFER, saved_read_framebuffer_binding);
356 
357  g_mutex_unlock(&self->frame_mutex);
358 
359  fl_task_runner_stop_wait(self->task_runner);
360 
361  return TRUE;
362 }
363 
364 static gboolean fl_compositor_opengl_render(FlCompositor* compositor,
365  cairo_t* cr,
366  GdkWindow* window) {
367  FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(compositor);
368 
369  g_mutex_lock(&self->frame_mutex);
370  if (self->framebuffer == nullptr) {
371  g_mutex_unlock(&self->frame_mutex);
372  return FALSE;
373  }
374 
375  // If frame not ready, then wait for it.
376  gint scale_factor = gdk_window_get_scale_factor(window);
377  size_t width = gdk_window_get_width(window) * scale_factor;
378  size_t height = gdk_window_get_height(window) * scale_factor;
379  while (fl_framebuffer_get_width(self->framebuffer) != width ||
380  fl_framebuffer_get_height(self->framebuffer) != height) {
381  g_mutex_unlock(&self->frame_mutex);
382  fl_task_runner_wait(self->task_runner);
383  g_mutex_lock(&self->frame_mutex);
384  }
385 
386  if (fl_framebuffer_get_shareable(self->framebuffer)) {
387  g_autoptr(FlFramebuffer) sibling =
388  fl_framebuffer_create_sibling(self->framebuffer);
389  gdk_cairo_draw_from_gl(cr, window, fl_framebuffer_get_texture_id(sibling),
390  GL_TEXTURE, scale_factor, 0, 0, width, height);
391  } else {
392  GLint saved_texture_binding;
393  glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding);
394 
395  GLuint texture_id;
396  glGenTextures(1, &texture_id);
397  glBindTexture(GL_TEXTURE_2D, texture_id);
398  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
399  GL_UNSIGNED_BYTE, self->pixels);
400 
401  gdk_cairo_draw_from_gl(cr, window, texture_id, GL_TEXTURE, scale_factor, 0,
402  0, width, height);
403 
404  glDeleteTextures(1, &texture_id);
405 
406  glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
407  }
408 
409  glFlush();
410 
411  g_mutex_unlock(&self->frame_mutex);
412 
413  return TRUE;
414 }
415 
416 static void fl_compositor_opengl_dispose(GObject* object) {
417  FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(object);
418 
419  cleanup_shader(self);
420 
421  g_clear_object(&self->task_runner);
422  g_clear_object(&self->opengl_manager);
423  g_clear_object(&self->framebuffer);
424  g_clear_pointer(&self->pixels, g_free);
425  g_mutex_clear(&self->frame_mutex);
426 
427  G_OBJECT_CLASS(fl_compositor_opengl_parent_class)->dispose(object);
428 }
429 
430 static void fl_compositor_opengl_class_init(FlCompositorOpenGLClass* klass) {
431  FL_COMPOSITOR_CLASS(klass)->present_layers =
433  FL_COMPOSITOR_CLASS(klass)->render = fl_compositor_opengl_render;
434 
435  G_OBJECT_CLASS(klass)->dispose = fl_compositor_opengl_dispose;
436 }
437 
438 static void fl_compositor_opengl_init(FlCompositorOpenGL* self) {
439  g_mutex_init(&self->frame_mutex);
440 }
441 
442 FlCompositorOpenGL* fl_compositor_opengl_new(FlTaskRunner* task_runner,
443  FlOpenGLManager* opengl_manager,
444  gboolean shareable) {
445  FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(
446  g_object_new(fl_compositor_opengl_get_type(), nullptr));
447 
448  self->task_runner = FL_TASK_RUNNER(g_object_ref(task_runner));
449  self->shareable = shareable;
450  self->opengl_manager = FL_OPENGL_MANAGER(g_object_ref(opengl_manager));
451 
452  setup_shader(self);
453 
454  return self;
455 }
return window
static void fl_compositor_opengl_dispose(GObject *object)
FlCompositorOpenGL * fl_compositor_opengl_new(FlTaskRunner *task_runner, FlOpenGLManager *opengl_manager, gboolean shareable)
static gboolean fl_compositor_opengl_present_layers(FlCompositor *compositor, const FlutterLayer **layers, size_t layers_count)
static const char * fragment_shader_src
static void fl_compositor_opengl_init(FlCompositorOpenGL *self)
static void cleanup_shader(FlCompositorOpenGL *self)
static gchar * get_program_log(GLuint program)
static void setup_shader(FlCompositorOpenGL *self)
G_DEFINE_TYPE(FlCompositorOpenGL, fl_compositor_opengl, fl_compositor_get_type()) static gchar *get_shader_log(GLuint shader)
static void fl_compositor_opengl_class_init(FlCompositorOpenGLClass *klass)
static void composite_layer(FlCompositorOpenGL *self, FlFramebuffer *framebuffer, double x, double y, int width, int height)
static const char * vertex_shader_src
static gboolean fl_compositor_opengl_render(FlCompositor *compositor, cairo_t *cr, GdkWindow *window)
G_BEGIN_DECLS FlOpenGLManager gboolean shareable
G_BEGIN_DECLS FlOpenGLManager * opengl_manager
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
self height
g_autoptr(GMutexLocker) locker
self width
return TRUE
fl_task_runner_stop_wait(self->task_runner)
size_t fl_framebuffer_get_height(FlFramebuffer *self)
GLuint fl_framebuffer_get_id(FlFramebuffer *self)
gboolean fl_framebuffer_get_shareable(FlFramebuffer *self)
size_t fl_framebuffer_get_width(FlFramebuffer *self)
GLuint fl_framebuffer_get_texture_id(FlFramebuffer *self)
FlFramebuffer * fl_framebuffer_create_sibling(FlFramebuffer *self)
FlFramebuffer * fl_framebuffer_new(GLint format, size_t width, size_t height, gboolean shareable)
gboolean fl_opengl_manager_make_current(FlOpenGLManager *self)
void fl_task_runner_wait(FlTaskRunner *self)
FlOpenGLManager * opengl_manager
FlFramebuffer * framebuffer
int64_t texture_id