Flutter Linux Embedder
fl_compositor_opengl.cc File Reference
#include "fl_compositor_opengl.h"
#include <epoxy/egl.h>
#include <epoxy/gl.h>
#include "flutter/common/constants.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/fl_framebuffer.h"

Go to the source code of this file.

Classes

struct  _FlCompositorOpenGL
 

Functions

 G_DEFINE_TYPE (FlCompositorOpenGL, fl_compositor_opengl, fl_compositor_get_type()) static gchar *get_shader_log(GLuint shader)
 
static gchar * get_program_log (GLuint program)
 
static void setup_shader (FlCompositorOpenGL *self)
 
static void cleanup_shader (FlCompositorOpenGL *self)
 
static void composite_layer (FlCompositorOpenGL *self, FlFramebuffer *framebuffer, double x, double y, int width, int height)
 
static gboolean fl_compositor_opengl_present_layers (FlCompositor *compositor, const FlutterLayer **layers, size_t layers_count)
 
static gboolean fl_compositor_opengl_render (FlCompositor *compositor, cairo_t *cr, GdkWindow *window)
 
static void fl_compositor_opengl_dispose (GObject *object)
 
static void fl_compositor_opengl_class_init (FlCompositorOpenGLClass *klass)
 
static void fl_compositor_opengl_init (FlCompositorOpenGL *self)
 
FlCompositorOpenGL * fl_compositor_opengl_new (FlTaskRunner *task_runner, FlOpenGLManager *opengl_manager, gboolean shareable)
 

Variables

static const char * vertex_shader_src
 
static const char * fragment_shader_src
 

Function Documentation

◆ cleanup_shader()

static void cleanup_shader ( FlCompositorOpenGL *  self)
static

Definition at line 171 of file fl_compositor_opengl.cc.

171  {
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 }
gboolean fl_opengl_manager_make_current(FlOpenGLManager *self)

References fl_opengl_manager_make_current().

Referenced by fl_compositor_opengl_dispose().

◆ composite_layer()

static void composite_layer ( FlCompositorOpenGL *  self,
FlFramebuffer *  framebuffer,
double  x,
double  y,
int  width,
int  height 
)
static

Definition at line 187 of file fl_compositor_opengl.cc.

192  {
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 }
self height
self width
size_t fl_framebuffer_get_height(FlFramebuffer *self)
size_t fl_framebuffer_get_width(FlFramebuffer *self)
GLuint fl_framebuffer_get_texture_id(FlFramebuffer *self)
int64_t texture_id

References fl_framebuffer_get_height(), fl_framebuffer_get_texture_id(), fl_framebuffer_get_width(), height, texture_id, and width.

Referenced by fl_compositor_opengl_present_layers().

◆ fl_compositor_opengl_class_init()

static void fl_compositor_opengl_class_init ( FlCompositorOpenGLClass *  klass)
static

Definition at line 403 of file fl_compositor_opengl.cc.

403  {
404  FL_COMPOSITOR_CLASS(klass)->present_layers =
406  FL_COMPOSITOR_CLASS(klass)->render = fl_compositor_opengl_render;
407 
408  G_OBJECT_CLASS(klass)->dispose = fl_compositor_opengl_dispose;
409 }
static void fl_compositor_opengl_dispose(GObject *object)
static gboolean fl_compositor_opengl_present_layers(FlCompositor *compositor, const FlutterLayer **layers, size_t layers_count)
static gboolean fl_compositor_opengl_render(FlCompositor *compositor, cairo_t *cr, GdkWindow *window)

References fl_compositor_opengl_dispose(), fl_compositor_opengl_present_layers(), and fl_compositor_opengl_render().

◆ fl_compositor_opengl_dispose()

static void fl_compositor_opengl_dispose ( GObject *  object)
static

Definition at line 389 of file fl_compositor_opengl.cc.

