Flutter Linux Embedder
fl_compositor_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 
8  FlCompositor parent_instance;
9 
10  // Task runner to wait for frames on.
11  FlTaskRunner* task_runner;
12 
13  // Width of frame in pixels.
14  size_t width;
15 
16  // Height of frame in pixels.
17  size_t height;
18 
19  // Surface to draw on view.
20  cairo_surface_t* surface;
21 
22  // Ensure Flutter and GTK can access the surface.
23  GMutex frame_mutex;
24 };
25 
26 G_DEFINE_TYPE(FlCompositorSoftware,
27  fl_compositor_software,
28  fl_compositor_get_type())
29 
30 static gboolean fl_compositor_software_present_layers(
31  FlCompositor* compositor,
32  const FlutterLayer** layers,
33  size_t layers_count) {
34  FlCompositorSoftware* self = FL_COMPOSITOR_SOFTWARE(compositor);
35 
36  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
37 
38  if (layers_count == 0) {
39  return TRUE;
40  }
41 
42  self->width = layers[0]->size.width;
43  self->height = layers[0]->size.height;
44 
45  // TODO(robert-ancell): Support multiple layers
46  if (layers_count == 1) {
47  const FlutterLayer* layer = layers[0];
48  g_assert(layer->type == kFlutterLayerContentTypeBackingStore);
49  g_assert(layer->backing_store->type == kFlutterBackingStoreTypeSoftware);
50  const FlutterBackingStore* backing_store = layer->backing_store;
51 
52  size_t allocation_length =
53  backing_store->software.row_bytes * backing_store->software.height;
54  unsigned char* old_data = self->surface != nullptr
55  ? cairo_image_surface_get_data(self->surface)
56  : nullptr;
57  unsigned char* data =
58  static_cast<unsigned char*>(realloc(old_data, allocation_length));
59  memcpy(data, backing_store->software.allocation, allocation_length);
60  cairo_surface_destroy(self->surface);
61  self->surface = cairo_image_surface_create_for_data(
62  data, CAIRO_FORMAT_ARGB32, backing_store->software.row_bytes / 4,
63  backing_store->software.height, backing_store->software.row_bytes);
64  }
65 
66  fl_task_runner_stop_wait(self->task_runner);
67 
68  return TRUE;
69 }
70 
71 static void fl_compositor_software_get_frame_size(FlCompositor* compositor,
72  size_t* width,
73  size_t* height) {
74  FlCompositorSoftware* self = FL_COMPOSITOR_SOFTWARE(compositor);
75 
76  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
77 
78  if (width != nullptr) {
79  *width = self->width;
80  }
81  if (height != nullptr) {
82  *height = self->height;
83  }
84 }
85 
86 static gboolean fl_compositor_software_render(FlCompositor* compositor,
87  cairo_t* cr,
88  GdkWindow* window,
89  gboolean wait_for_frame) {
90  FlCompositorSoftware* self = FL_COMPOSITOR_SOFTWARE(compositor);
91 
92  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
93 
94  if (self->surface == nullptr) {
95  return FALSE;
96  }
97 
98  // If frame not ready, then wait for it.
99  gint scale_factor = gdk_window_get_scale_factor(window);
100  if (wait_for_frame) {
101  gint64 expiry_time =
102  g_get_monotonic_time() + kCompositorRenderTimeoutMicroseconds;
103  while (true) {
104  size_t width = gdk_window_get_width(window) * scale_factor;
105  size_t height = gdk_window_get_height(window) * scale_factor;
106  if (self->width == width && self->height == height) {
107  break;
108  }
109 
110  if (g_get_monotonic_time() > expiry_time) {
111  g_warning(
112  "Timed out waiting for software frame of size %zdx%zd (have "
113  "%zdx%zd)",
114  width, height, self->width, self->height);
115  break;
116  }
117 
118  g_mutex_unlock(&self->frame_mutex);
119  fl_task_runner_wait(self->task_runner, expiry_time);
120  g_mutex_lock(&self->frame_mutex);
121  }
122  }
123 
124  cairo_surface_set_device_scale(self->surface, scale_factor, scale_factor);
125  cairo_set_source_surface(cr, self->surface, 0.0, 0.0);
126  cairo_paint(cr);
127 
128  return TRUE;
129 }
130 
131 static void fl_compositor_software_dispose(GObject* object) {
132  FlCompositorSoftware* self = FL_COMPOSITOR_SOFTWARE(object);
133 
134  g_clear_object(&self->task_runner);
135  if (self->surface != nullptr) {
136  free(cairo_image_surface_get_data(self->surface));
137  }
138  g_clear_pointer(&self->surface, cairo_surface_destroy);
139  g_mutex_clear(&self->frame_mutex);
140 
141  G_OBJECT_CLASS(fl_compositor_software_parent_class)->dispose(object);
142 }
143 
145  FlCompositorSoftwareClass* klass) {
146  FL_COMPOSITOR_CLASS(klass)->present_layers =
147  fl_compositor_software_present_layers;
148  FL_COMPOSITOR_CLASS(klass)->get_frame_size =
150  FL_COMPOSITOR_CLASS(klass)->render = fl_compositor_software_render;
151 
152  G_OBJECT_CLASS(klass)->dispose = fl_compositor_software_dispose;
153 }
154 
155 static void fl_compositor_software_init(FlCompositorSoftware* self) {
156  g_mutex_init(&self->frame_mutex);
157 }
158 
159 FlCompositorSoftware* fl_compositor_software_new(FlTaskRunner* task_runner) {
160  FlCompositorSoftware* self = FL_COMPOSITOR_SOFTWARE(
161  g_object_new(fl_compositor_software_get_type(), nullptr));
162  self->task_runner = FL_TASK_RUNNER(g_object_ref(task_runner));
163  return self;
164 }
constexpr G_BEGIN_DECLS gint64 kCompositorRenderTimeoutMicroseconds
Definition: fl_compositor.h:16
const FlutterLayer size_t layers_count
const FlutterLayer ** layers
self height
g_autoptr(GMutexLocker) locker
self width
static void fl_compositor_software_class_init(FlCompositorSoftwareClass *klass)
return TRUE
G_DEFINE_TYPE(FlCompositorSoftware, fl_compositor_software, fl_compositor_get_type()) static gboolean fl_compositor_software_present_layers(FlCompositor *compositor
static void fl_compositor_software_init(FlCompositorSoftware *self)
fl_task_runner_stop_wait(self->task_runner)
static void fl_compositor_software_get_frame_size(FlCompositor *compositor, size_t *width, size_t *height)
FlCompositorSoftware * fl_compositor_software_new(FlTaskRunner *task_runner)
static void fl_compositor_software_dispose(GObject *object)
static gboolean fl_compositor_software_render(FlCompositor *compositor, cairo_t *cr, GdkWindow *window, gboolean wait_for_frame)
void fl_task_runner_wait(FlTaskRunner *self, gint64 expiry_time)