Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
6
7#include <epoxy/egl.h>
8#include <epoxy/gl.h>
9
15
17 FlCompositor parent_instance;
18
19 // Task runner to wait for frames on.
20 FlTaskRunner* task_runner;
21
22 // TRUE if can share framebuffers between contexts.
23 gboolean shareable;
24
25 // Flutter OpenGL contexts.
26 FlOpenGLManager* opengl_manager;
27
28 // Last rendered frame.
29 FlFramebuffer* framebuffer;
30
31 // Last rendered frame pixels (only set if shareable is FALSE).
32 uint8_t* pixels;
33
34 // Shader program used to composite layers.
35 FlCompositorOpenGLShader* shader;
36
37 // Ensure Flutter and GTK can access the frame data (framebuffer or pixels).
39};
40
41G_DEFINE_TYPE(FlCompositorOpenGL,
42 fl_compositor_opengl,
43 fl_compositor_get_type())
44
45static void composite_layer(FlCompositorOpenGL* self,
46 FlFramebuffer* framebuffer,
47 double x,
48 double y,
49 int width,
50 int height) {
51 size_t texture_width = fl_framebuffer_get_width(framebuffer);
54 (2 * y / height) - 1.0);
57
59 glBindTexture(GL_TEXTURE_2D, texture_id);
60
61 glDrawArrays(GL_TRIANGLES, 0, 6);
62}
63
64static gboolean fl_compositor_opengl_present_layers(FlCompositor* compositor,
65 const FlutterLayer** layers,
66 size_t layers_count) {
67 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(compositor);
68
69 g_mutex_lock(&self->frame_mutex);
70 if (layers_count == 0) {
71 g_mutex_unlock(&self->frame_mutex);
72 return TRUE;
73 }
74
75 GLint general_format = GL_RGBA;
76 if (epoxy_has_gl_extension("GL_EXT_texture_format_BGRA8888")) {
77 general_format = GL_BGRA_EXT;
78 }
79
80 // Save bindings that are set by this function. All bindings must be restored
81 // to their original values because Skia expects that its bindings have not
82 // been altered.
83 GLint saved_texture_binding;
84 glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding);
85 GLint saved_vao_binding;
86 glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao_binding);
87 GLint saved_array_buffer_binding;
88 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &saved_array_buffer_binding);
89 GLint saved_draw_framebuffer_binding;
90 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &saved_draw_framebuffer_binding);
91 GLint saved_read_framebuffer_binding;
92 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &saved_read_framebuffer_binding);
93 GLint saved_current_program;
94 glGetIntegerv(GL_CURRENT_PROGRAM, &saved_current_program);
95 GLboolean saved_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
96 GLboolean saved_blend = glIsEnabled(GL_BLEND);
97 GLint saved_src_rgb;
98 glGetIntegerv(GL_BLEND_SRC_RGB, &saved_src_rgb);
99 GLint saved_src_alpha;
100 glGetIntegerv(GL_BLEND_SRC_ALPHA, &saved_src_alpha);
101 GLint saved_dst_rgb;
102 glGetIntegerv(GL_BLEND_DST_RGB, &saved_dst_rgb);
103 GLint saved_dst_alpha;
104 glGetIntegerv(GL_BLEND_DST_ALPHA, &saved_dst_alpha);
105
106 // Update framebuffer to write into.
107 size_t width = layers[0]->size.width;
108 size_t height = layers[0]->size.height;
109 if (self->framebuffer == nullptr ||
110 fl_framebuffer_get_width(self->framebuffer) != width ||
111 fl_framebuffer_get_height(self->framebuffer) != height) {
112 g_clear_object(&self->framebuffer);
113 self->framebuffer =
114 fl_framebuffer_new(general_format, width, height, self->shareable);
115
116 // If not shareable make buffer to copy frame pixels into.
117 if (!self->shareable) {
118 size_t data_length = width * height * 4;
119 self->pixels =
120 static_cast<uint8_t*>(g_realloc(self->pixels, data_length));
121 }
122 }
123
124 // FIXME(robert-ancell): The vertex array is the same for all views, but
125 // cannot be shared in OpenGL. Find a way to not generate this every time.
126 GLuint vao;
127 glGenVertexArrays(1, &vao);
128 glBindVertexArray(vao);
129
130 glEnable(GL_BLEND);
131 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
132
134
135 // Disable the scissor test as it can affect blit operations.
136 // Prevents regressions like: https://github.com/flutter/flutter/issues/140828
137 // See OpenGL specification version 4.6, section 18.3.1.
138 glDisable(GL_SCISSOR_TEST);
139
140 glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
141 fl_framebuffer_get_id(self->framebuffer));
142 gboolean first_layer = TRUE;
143 for (size_t i = 0; i < layers_count; ++i) {
144 const FlutterLayer* layer = layers[i];
145 switch (layer->type) {
147 const FlutterBackingStore* backing_store = layer->backing_store;
148 FlFramebuffer* framebuffer =
149 FL_FRAMEBUFFER(backing_store->open_gl.framebuffer.user_data);
150 glBindFramebuffer(GL_READ_FRAMEBUFFER,
152 // The first layer can be blitted, and following layers composited with
153 // this.
154 if (first_layer) {
155 glBlitFramebuffer(layer->offset.x, layer->offset.y, layer->size.width,
156 layer->size.height, layer->offset.x,
157 layer->offset.y, layer->size.width,
158 layer->size.height, GL_COLOR_BUFFER_BIT,
159 GL_NEAREST);
160 first_layer = FALSE;
161 } else {
162 composite_layer(self, framebuffer, layer->offset.x, layer->offset.y,
163 width, height);
164 }
165 } break;
167 // TODO(robert-ancell) Not implemented -
168 // https://github.com/flutter/flutter/issues/41724
169 } break;
170 }
171 }
172 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
173 glFlush();
174
175 glDeleteVertexArrays(1, &vao);
176
177 if (saved_blend) {
178 glEnable(GL_BLEND);
179 } else {
180 glDisable(GL_BLEND);
181 }
182 if (saved_scissor_test) {
183 glEnable(GL_SCISSOR_TEST);
184 } else {
185 glDisable(GL_SCISSOR_TEST);
186 }
187
188 glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
189 glBindVertexArray(saved_vao_binding);
190 glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding);
191 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, saved_draw_framebuffer_binding);
192 glUseProgram(saved_current_program);
193 glBlendFuncSeparate(saved_src_rgb, saved_dst_rgb, saved_src_alpha,
194 saved_dst_alpha);
195
196 if (!self->shareable) {
197 glBindFramebuffer(GL_READ_FRAMEBUFFER,
198 fl_framebuffer_get_id(self->framebuffer));
199 glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, self->pixels);
200 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
201 }
202 glBindFramebuffer(GL_READ_FRAMEBUFFER, saved_read_framebuffer_binding);
203
204 g_mutex_unlock(&self->frame_mutex);
205
206 fl_task_runner_stop_wait(self->task_runner);
207
208 return TRUE;
209}
210
211static void fl_compositor_opengl_get_frame_size(FlCompositor* compositor,
212 size_t* width,
213 size_t* height) {
214 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(compositor);
215
216 g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
217
218 if (width != nullptr) {
219 *width = self->framebuffer != nullptr
220 ? fl_framebuffer_get_width(self->framebuffer)
221 : 0;
222 }
223 if (height != nullptr) {
224 *height = self->framebuffer != nullptr
225 ? fl_framebuffer_get_height(self->framebuffer)
226 : 0;
227 }
228}
229
230static gboolean fl_compositor_opengl_render(FlCompositor* compositor,
231 cairo_t* cr,
232 GdkWindow* window,
233 gboolean wait_for_frame) {
234 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(compositor);
235
236 g_mutex_lock(&self->frame_mutex);
237 if (self->framebuffer == nullptr) {
238 g_mutex_unlock(&self->frame_mutex);
239 return FALSE;
240 }
241
242 // If frame not ready, then wait for it.
243 gint scale_factor = gdk_window_get_scale_factor(window);
244 size_t width, height;
245 gint64 expiry_time =
246 g_get_monotonic_time() + kCompositorRenderTimeoutMicroseconds;
247 while (true) {
248 width = gdk_window_get_width(window) * scale_factor;
249 height = gdk_window_get_height(window) * scale_factor;
250 if (!wait_for_frame) {
251 break;
252 }
253
254 size_t framebuffer_width = fl_framebuffer_get_width(self->framebuffer);
255 size_t framebuffer_height = fl_framebuffer_get_height(self->framebuffer);
256 if (framebuffer_width == width && framebuffer_height == height) {
257 break;
258 }
259
260 if (g_get_monotonic_time() > expiry_time) {
261 g_warning(
262 "Timed out waiting for OpenGL frame of size %zdx%zd (have %zdx%zd)",
263 width, height, framebuffer_width, framebuffer_height);
264 break;
265 }
266
267 g_mutex_unlock(&self->frame_mutex);
268 fl_task_runner_wait(self->task_runner, expiry_time);
269 g_mutex_lock(&self->frame_mutex);
270 }
271
272 if (fl_framebuffer_get_shareable(self->framebuffer)) {
273 g_autoptr(FlFramebuffer) sibling =
276 GL_TEXTURE, scale_factor, 0, 0, width, height);
277 } else {
278 GLint saved_texture_binding;
279 glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding);
280
281 GLuint texture_id;
282 glGenTextures(1, &texture_id);
283 glBindTexture(GL_TEXTURE_2D, texture_id);
284 GLsizei fb_width = 0;
285 GLsizei fb_height = 0;
286 if (self->framebuffer != nullptr) {
287 fb_width =
288 static_cast<GLsizei>(fl_framebuffer_get_width(self->framebuffer));
289 fb_height =
290 static_cast<GLsizei>(fl_framebuffer_get_height(self->framebuffer));
291 }
292 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb_width, fb_height, 0, GL_RGBA,
293 GL_UNSIGNED_BYTE, self->pixels);
294
295 gdk_cairo_draw_from_gl(cr, window, texture_id, GL_TEXTURE, scale_factor, 0,
296 0, width, height);
297
298 glDeleteTextures(1, &texture_id);
299
300 glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
301 }
302
303 glFlush();
304
305 g_mutex_unlock(&self->frame_mutex);
306
307 return TRUE;
308}
309
310static void fl_compositor_opengl_dispose(GObject* object) {
311 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(object);
312
313 g_clear_object(&self->shader);
314
315 g_clear_object(&self->task_runner);
316 g_clear_object(&self->opengl_manager);
317 g_clear_object(&self->framebuffer);
318 g_clear_pointer(&self->pixels, g_free);
319 g_mutex_clear(&self->frame_mutex);
320
321 G_OBJECT_CLASS(fl_compositor_opengl_parent_class)->dispose(object);
322}
323
324static void fl_compositor_opengl_class_init(FlCompositorOpenGLClass* klass) {
325 FL_COMPOSITOR_CLASS(klass)->present_layers =
327 FL_COMPOSITOR_CLASS(klass)->get_frame_size =
329 FL_COMPOSITOR_CLASS(klass)->render = fl_compositor_opengl_render;
330
331 G_OBJECT_CLASS(klass)->dispose = fl_compositor_opengl_dispose;
332}
333
334static void fl_compositor_opengl_init(FlCompositorOpenGL* self) {
335 g_mutex_init(&self->frame_mutex);
336}
337
338FlCompositorOpenGL* fl_compositor_opengl_new(FlTaskRunner* task_runner,
339 FlOpenGLManager* opengl_manager,
340 gboolean shareable) {
341 FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(
342 g_object_new(fl_compositor_opengl_get_type(), nullptr));
343
344 self->task_runner = FL_TASK_RUNNER(g_object_ref(task_runner));
345 self->shareable = shareable;
346 self->opengl_manager = FL_OPENGL_MANAGER(g_object_ref(opengl_manager));
348
349 return self;
350}
@ kFlutterLayerContentTypePlatformView
Indicates that the contents of this layer are determined by the embedder.
Definition embedder.h:2159
@ kFlutterLayerContentTypeBackingStore
Definition embedder.h:2157
GLFWwindow * window
Definition main.cc:60
g_autoptr(FlEngine) engine
G_BEGIN_DECLS constexpr gint64 kCompositorRenderTimeoutMicroseconds
static void fl_compositor_opengl_dispose(GObject *object)
GLuint texture_id
G_DEFINE_TYPE(FlCompositorOpenGL, fl_compositor_opengl, fl_compositor_get_type()) static void composite_layer(FlCompositorOpenGL *self
FlFramebuffer * framebuffer
static gboolean fl_compositor_opengl_present_layers(FlCompositor *compositor, const FlutterLayer **layers, size_t layers_count)
FlFramebuffer double x
static void fl_compositor_opengl_init(FlCompositorOpenGL *self)
size_t texture_height
FlFramebuffer double double int width
glDrawArrays(GL_TRIANGLES, 0, 6)
fl_compositor_opengl_shader_set_scale(self->shader, texture_width/width, texture_height/height)
static void fl_compositor_opengl_class_init(FlCompositorOpenGLClass *klass)
glBindTexture(GL_TEXTURE_2D, texture_id)
static gboolean fl_compositor_opengl_render(FlCompositor *compositor, cairo_t *cr, GdkWindow *window, gboolean wait_for_frame)
static void fl_compositor_opengl_get_frame_size(FlCompositor *compositor, size_t *width, size_t *height)
fl_compositor_opengl_shader_set_offset(self->shader,(2 *x/width) - 1.0,(2 *y/height) - 1.0)
FlFramebuffer double double y
FlCompositorOpenGL * fl_compositor_opengl_new(FlTaskRunner *task_runner, FlOpenGLManager *opengl_manager, gboolean shareable)
FlFramebuffer double double int int height
G_BEGIN_DECLS FlOpenGLManager gboolean shareable
G_BEGIN_DECLS FlOpenGLManager * opengl_manager
FlCompositorOpenGLShader * fl_compositor_opengl_shader_new(FlOpenGLManager *opengl_manager)
void fl_compositor_opengl_shader_use(FlCompositorOpenGLShader *self)
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
return TRUE
fl_task_runner_stop_wait(self->task_runner)
size_t fl_framebuffer_get_height(FlFramebuffer *self)
FlFramebuffer * fl_framebuffer_new(GLint format, size_t width, size_t height, gboolean shareable)
GLuint fl_framebuffer_get_id(FlFramebuffer *self)
FlFramebuffer * fl_framebuffer_create_sibling(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)
void fl_task_runner_wait(FlTaskRunner *self, gint64 expiry_time)
bool epoxy_has_gl_extension(const char *extension)
void gdk_cairo_draw_from_gl(cairo_t *cr, GdkWindow *window, int source, int source_type, int buffer_scale, int x, int y, int width, int height)
Definition mock_gtk.cc:170
int gdk_window_get_width(GdkWindow *window)
Definition mock_gtk.cc:79
gint gdk_window_get_scale_factor(GdkWindow *window)
Definition mock_gtk.cc:95
int gdk_window_get_height(GdkWindow *window)
Definition mock_gtk.cc:87
FlCompositorOpenGLShader * shader
FlOpenGLManager * opengl_manager
FlutterOpenGLBackingStore open_gl
The description of the OpenGL backing store.
Definition embedder.h:2132
FlutterPoint offset
Definition embedder.h:2200
FlutterLayerContentType type
Definition embedder.h:2189
const FlutterBackingStore * backing_store
Definition embedder.h:2193
FlutterSize size
The size of the layer (in physical pixels).
Definition embedder.h:2202
FlutterOpenGLFramebuffer framebuffer
Definition embedder.h:1975
void * user_data
User data to be returned on the invocation of the destruction callback.
Definition embedder.h:554
double height
Definition embedder.h:636
double width
Definition embedder.h:635