389  {
390  FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(object);
391 
392  cleanup_shader(self);
393 
394  g_clear_object(&self->task_runner);
395  g_clear_object(&self->opengl_manager);
396  g_clear_object(&self->framebuffer);
397  g_clear_pointer(&self->pixels, g_free);
398  g_mutex_clear(&self->frame_mutex);
399 
400  G_OBJECT_CLASS(fl_compositor_opengl_parent_class)->dispose(object);
401 }
static void cleanup_shader(FlCompositorOpenGL *self)

References cleanup_shader().

Referenced by fl_compositor_opengl_class_init().

◆ fl_compositor_opengl_init()

static void fl_compositor_opengl_init ( FlCompositorOpenGL *  self)
static

Definition at line 411 of file fl_compositor_opengl.cc.

411  {
412  g_mutex_init(&self->frame_mutex);
413 }

◆ fl_compositor_opengl_new()

FlCompositorOpenGL* fl_compositor_opengl_new ( FlTaskRunner *  task_runner,
FlOpenGLManager *  opengl_manager,
gboolean  shareable 
)

Definition at line 415 of file fl_compositor_opengl.cc.

417  {
418  FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(
419  g_object_new(fl_compositor_opengl_get_type(), nullptr));
420 
421  self->task_runner = FL_TASK_RUNNER(g_object_ref(task_runner));
422  self->shareable = shareable;
423  self->opengl_manager = FL_OPENGL_MANAGER(g_object_ref(opengl_manager));
424 
425  setup_shader(self);
426 
427  return self;
428 }
static void setup_shader(FlCompositorOpenGL *self)
G_BEGIN_DECLS FlOpenGLManager gboolean shareable
G_BEGIN_DECLS FlOpenGLManager * opengl_manager

References opengl_manager, setup_shader(), and shareable.

Referenced by setup_opengl(), and TEST().

◆ fl_compositor_opengl_present_layers()

static gboolean fl_compositor_opengl_present_layers ( FlCompositor *  compositor,
const FlutterLayer **  layers,
size_t  layers_count 
)
static

Definition at line 206 of file fl_compositor_opengl.cc.

