Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
fl_view_renderer_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 <gdk/gdkwayland.h>
8
13
14// Maximum time to wait for a frame to be ready before giving up and rendering.
15static constexpr gint64 kRenderTimeoutMicroseconds = 100000; // 100ms
16
18 FlViewRenderer parent_instance;
19
20 // Engine this widget is rendering.
21 FlEngine* engine;
22
23 // TRUE if the view size should be controlled by Flutter.
25
26 // Rendering context for OpenGL.
27 GdkGLContext* render_context;
28
29 // Combines layers into frame.
30 FlCompositorOpenGL* compositor;
31
32 // Task runner to wait for frames on.
33 FlTaskRunner* task_runner;
34
35 // Ensure Flutter and GTK can access the frame stored in the compositor.
37};
38
39G_DEFINE_TYPE(FlViewRendererOpenGL,
40 fl_view_renderer_opengl,
41 fl_view_renderer_get_type())
42
43// Redraw the view from the GTK thread.
44static gboolean redraw_cb(gpointer user_data) {
45 g_autoptr(FlViewRendererOpenGL) self = FL_VIEW_RENDERER_OPENGL(user_data);
46
47 if (self->compositor == nullptr) {
48 return G_SOURCE_REMOVE;
49 }
50
51 fl_view_renderer_notify_frame(FL_VIEW_RENDERER(self));
52
53 // If Flutter is controlling the window size, then resize the view if
54 // necessary.
55 GtkWidget* render_widget = GTK_WIDGET(self);
56 GtkAllocation allocation;
57 gtk_widget_get_allocation(render_widget, &allocation);
58 gint scale_factor = gtk_widget_get_scale_factor(render_widget);
59 size_t width = allocation.width * scale_factor;
60 size_t height = allocation.height * scale_factor;
61 size_t frame_width, frame_height;
62 {
63 g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
64 fl_compositor_opengl_get_frame_size(self->compositor, &frame_width,
65 &frame_height);
66 }
67 gboolean frame_size_matches = width == frame_width && height == frame_height;
68 if (self->sized_to_content && !frame_size_matches) {
69 gtk_widget_set_size_request(render_widget, frame_width / scale_factor,
70 frame_height / scale_factor);
71 GtkWidget* toplevel = gtk_widget_get_toplevel(render_widget);
72 if (GTK_IS_WINDOW(toplevel)) {
73 // Resize to smallest size, so that the window will shrink to fit the new
74 // size of the render area.
75 gtk_window_resize(GTK_WINDOW(toplevel), 1, 1);
76 }
77 return G_SOURCE_REMOVE;
78 }
79
80 gtk_widget_queue_draw(render_widget);
81
82 return G_SOURCE_REMOVE;
83}
84
85// Wait for a frame matching the window size to be ready, or until the timeout
86// expires. Must be called with the frame mutex held; the mutex is still held
87// when this function returns.
88static void wait_for_frame(FlViewRendererOpenGL* self,
89 GdkWindow* window,
90 gint scale_factor) {
91 gint64 expiry_time = g_get_monotonic_time() + kRenderTimeoutMicroseconds;
92 while (true) {
93 size_t width = gdk_window_get_width(window) * scale_factor;
94 size_t height = gdk_window_get_height(window) * scale_factor;
95 size_t frame_width, frame_height;
96 fl_compositor_opengl_get_frame_size(self->compositor, &frame_width,
97 &frame_height);
98 if (frame_width == width && frame_height == height) {
99 break;
100 }
101
102 if (g_get_monotonic_time() > expiry_time) {
103 g_warning(
104 "Timed out waiting for OpenGL frame of size %zdx%zd (have "
105 "%zdx%zd)",
106 width, height, frame_width, frame_height);
107 break;
108 }
109
110 g_mutex_unlock(&self->frame_mutex);
111 fl_task_runner_wait(self->task_runner, expiry_time);
112 g_mutex_lock(&self->frame_mutex);
113 }
114}
115
116// Implements GtkWidget::realize.
117static void fl_view_renderer_opengl_realize(GtkWidget* widget) {
118 FlViewRendererOpenGL* self = FL_VIEW_RENDERER_OPENGL(widget);
119
120 GTK_WIDGET_CLASS(fl_view_renderer_opengl_parent_class)->realize(widget);
121
122 g_autoptr(GError) error = nullptr;
123 self->render_context = gdk_window_create_gl_context(
124 gtk_widget_get_window(GTK_WIDGET(self)), &error);
125 if (self->render_context == nullptr) {
126 g_warning("Failed to create OpenGL context: %s", error->message);
127 return;
128 }
129
130 if (!gdk_gl_context_realize(self->render_context, &error)) {
131 g_warning("Failed to realize OpenGL context: %s", error->message);
132 g_clear_object(&self->render_context);
133 return;
134 }
135
136 // If using Wayland, then EGL is in use and we can access the frame
137 // from the Flutter context using EGLImage. If not (i.e. X11 using GLX)
138 // then we have to copy the texture via the CPU.
139 gboolean shareable =
140 GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(GTK_WIDGET(self)));
141 self->task_runner =
142 FL_TASK_RUNNER(g_object_ref(fl_engine_get_task_runner(self->engine)));
143 self->compositor = fl_compositor_opengl_new(
145}
146
147// Implements GtkWidget::draw.
148static gboolean fl_view_renderer_opengl_draw(GtkWidget* widget, cairo_t* cr) {
149 FlViewRendererOpenGL* self = FL_VIEW_RENDERER_OPENGL(widget);
150
151 fl_view_renderer_paint_background(FL_VIEW_RENDERER(self), cr);
152
153 // The compositor is created when the widget is realized; if it is not yet
154 // available there is nothing to render beyond the background.
155 if (self->compositor == nullptr) {
156 return TRUE;
157 }
158
159 GdkWindow* window = gtk_widget_get_window(widget);
160 gint scale_factor = gdk_window_get_scale_factor(window);
161
162 g_mutex_lock(&self->frame_mutex);
163
164 // If frame not ready, then wait for it.
165 if (!self->sized_to_content) {
166 wait_for_frame(self, window, scale_factor);
167 }
168
169 if (self->render_context != nullptr) {
170 gdk_gl_context_make_current(self->render_context);
171 }
172
173 gboolean result = fl_compositor_opengl_render(self->compositor, cr, window);
174
175 if (self->render_context != nullptr) {
177 }
178
179 g_mutex_unlock(&self->frame_mutex);
180
181 return result;
182}
183
184// Implements FlViewRenderer::present_layers.
185static void fl_view_renderer_opengl_present_layers(FlViewRenderer* renderer,
186 const FlutterLayer** layers,
187 size_t layers_count) {
188 FlViewRendererOpenGL* self = FL_VIEW_RENDERER_OPENGL(renderer);
189
190 // Frames may be presented before the widget is realized and the compositor
191 // is set up; ignore them.
192 if (self->compositor == nullptr) {
193 return;
194 }
195
196 g_mutex_lock(&self->frame_mutex);
198 g_mutex_unlock(&self->frame_mutex);
199
200 // Wake up the GTK thread if it is waiting for this frame.
201 fl_task_runner_stop_wait(self->task_runner);
202
203 // Perform the redraw in the GTK thread.
204 g_idle_add(redraw_cb, g_object_ref(self));
205}
206
207static void fl_view_renderer_opengl_dispose(GObject* object) {
208 FlViewRendererOpenGL* self = FL_VIEW_RENDERER_OPENGL(object);
209
210 g_clear_object(&self->engine);
211 g_clear_object(&self->render_context);
212 g_clear_object(&self->task_runner);
213 g_mutex_clear(&self->frame_mutex);
214
215 G_OBJECT_CLASS(fl_view_renderer_opengl_parent_class)->dispose(object);
216}
217
218static void fl_view_renderer_opengl_finalize(GObject* object) {
219 FlViewRendererOpenGL* self = FL_VIEW_RENDERER_OPENGL(object);
220
221 // The compositor is released here rather than in dispose() so it outlives a
222 // forced dispose (e.g. gtk_widget_destroy()) and is only freed once the last
223 // reference is dropped. This keeps it alive for the raster thread, which
224 // holds a strong reference on the view (and thus this renderer) while
225 // presenting.
226 g_clear_object(&self->compositor);
227
228 G_OBJECT_CLASS(fl_view_renderer_opengl_parent_class)->finalize(object);
229}
230
232 FlViewRendererOpenGLClass* klass) {
233 GObjectClass* object_class = G_OBJECT_CLASS(klass);
234 object_class->dispose = fl_view_renderer_opengl_dispose;
235 object_class->finalize = fl_view_renderer_opengl_finalize;
236
237 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
238 widget_class->realize = fl_view_renderer_opengl_realize;
239 widget_class->draw = fl_view_renderer_opengl_draw;
240
241 FlViewRendererClass* renderer_class = FL_VIEW_RENDERER_CLASS(klass);
242 renderer_class->present_layers = fl_view_renderer_opengl_present_layers;
243}
244
245static void fl_view_renderer_opengl_init(FlViewRendererOpenGL* self) {
246 g_mutex_init(&self->frame_mutex);
247}
248
249FlViewRendererOpenGL* fl_view_renderer_opengl_new(FlEngine* engine,
250 gboolean sized_to_content) {
251 g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr);
252
253 FlViewRendererOpenGL* self = FL_VIEW_RENDERER_OPENGL(
254 g_object_new(fl_view_renderer_opengl_get_type(), nullptr));
255 self->engine = FL_ENGINE(g_object_ref(engine));
256 self->sized_to_content = sized_to_content;
257 return self;
258}
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
g_autoptr(FlEngine) engine
FlCompositorOpenGL * fl_compositor_opengl_new(FlOpenGLManager *opengl_manager, gboolean shareable)
void fl_compositor_opengl_get_frame_size(FlCompositorOpenGL *self, size_t *width, size_t *height)
gboolean fl_compositor_opengl_render(FlCompositorOpenGL *self, cairo_t *cr, GdkWindow *window)
gboolean fl_compositor_opengl_composite_layers(FlCompositorOpenGL *self, const FlutterLayer **layers, size_t layers_count)
G_BEGIN_DECLS gboolean shareable
FlTaskRunner * fl_engine_get_task_runner(FlEngine *self)
FlOpenGLManager * fl_engine_get_opengl_manager(FlEngine *self)
Definition fl_engine.cc:749
const uint8_t uint32_t uint32_t GError ** error
void fl_task_runner_stop_wait(FlTaskRunner *self)
void fl_task_runner_wait(FlTaskRunner *self, gint64 expiry_time)
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
void fl_view_renderer_notify_frame(FlViewRenderer *self)
void fl_view_renderer_paint_background(FlViewRenderer *self, cairo_t *cr)
static void fl_view_renderer_opengl_class_init(FlViewRendererOpenGLClass *klass)
static void fl_view_renderer_opengl_present_layers(FlViewRenderer *renderer, const FlutterLayer **layers, size_t layers_count)
static gboolean fl_view_renderer_opengl_draw(GtkWidget *widget, cairo_t *cr)
static void fl_view_renderer_opengl_finalize(GObject *object)
static void fl_view_renderer_opengl_dispose(GObject *object)
FlViewRendererOpenGL * fl_view_renderer_opengl_new(FlEngine *engine, gboolean sized_to_content)
static constexpr gint64 kRenderTimeoutMicroseconds
static void fl_view_renderer_opengl_init(FlViewRendererOpenGL *self)
G_DEFINE_TYPE(FlViewRendererOpenGL, fl_view_renderer_opengl, fl_view_renderer_get_type()) static gboolean redraw_cb(gpointer user_data)
static void fl_view_renderer_opengl_realize(GtkWidget *widget)
static void wait_for_frame(FlViewRendererOpenGL *self, GdkWindow *window, gint scale_factor)
G_BEGIN_DECLS gboolean sized_to_content
GdkDisplay * gtk_widget_get_display(GtkWidget *widget)
Definition mock_gtk.cc:253
void gtk_widget_queue_draw(GtkWidget *widget)
Definition mock_gtk.cc:271
void gtk_window_resize(GtkWindow *window, gint width, gint height)
Definition mock_gtk.cc:207
void gdk_gl_context_clear_current(GdkGLContext *context)
Definition mock_gtk.cc:162
int gdk_window_get_width(GdkWindow *window)
Definition mock_gtk.cc:79
void gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation)
Definition mock_gtk.cc:245
gint gdk_window_get_scale_factor(GdkWindow *window)
Definition mock_gtk.cc:95
void gdk_gl_context_make_current(GdkGLContext *context)
Definition mock_gtk.cc:166
int gdk_window_get_height(GdkWindow *window)
Definition mock_gtk.cc:87
GtkWidget * gtk_widget_get_toplevel(GtkWidget *widget)
Definition mock_gtk.cc:304
GdkGLContext * gdk_window_create_gl_context(GdkWindow *window, GError **error)
Definition mock_gtk.cc:149
void gdk_gl_context_realize(GdkGLContext *context)
Definition mock_gtk.cc:158
GdkWindow * gtk_widget_get_window(GtkWidget *widget)
Definition mock_gtk.cc:309
gint gtk_widget_get_scale_factor(GtkWidget *widget)
Definition mock_gtk.cc:258
int32_t height
int32_t width
FlCompositorOpenGL * compositor