Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
fl_view_renderer_software.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
10
11// Maximum time to wait for a frame to be ready before giving up and rendering.
12static constexpr gint64 kRenderTimeoutMicroseconds = 100000; // 100ms
13
15 FlViewRenderer parent_instance;
16
17 // Engine this widget is rendering.
18 FlEngine* engine;
19
20 // TRUE if the view size should be controlled by Flutter.
22
23 // Combines and stores frames.
24 FlCompositorSoftware* compositor;
25
26 // Task runner to wait for frames on.
27 FlTaskRunner* task_runner;
28
29 // Ensure Flutter and GTK can access the frame stored in the compositor.
31};
32
33G_DEFINE_TYPE(FlViewRendererSoftware,
34 fl_view_renderer_software,
35 fl_view_renderer_get_type())
36
37// Redraw the view from the GTK thread.
38static gboolean redraw_cb(gpointer user_data) {
39 g_autoptr(FlViewRendererSoftware) self = FL_VIEW_RENDERER_SOFTWARE(user_data);
40
41 if (self->compositor == nullptr) {
42 return G_SOURCE_REMOVE;
43 }
44
45 fl_view_renderer_notify_frame(FL_VIEW_RENDERER(self));
46
47 // If Flutter is controlling the window size, then resize the view if
48 // necessary.
49 GtkWidget* render_widget = GTK_WIDGET(self);
50 GtkAllocation allocation;
51 gtk_widget_get_allocation(render_widget, &allocation);
52 gint scale_factor = gtk_widget_get_scale_factor(render_widget);
53 size_t width = allocation.width * scale_factor;
54 size_t height = allocation.height * scale_factor;
55 size_t frame_width, frame_height;
56 {
57 g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
58 fl_compositor_software_get_frame_size(self->compositor, &frame_width,
59 &frame_height);
60 }
61 gboolean frame_size_matches = width == frame_width && height == frame_height;
62 if (self->sized_to_content && !frame_size_matches) {
63 gtk_widget_set_size_request(render_widget, frame_width / scale_factor,
64 frame_height / scale_factor);
65 GtkWidget* toplevel = gtk_widget_get_toplevel(render_widget);
66 if (GTK_IS_WINDOW(toplevel)) {
67 // Resize to smallest size, so that the window will shrink to fit the new
68 // size of the render area.
69 gtk_window_resize(GTK_WINDOW(toplevel), 1, 1);
70 }
71 return G_SOURCE_REMOVE;
72 }
73
74 gtk_widget_queue_draw(render_widget);
75
76 return G_SOURCE_REMOVE;
77}
78
79// Wait for a frame matching the window size to be ready, or until the timeout
80// expires. Must be called with the frame mutex held; the mutex is still held
81// when this function returns.
82static void wait_for_frame(FlViewRendererSoftware* self,
83 GdkWindow* window,
84 gint scale_factor) {
85 gint64 expiry_time = g_get_monotonic_time() + kRenderTimeoutMicroseconds;
86 while (true) {
87 size_t width = gdk_window_get_width(window) * scale_factor;
88 size_t height = gdk_window_get_height(window) * scale_factor;
89 size_t frame_width, frame_height;
90 fl_compositor_software_get_frame_size(self->compositor, &frame_width,
91 &frame_height);
92 if (frame_width == width && frame_height == height) {
93 break;
94 }
95
96 if (g_get_monotonic_time() > expiry_time) {
97 g_warning(
98 "Timed out waiting for software frame of size %zdx%zd (have "
99 "%zdx%zd)",
100 width, height, frame_width, frame_height);
101 break;
102 }
103
104 g_mutex_unlock(&self->frame_mutex);
105 fl_task_runner_wait(self->task_runner, expiry_time);
106 g_mutex_lock(&self->frame_mutex);
107 }
108}
109
110// Implements GtkWidget::realize.
111static void fl_view_renderer_software_realize(GtkWidget* widget) {
112 FlViewRendererSoftware* self = FL_VIEW_RENDERER_SOFTWARE(widget);
113
114 GTK_WIDGET_CLASS(fl_view_renderer_software_parent_class)->realize(widget);
115
116 self->task_runner =
117 FL_TASK_RUNNER(g_object_ref(fl_engine_get_task_runner(self->engine)));
118 self->compositor = fl_compositor_software_new();
119}
120
121// Implements GtkWidget::draw.
122static gboolean fl_view_renderer_software_draw(GtkWidget* widget, cairo_t* cr) {
123 FlViewRendererSoftware* self = FL_VIEW_RENDERER_SOFTWARE(widget);
124
125 fl_view_renderer_paint_background(FL_VIEW_RENDERER(self), cr);
126
127 // The compositor is created when the widget is realized; if it is not yet
128 // available there is nothing to render beyond the background.
129 if (self->compositor == nullptr) {
130 return TRUE;
131 }
132
133 GdkWindow* window = gtk_widget_get_window(widget);
134 gint scale_factor = gdk_window_get_scale_factor(window);
135
136 g_mutex_lock(&self->frame_mutex);
137
138 // If frame not ready, then wait for it.
139 if (!self->sized_to_content) {
140 wait_for_frame(self, window, scale_factor);
141 }
142
143 gboolean result =
144 fl_compositor_software_render(self->compositor, cr, scale_factor);
145
146 g_mutex_unlock(&self->frame_mutex);
147
148 return result;
149}
150
151// Implements FlViewRenderer::present_layers.
153 FlViewRenderer* renderer,
154 const FlutterLayer** layers,
155 size_t layers_count) {
156 FlViewRendererSoftware* self = FL_VIEW_RENDERER_SOFTWARE(renderer);
157
158 // Frames may be presented before the widget is realized and the compositor
159 // is set up; ignore them.
160 if (self->compositor == nullptr) {
161 return;
162 }
163
164 g_mutex_lock(&self->frame_mutex);
167 g_mutex_unlock(&self->frame_mutex);
168
169 // Wake up the GTK thread if it is waiting for this frame.
170 fl_task_runner_stop_wait(self->task_runner);
171
172 // Perform the redraw in the GTK thread.
173 g_idle_add(redraw_cb, g_object_ref(self));
174}
175
176static void fl_view_renderer_software_dispose(GObject* object) {
177 FlViewRendererSoftware* self = FL_VIEW_RENDERER_SOFTWARE(object);
178
179 g_clear_object(&self->engine);
180 g_clear_object(&self->task_runner);
181 g_mutex_clear(&self->frame_mutex);
182
183 G_OBJECT_CLASS(fl_view_renderer_software_parent_class)->dispose(object);
184}
185
186static void fl_view_renderer_software_finalize(GObject* object) {
187 FlViewRendererSoftware* self = FL_VIEW_RENDERER_SOFTWARE(object);
188
189 // The compositor is released here rather than in dispose() so it outlives a
190 // forced dispose (e.g. gtk_widget_destroy()) and is only freed once the last
191 // reference is dropped. This keeps it alive for the raster thread, which
192 // holds a strong reference on the view (and thus this renderer) while
193 // presenting.
194 g_clear_object(&self->compositor);
195
196 G_OBJECT_CLASS(fl_view_renderer_software_parent_class)->finalize(object);
197}
198
200 FlViewRendererSoftwareClass* klass) {
201 GObjectClass* object_class = G_OBJECT_CLASS(klass);
202 object_class->dispose = fl_view_renderer_software_dispose;
203 object_class->finalize = fl_view_renderer_software_finalize;
204
205 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
206 widget_class->realize = fl_view_renderer_software_realize;
207 widget_class->draw = fl_view_renderer_software_draw;
208
209 FlViewRendererClass* renderer_class = FL_VIEW_RENDERER_CLASS(klass);
210 renderer_class->present_layers = fl_view_renderer_software_present_layers;
211}
212
213static void fl_view_renderer_software_init(FlViewRendererSoftware* self) {
214 g_mutex_init(&self->frame_mutex);
215}
216
217FlViewRendererSoftware* fl_view_renderer_software_new(
218 FlEngine* engine,
219 gboolean sized_to_content) {
220 g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr);
221
222 FlViewRendererSoftware* self = FL_VIEW_RENDERER_SOFTWARE(
223 g_object_new(fl_view_renderer_software_get_type(), nullptr));
224 self->engine = FL_ENGINE(g_object_ref(engine));
225 self->sized_to_content = sized_to_content;
226 return self;
227}
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
g_autoptr(FlEngine) engine
gboolean fl_compositor_software_render(FlCompositorSoftware *self, cairo_t *cr, gint scale_factor)
FlCompositorSoftware * fl_compositor_software_new()
void fl_compositor_software_get_frame_size(FlCompositorSoftware *self, size_t *width, size_t *height)
gboolean fl_compositor_software_composite_layers(FlCompositorSoftware *self, const FlutterLayer **layers, size_t layers_count)
FlTaskRunner * fl_engine_get_task_runner(FlEngine *self)
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)
G_BEGIN_DECLS gboolean sized_to_content
static gboolean fl_view_renderer_software_draw(GtkWidget *widget, cairo_t *cr)
FlViewRendererSoftware * fl_view_renderer_software_new(FlEngine *engine, gboolean sized_to_content)
static void wait_for_frame(FlViewRendererSoftware *self, GdkWindow *window, gint scale_factor)
static void fl_view_renderer_software_class_init(FlViewRendererSoftwareClass *klass)
static void fl_view_renderer_software_realize(GtkWidget *widget)
static void fl_view_renderer_software_finalize(GObject *object)
static void fl_view_renderer_software_present_layers(FlViewRenderer *renderer, const FlutterLayer **layers, size_t layers_count)
static constexpr gint64 kRenderTimeoutMicroseconds
G_DEFINE_TYPE(FlViewRendererSoftware, fl_view_renderer_software, fl_view_renderer_get_type()) static gboolean redraw_cb(gpointer user_data)
static void fl_view_renderer_software_dispose(GObject *object)
static void fl_view_renderer_software_init(FlViewRendererSoftware *self)
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
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
int gdk_window_get_height(GdkWindow *window)
Definition mock_gtk.cc:87
GtkWidget * gtk_widget_get_toplevel(GtkWidget *widget)
Definition mock_gtk.cc:304
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
FlCompositorSoftware * compositor