208  {
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_framebuffer_binding;
232  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &saved_framebuffer_binding);
233 
234  // Update framebuffer to write into.
235  size_t width = layers[0]->size.width;
236  size_t height = layers[0]->size.height;
237  if (self->framebuffer == nullptr ||
238  fl_framebuffer_get_width(self->framebuffer) != width ||
239  fl_framebuffer_get_height(self->framebuffer) != height) {
240  g_clear_object(&self->framebuffer);
241  self->framebuffer =
242  fl_framebuffer_new(general_format, width, height, self->shareable);
243 
244  // If not shareable make buffer to copy frame pixels into.
245  if (!self->shareable) {
246  size_t data_length = width * height * 4;
247  self->pixels = static_cast<uint8_t*>(realloc(self->pixels, data_length));
248  }
249  }
250 
251  self->had_first_frame = true;
252 
253  // FIXME(robert-ancell): The vertex array is the same for all views, but
254  // cannot be shared in OpenGL. Find a way to not generate this every time.
255  GLuint vao;
256  glGenVertexArrays(1, &vao);
257  glBindVertexArray(vao);
258  glBindBuffer(GL_ARRAY_BUFFER, self->vertex_buffer);
259  GLint position_location = glGetAttribLocation(self->program, "position");
260  glEnableVertexAttribArray(position_location);
261  glVertexAttribPointer(position_location, 2, GL_FLOAT, GL_FALSE,
262  sizeof(GLfloat) * 4, 0);
263  GLint texcoord_location = glGetAttribLocation(self->program, "in_texcoord");
264  glEnableVertexAttribArray(texcoord_location);
265  glVertexAttribPointer(texcoord_location, 2, GL_FLOAT, GL_FALSE,
266  sizeof(GLfloat) * 4,
267  reinterpret_cast<void*>(sizeof(GLfloat) * 2));
268 
269  glEnable(GL_BLEND);
270  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
271 
272  glUseProgram(self->program);
273 
274  // Disable the scissor test as it can affect blit operations.
275  // Prevents regressions like: https://github.com/flutter/flutter/issues/140828
276  // See OpenGL specification version 4.6, section 18.3.1.
277  glDisable(GL_SCISSOR_TEST);
278 
279  glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
280  fl_framebuffer_get_id(self->framebuffer));
281  gboolean first_layer = TRUE;
282  for (size_t i = 0; i < layers_count; ++i) {
283  const FlutterLayer* layer = layers[i];
284  switch (layer->type) {
285  case kFlutterLayerContentTypeBackingStore: {
286  const FlutterBackingStore* backing_store = layer->backing_store;
287  FlFramebuffer* framebuffer =
288  FL_FRAMEBUFFER(backing_store->open_gl.framebuffer.user_data);
289  glBindFramebuffer(GL_READ_FRAMEBUFFER,
290  fl_framebuffer_get_id(framebuffer));
291  // The first layer can be blitted, and following layers composited with
292  // this.
293  if (first_layer) {
294  glBlitFramebuffer(layer->offset.x, layer->offset.y, layer->size.width,
295  layer->size.height, layer->offset.x,
296  layer->offset.y, layer->size.width,
297  layer->size.height, GL_COLOR_BUFFER_BIT,
298  GL_NEAREST);
299  first_layer = FALSE;
300  } else {
301  composite_layer(self, framebuffer, layer->offset.x, layer->offset.y,
302  width, height);
303  }
304  } break;
305  case kFlutterLayerContentTypePlatformView: {
306  // TODO(robert-ancell) Not implemented -
307  // https://github.com/flutter/flutter/issues/41724
308  } break;
309  }
310  }
311  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
312  glFlush();
313 
314  glDeleteVertexArrays(1, &vao);
315 
316  glDisable(GL_BLEND);
317 
318  glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
319  glBindVertexArray(saved_vao_binding);
320  glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding);
321  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, saved_framebuffer_binding);
322 
323  if (!self->shareable) {
324  glBindFramebuffer(GL_READ_FRAMEBUFFER,
325  fl_framebuffer_get_id(self->framebuffer));
326  glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, self->pixels);
327  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
328  }
329 
330  g_mutex_unlock(&self->frame_mutex);
331 
332  fl_task_runner_stop_wait(self->task_runner);
333 
334  return TRUE;
335 }
static void composite_layer(FlCompositorOpenGL *self, FlFramebuffer *framebuffer, double x, double y, int width, int height)
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
return TRUE
fl_task_runner_stop_wait(self->task_runner)
GLuint fl_framebuffer_get_id(FlFramebuffer *self)
FlFramebuffer * fl_framebuffer_new(GLint format, size_t width, size_t height, gboolean shareable)

References composite_layer(), fl_framebuffer_get_height(), fl_framebuffer_get_id(), fl_framebuffer_get_width(), fl_framebuffer_new(), fl_task_runner_stop_wait(), height, i, layers, layers_count, TRUE, and width.

Referenced by fl_compositor_opengl_class_init().

◆ fl_compositor_opengl_render()

static gboolean fl_compositor_opengl_render ( FlCompositor *  compositor,
cairo_t *  cr,
GdkWindow *  window 
)
static

Definition at line 337 of file fl_compositor_opengl.cc.

