Flutter Linux Embedder
fl_view_renderer.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 
14 
16  GtkDrawingArea parent_instance;
17 
18  // Engine this widget is rendering.
19  FlEngine* engine;
20 
21  // Rendering context when using OpenGL.
22  GdkGLContext* render_context;
23 
24  // Combines layers into frame.
25  FlCompositor* compositor;
26 
27  // Background color.
28  GdkRGBA* background_color;
29 
30  // TRUE if the view size should be controlled by Flutter.
31  gboolean sized_to_content;
32 
33  // TRUE if have got the first frame to render.
34  gboolean have_first_frame;
35 };
36 
38 
40 
41 G_DEFINE_TYPE(FlViewRenderer, fl_view_renderer, GTK_TYPE_DRAWING_AREA)
42 
43 // Redraw the view from the GTK thread.
44 static gboolean redraw_cb(gpointer user_data) {
45  g_autoptr(FlViewRenderer) self = FL_VIEW_RENDERER(user_data);
46 
47  if (self->compositor == nullptr) {
48  return G_SOURCE_REMOVE;
49  }
50 
51  if (!self->have_first_frame) {
52  self->have_first_frame = TRUE;
53  g_signal_emit(self, fl_view_renderer_signals[SIGNAL_FIRST_FRAME], 0);
54  }
55 
56  // If Flutter is controlling the window size, then resize the view if
57  // necessary.
58  GtkWidget* render_widget = GTK_WIDGET(self);
59  GtkAllocation allocation;
60  gtk_widget_get_allocation(render_widget, &allocation);
61  gint scale_factor = gtk_widget_get_scale_factor(render_widget);
62  size_t width = allocation.width * scale_factor;
63  size_t height = allocation.height * scale_factor;
64  size_t frame_width, frame_height;
65  fl_compositor_get_frame_size(self->compositor, &frame_width, &frame_height);
66  gboolean frame_size_matches = width == frame_width && height == frame_height;
67  if (self->sized_to_content && !frame_size_matches) {
68  gtk_widget_set_size_request(render_widget, frame_width / scale_factor,
69  frame_height / scale_factor);
70  GtkWidget* toplevel = gtk_widget_get_toplevel(render_widget);
71  if (GTK_IS_WINDOW(toplevel)) {
72  // Resize to smallest size, so that the window will shrink to fit the new
73  // size of the render area.
74  gtk_window_resize(GTK_WINDOW(toplevel), 1, 1);
75  }
76  return G_SOURCE_REMOVE;
77  }
78 
79  gtk_widget_queue_draw(render_widget);
80 
81  return G_SOURCE_REMOVE;
82 }
83 
84 static void setup_opengl(FlViewRenderer* self) {
85  g_autoptr(GError) error = nullptr;
86 
87  self->render_context = gdk_window_create_gl_context(
88  gtk_widget_get_window(GTK_WIDGET(self)), &error);
89  if (self->render_context == nullptr) {
90  g_warning("Failed to create OpenGL context: %s", error->message);
91  return;
92  }
93 
94  if (!gdk_gl_context_realize(self->render_context, &error)) {
95  g_warning("Failed to realize OpenGL context: %s", error->message);
96  return;
97  }
98 
99  // If using Wayland, then EGL is in use and we can access the frame
100  // from the Flutter context using EGLImage. If not (i.e. X11 using GLX)
101  // then we have to copy the texture via the CPU.
102  gboolean shareable =
103  GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(GTK_WIDGET(self)));
104  self->compositor = FL_COMPOSITOR(fl_compositor_opengl_new(
105  fl_engine_get_task_runner(self->engine),
106  fl_engine_get_opengl_manager(self->engine), shareable));
107 }
108 
109 static void setup_software(FlViewRenderer* self) {
110  self->compositor = FL_COMPOSITOR(
112 }
113 
114 static void paint_background(FlViewRenderer* self, cairo_t* cr) {
115  // Don't bother drawing if fully transparent - the widget above this will
116  // already be drawn by GTK.
117  if (self->background_color->red == 0 && self->background_color->green == 0 &&
118  self->background_color->blue == 0 && self->background_color->alpha == 0) {
119  return;
120  }
121 
122  gdk_cairo_set_source_rgba(cr, self->background_color);
123  cairo_paint(cr);
124 }
125 
126 // Implements GtkWidget::realize.
127 static void fl_view_renderer_realize(GtkWidget* widget) {
128  FlViewRenderer* self = FL_VIEW_RENDERER(widget);
129 
130  GTK_WIDGET_CLASS(fl_view_renderer_parent_class)->realize(widget);
131 
132  switch (fl_engine_get_renderer_type(self->engine)) {
133  case kOpenGL:
134  setup_opengl(self);
135  break;
136  case kSoftware:
137  setup_software(self);
138  break;
139  default:
140  break;
141  }
142 }
143 
144 // Implements GtkWidget::draw.
145 static gboolean fl_view_renderer_draw(GtkWidget* widget, cairo_t* cr) {
146  FlViewRenderer* self = FL_VIEW_RENDERER(widget);
147 
148  paint_background(self, cr);
149 
150  // The compositor is created when the widget is realized; if it is not yet
151  // available there is nothing to render beyond the background.
152  if (self->compositor == nullptr) {
153  return TRUE;
154  }
155 
156  if (self->render_context) {
157  gdk_gl_context_make_current(self->render_context);
158  }
159 
160  gboolean wait_for_frame = !self->sized_to_content;
161  gboolean result = fl_compositor_render(
162  self->compositor, cr, gtk_widget_get_window(widget), wait_for_frame);
163 
164  if (self->render_context) {
165  gdk_gl_context_clear_current();
166  }
167 
168  return result;
169 }
170 
171 static void fl_view_renderer_dispose(GObject* object) {
172  FlViewRenderer* self = FL_VIEW_RENDERER(object);
173 
174  g_clear_object(&self->render_context);
175  g_clear_object(&self->engine);
176  g_clear_pointer(&self->background_color, gdk_rgba_free);
177 
178  G_OBJECT_CLASS(fl_view_renderer_parent_class)->dispose(object);
179 }
180 
181 static void fl_view_renderer_finalize(GObject* object) {
182  FlViewRenderer* self = FL_VIEW_RENDERER(object);
183 
184  // The compositor is released here rather than in dispose() so it outlives a
185  // forced dispose (e.g. gtk_widget_destroy()) and is only freed once the last
186  // reference is dropped. This keeps it alive for the raster thread, which
187  // holds a strong reference on the view (and thus this renderer) while
188  // presenting.
189  g_clear_object(&self->compositor);
190 
191  G_OBJECT_CLASS(fl_view_renderer_parent_class)->finalize(object);
192 }
193 
194 static void fl_view_renderer_class_init(FlViewRendererClass* klass) {
195  G_OBJECT_CLASS(klass)->dispose = fl_view_renderer_dispose;
196  G_OBJECT_CLASS(klass)->finalize = fl_view_renderer_finalize;
197 
198  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
199  widget_class->realize = fl_view_renderer_realize;
200  widget_class->draw = fl_view_renderer_draw;
201 
203  g_signal_new("first-frame", fl_view_renderer_get_type(),
204  G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
205 }
206 
207 static void fl_view_renderer_init(FlViewRenderer* self) {
208  GdkRGBA default_background = {
209  .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
210  self->background_color = gdk_rgba_copy(&default_background);
211 }
212 
213 FlViewRenderer* fl_view_renderer_new(FlEngine* engine,
214  gboolean sized_to_content) {
215  g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr);
216 
217  FlViewRenderer* self =
218  FL_VIEW_RENDERER(g_object_new(fl_view_renderer_get_type(), nullptr));
219 
220  self->engine = FL_ENGINE(g_object_ref(engine));
221  self->sized_to_content = sized_to_content;
222 
223  return self;
224 }
225 
226 void fl_view_renderer_set_background_color(FlViewRenderer* self,
227  const GdkRGBA* color) {
228  g_return_if_fail(FL_IS_VIEW_RENDERER(self));
229  gdk_rgba_free(self->background_color);
230  self->background_color = gdk_rgba_copy(color);
231 }
232 
233 void fl_view_renderer_present_layers(FlViewRenderer* self,
234  const FlutterLayer** layers,
235  size_t layers_count) {
236  g_return_if_fail(FL_IS_VIEW_RENDERER(self));
237 
238  // Frames may be presented before the widget is realized and the compositor
239  // is set up; ignore them.
240  if (self->compositor == nullptr) {
241  return;
242  }
243 
245 
246  // Perform the redraw in the GTK thread.
247  g_idle_add(redraw_cb, g_object_ref(self));
248 }
g_autoptr(FlEngine) engine
const char FlTextDirection FlAssertiveness gpointer user_data
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
gboolean fl_compositor_render(FlCompositor *self, cairo_t *cr, GdkWindow *window, gboolean wait_for_frame)
void fl_compositor_get_frame_size(FlCompositor *self, size_t *width, size_t *height)
gboolean fl_compositor_present_layers(FlCompositor *self, const FlutterLayer **layers, size_t layers_count)
FlCompositorOpenGL * fl_compositor_opengl_new(FlTaskRunner *task_runner, FlOpenGLManager *opengl_manager, gboolean shareable)
FlFramebuffer double double int int height
FlFramebuffer double double int width
G_BEGIN_DECLS FlOpenGLManager gboolean shareable
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
return TRUE
FlCompositorSoftware * fl_compositor_software_new(FlTaskRunner *task_runner)
FlOpenGLManager * fl_engine_get_opengl_manager(FlEngine *self)
Definition: fl_engine.cc:749
FlutterRendererType fl_engine_get_renderer_type(FlEngine *self)
Definition: fl_engine.cc:744
FlTaskRunner * fl_engine_get_task_runner(FlEngine *self)
Definition: fl_engine.cc:1528
const uint8_t uint32_t uint32_t GError ** error
static gboolean fl_view_renderer_draw(GtkWidget *widget, cairo_t *cr)
static void fl_view_renderer_dispose(GObject *object)
void fl_view_renderer_set_background_color(FlViewRenderer *self, const GdkRGBA *color)
static void fl_view_renderer_realize(GtkWidget *widget)
static gboolean redraw_cb(gpointer user_data)
static void setup_software(FlViewRenderer *self)
static void fl_view_renderer_init(FlViewRenderer *self)
static void fl_view_renderer_finalize(GObject *object)
static void setup_opengl(FlViewRenderer *self)
@ LAST_SIGNAL
@ SIGNAL_FIRST_FRAME
static void fl_view_renderer_class_init(FlViewRendererClass *klass)
static void paint_background(FlViewRenderer *self, cairo_t *cr)
FlViewRenderer * fl_view_renderer_new(FlEngine *engine, gboolean sized_to_content)
void fl_view_renderer_present_layers(FlViewRenderer *self, const FlutterLayer **layers, size_t layers_count)
static guint fl_view_renderer_signals[LAST_SIGNAL]
G_BEGIN_DECLS gboolean sized_to_content
GdkGLContext * render_context
gboolean have_first_frame
GdkRGBA * background_color
FlCompositor * compositor
gboolean sized_to_content
GtkDrawingArea parent_instance