339  {
340  FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(compositor);
341 
342  g_mutex_lock(&self->frame_mutex);
343  if (self->framebuffer == nullptr) {
344  g_mutex_unlock(&self->frame_mutex);
345  return FALSE;
346  }
347 
348  // If frame not ready, then wait for it.
349  gint scale_factor = gdk_window_get_scale_factor(window);
350  size_t width = gdk_window_get_width(window) * scale_factor;
351  size_t height = gdk_window_get_height(window) * scale_factor;
352  while (fl_framebuffer_get_width(self->framebuffer) != width ||
353  fl_framebuffer_get_height(self->framebuffer) != height) {
354  g_mutex_unlock(&self->frame_mutex);
355  fl_task_runner_wait(self->task_runner);
356  g_mutex_lock(&self->frame_mutex);
357  }
358 
359  if (fl_framebuffer_get_shareable(self->framebuffer)) {
360  g_autoptr(FlFramebuffer) sibling =
361  fl_framebuffer_create_sibling(self->framebuffer);
362  gdk_cairo_draw_from_gl(cr, window, fl_framebuffer_get_texture_id(sibling),
363  GL_TEXTURE, scale_factor, 0, 0, width, height);
364  } else {
365  GLint saved_texture_binding;
366  glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding);
367 
368  GLuint texture_id;
369  glGenTextures(1, &texture_id);
370  glBindTexture(GL_TEXTURE_2D, texture_id);
371  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
372  GL_UNSIGNED_BYTE, self->pixels);
373 
374  gdk_cairo_draw_from_gl(cr, window, texture_id, GL_TEXTURE, scale_factor, 0,
375  0, width, height);
376 
377  glDeleteTextures(1, &texture_id);
378 
379  glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
380  }
381 
382  glFlush();
383 
384  g_mutex_unlock(&self->frame_mutex);
385 
386  return TRUE;
387 }
return window
g_autoptr(GMutexLocker) locker
gboolean fl_framebuffer_get_shareable(FlFramebuffer *self)
FlFramebuffer * fl_framebuffer_create_sibling(FlFramebuffer *self)
void fl_task_runner_wait(FlTaskRunner *self)

References fl_framebuffer_create_sibling(), fl_framebuffer_get_height(), fl_framebuffer_get_shareable(), fl_framebuffer_get_texture_id(), fl_framebuffer_get_width(), fl_task_runner_wait(), g_autoptr(), height, texture_id, TRUE, width, and window.

Referenced by fl_compositor_opengl_class_init().

◆ G_DEFINE_TYPE()

G_DEFINE_TYPE ( FlCompositorOpenGL  ,
fl_compositor_opengl  ,
fl_compositor_get_type()   
)

Definition at line 82 of file fl_compositor_opengl.cc.

87  {
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 }

◆ get_program_log()

static gchar* get_program_log ( GLuint  program)
static

Definition at line 100 of file fl_compositor_opengl.cc.

100  {
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 }

Referenced by setup_shader().

◆ setup_shader()

static void setup_shader ( FlCompositorOpenGL *  self)
static

Definition at line 112 of file fl_compositor_opengl.cc.

112  {
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 }
static const char * fragment_shader_src
static gchar * get_program_log(GLuint program)
static const char * vertex_shader_src

References fl_opengl_manager_make_current(), fragment_shader_src, get_program_log(), and vertex_shader_src.

Referenced by fl_compositor_opengl_new().

Variable Documentation

◆ fragment_shader_src

const char* fragment_shader_src
static
Initial value:
=
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
"\n"
"uniform sampler2D texture;\n"
"varying vec2 texcoord;\n"
"\n"
"void main() {\n"
" gl_FragColor = texture2D(texture, texcoord);\n"
"}\n"

Definition at line 29 of file fl_compositor_opengl.cc.

Referenced by setup_shader().

◆ vertex_shader_src

const char* vertex_shader_src
static
Initial value:
=
"attribute vec2 position;\n"
"attribute vec2 in_texcoord;\n"
"uniform vec2 offset;\n"
"uniform vec2 scale;\n"
"varying vec2 texcoord;\n"
"\n"
"void main() {\n"
" gl_Position = vec4(offset + position * scale, 0, 1);\n"
" texcoord = in_texcoord;\n"
"}\n"

Definition at line 16 of file fl_compositor_opengl.cc.

Referenced by setup_shader().