Flutter Linux Embedder
fl_compositor_opengl_test.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 
5 #include <thread>
6 #include "gtest/gtest.h"
7 
8 #include "flutter/common/constants.h"
9 #include "flutter/fml/logging.h"
10 #include "flutter/fml/synchronization/waitable_event.h"
16 #include "flutter/shell/platform/linux/testing/mock_epoxy.h"
17 #include "flutter/shell/platform/linux/testing/mock_renderable.h"
18 
19 #include <epoxy/egl.h>
20 
21 TEST(FlCompositorOpenGLTest, Render) {
22  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
23  g_autoptr(FlDartProject) project = fl_dart_project_new();
24  g_autoptr(FlEngine) engine = fl_engine_new(project);
25  g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
26  g_autoptr(FlOpenGLManager) opengl_manager = fl_opengl_manager_new();
27 
28  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
29  g_autoptr(FlCompositorOpenGL) compositor =
30  fl_compositor_opengl_new(task_runner, opengl_manager, FALSE);
31  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
32 
33  // Present layer from a thread.
34  constexpr size_t width = 100;
35  constexpr size_t height = 100;
36  g_autoptr(FlFramebuffer) framebuffer =
37  fl_framebuffer_new(GL_RGB, width, height, FALSE);
38  FlutterBackingStore backing_store = {
39  .type = kFlutterBackingStoreTypeOpenGL,
40  .open_gl = {.framebuffer = {.user_data = framebuffer}}};
41  FlutterLayer layer = {.type = kFlutterLayerContentTypeBackingStore,
42  .backing_store = &backing_store,
43  .offset = {0, 0},
44  .size = {width, height}};
45  const FlutterLayer* layers[1] = {&layer};
46  std::thread([&]() {
47  fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
48  }).join();
49 
50  // Render presented layer.
51  int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
52  g_autofree unsigned char* image_data =
53  static_cast<unsigned char*>(malloc(height * stride));
54  cairo_surface_t* surface = cairo_image_surface_create_for_data(
55  image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
56  cairo_t* cr = cairo_create(surface);
57  fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr);
58  cairo_surface_destroy(surface);
59  cairo_destroy(cr);
60 }
61 
62 TEST(FlCompositorOpenGLTest, Resize) {
63  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
64  g_autoptr(FlDartProject) project = fl_dart_project_new();
65  g_autoptr(FlEngine) engine = fl_engine_new(project);
66  g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
67  g_autoptr(FlOpenGLManager) opengl_manager = fl_opengl_manager_new();
68 
69  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
70  g_autoptr(FlCompositorOpenGL) compositor =
71  fl_compositor_opengl_new(task_runner, opengl_manager, FALSE);
72  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
73 
74  // Present a layer that is the old size.
75  constexpr size_t width1 = 90;
76  constexpr size_t height1 = 90;
77  g_autoptr(FlFramebuffer) framebuffer1 =
78  fl_framebuffer_new(GL_RGB, width1, height1, FALSE);
79  FlutterBackingStore backing_store1 = {
80  .type = kFlutterBackingStoreTypeOpenGL,
81  .open_gl = {.framebuffer = {.user_data = framebuffer1}}};
82  FlutterLayer layer1 = {.type = kFlutterLayerContentTypeBackingStore,
83  .backing_store = &backing_store1,
84  .offset = {0, 0},
85  .size = {width1, height1}};
86  const FlutterLayer* layers1[1] = {&layer1};
87  std::thread([&]() {
88  fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers1, 1);
89  }).join();
90 
91  // Present layer in current size.
92  constexpr size_t width2 = 100;
93  constexpr size_t height2 = 100;
94  g_autoptr(FlFramebuffer) framebuffer2 =
95  fl_framebuffer_new(GL_RGB, width2, height2, FALSE);
96  FlutterBackingStore backing_store2 = {
97  .type = kFlutterBackingStoreTypeOpenGL,
98  .open_gl = {.framebuffer = {.user_data = framebuffer2}}};
99  FlutterLayer layer2 = {.type = kFlutterLayerContentTypeBackingStore,
100  .backing_store = &backing_store2,
101  .offset = {0, 0},
102  .size = {width2, height2}};
103  const FlutterLayer* layers2[1] = {&layer2};
104  fml::AutoResetWaitableEvent latch;
105  std::thread([&]() {
106  fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers2, 1);
107  latch.Signal();
108  }).detach();
109 
110  // Render, will wait for the second layer if necessary.
111  int stride2 = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width2);
112  g_autofree unsigned char* image_data =
113  static_cast<unsigned char*>(malloc(height2 * stride2));
114  cairo_surface_t* surface = cairo_image_surface_create_for_data(
115  image_data, CAIRO_FORMAT_ARGB32, width2, height2, stride2);
116  cairo_t* cr = cairo_create(surface);
117  fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr);
118  cairo_surface_destroy(surface);
119  cairo_destroy(cr);
120 
121  latch.Wait();
122 }
123 
124 TEST(FlCompositorOpenGLTest, RestoresGLState) {
125  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
126  g_autoptr(FlDartProject) project = fl_dart_project_new();
127  g_autoptr(FlEngine) engine = fl_engine_new(project);
128  g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
129  g_autoptr(FlOpenGLManager) opengl_manager = fl_opengl_manager_new();
130 
131  constexpr size_t width = 100;
132  constexpr size_t height = 100;
133 
134  // OpenGL 3.0
135  ON_CALL(epoxy, glGetString(GL_VENDOR))
136  .WillByDefault(
137  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
138  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
139  ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(30));
140 
141  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
142  g_autoptr(FlCompositorOpenGL) compositor =
143  fl_compositor_opengl_new(task_runner, opengl_manager, FALSE);
144  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
145 
146  g_autoptr(FlFramebuffer) framebuffer =
147  fl_framebuffer_new(GL_RGB, width, height, FALSE);
148  FlutterBackingStore backing_store = {
149  .type = kFlutterBackingStoreTypeOpenGL,
150  .open_gl = {.framebuffer = {.user_data = framebuffer}}};
151  FlutterLayer layer = {.type = kFlutterLayerContentTypeBackingStore,
152  .backing_store = &backing_store,
153  .offset = {0, 0},
154  .size = {width, height}};
155  const FlutterLayer* layers[1] = {&layer};
156 
157  constexpr GLuint kFakeTextureName = 123;
158  glBindTexture(GL_TEXTURE_2D, kFakeTextureName);
159 
160  // Present layer and render.
161  std::thread([&]() {
162  fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
163  }).join();
164  int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
165  g_autofree unsigned char* image_data =
166  static_cast<unsigned char*>(malloc(height * stride));
167  cairo_surface_t* surface = cairo_image_surface_create_for_data(
168  image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
169  cairo_t* cr = cairo_create(surface);
170  fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr);
171  cairo_surface_destroy(surface);
172  cairo_destroy(cr);
173 
174  GLuint texture_2d_binding;
175  glGetIntegerv(GL_TEXTURE_BINDING_2D,
176  reinterpret_cast<GLint*>(&texture_2d_binding));
177  EXPECT_EQ(texture_2d_binding, kFakeTextureName);
178 }
179 
180 TEST(FlCompositorOpenGLTest, BlitFramebuffer) {
181  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
182  g_autoptr(FlDartProject) project = fl_dart_project_new();
183  g_autoptr(FlEngine) engine = fl_engine_new(project);
184  g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
185  g_autoptr(FlOpenGLManager) opengl_manager = fl_opengl_manager_new();
186 
187  constexpr size_t width = 100;
188  constexpr size_t height = 100;
189 
190  // OpenGL 3.0
191  ON_CALL(epoxy, glGetString(GL_VENDOR))
192  .WillByDefault(
193  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
194  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
195  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
196 
197  EXPECT_CALL(epoxy, glBlitFramebuffer);
198 
199  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
200  g_autoptr(FlCompositorOpenGL) compositor =
201  fl_compositor_opengl_new(task_runner, opengl_manager, FALSE);
202  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
203 
204  g_autoptr(FlFramebuffer) framebuffer =
205  fl_framebuffer_new(GL_RGB, width, height, FALSE);
206  FlutterBackingStore backing_store = {
207  .type = kFlutterBackingStoreTypeOpenGL,
208  .open_gl = {.framebuffer = {.user_data = framebuffer}}};
209  FlutterLayer layer = {.type = kFlutterLayerContentTypeBackingStore,
210  .backing_store = &backing_store,
211  .offset = {0, 0},
212  .size = {width, height}};
213  const FlutterLayer* layers[1] = {&layer};
214 
215  // Present layer and render.
216  std::thread([&]() {
217  fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
218  }).join();
219  int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
220  g_autofree unsigned char* image_data =
221  static_cast<unsigned char*>(malloc(height * stride));
222  cairo_surface_t* surface = cairo_image_surface_create_for_data(
223  image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
224  cairo_t* cr = cairo_create(surface);
225  fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr);
226  cairo_surface_destroy(surface);
227  cairo_destroy(cr);
228 }
229 
230 TEST(FlCompositorOpenGLTest, BlitFramebufferExtension) {
231  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
232  g_autoptr(FlDartProject) project = fl_dart_project_new();
233  g_autoptr(FlEngine) engine = fl_engine_new(project);
234  g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
235  g_autoptr(FlOpenGLManager) opengl_manager = fl_opengl_manager_new();
236 
237  constexpr size_t width = 100;
238  constexpr size_t height = 100;
239 
240  // OpenGL 2.0 with GL_EXT_framebuffer_blit extension
241  ON_CALL(epoxy, glGetString(GL_VENDOR))
242  .WillByDefault(
243  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
244  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
245  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20));
246  EXPECT_CALL(epoxy, epoxy_has_gl_extension(::testing::_))
247  .WillRepeatedly(::testing::Return(false));
248  EXPECT_CALL(epoxy, epoxy_has_gl_extension(
249  ::testing::StrEq("GL_EXT_framebuffer_blit")))
250  .WillRepeatedly(::testing::Return(true));
251 
252  EXPECT_CALL(epoxy, glBlitFramebuffer);
253 
254  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
255  g_autoptr(FlCompositorOpenGL) compositor =
256  fl_compositor_opengl_new(task_runner, opengl_manager, FALSE);
257  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
258 
259  g_autoptr(FlFramebuffer) framebuffer =
260  fl_framebuffer_new(GL_RGB, width, height, FALSE);
261  FlutterBackingStore backing_store = {
262  .type = kFlutterBackingStoreTypeOpenGL,
263  .open_gl = {.framebuffer = {.user_data = framebuffer}}};
264  FlutterLayer layer = {.type = kFlutterLayerContentTypeBackingStore,
265  .backing_store = &backing_store,
266  .offset = {0, 0},
267  .size = {width, height}};
268  const FlutterLayer* layers[1] = {&layer};
269 
270  // Present layer and render.
271  std::thread([&]() {
272  fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
273  }).join();
274  int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
275  g_autofree unsigned char* image_data =
276  static_cast<unsigned char*>(malloc(height * stride));
277  cairo_surface_t* surface = cairo_image_surface_create_for_data(
278  image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
279  cairo_t* cr = cairo_create(surface);
280  fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr);
281  cairo_surface_destroy(surface);
282  cairo_destroy(cr);
283 }
284 
285 TEST(FlCompositorOpenGLTest, NoBlitFramebuffer) {
286  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
287  g_autoptr(FlDartProject) project = fl_dart_project_new();
288  g_autoptr(FlEngine) engine = fl_engine_new(project);
289  g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
290  g_autoptr(FlOpenGLManager) opengl_manager = fl_opengl_manager_new();
291 
292  constexpr size_t width = 100;
293  constexpr size_t height = 100;
294 
295  // OpenGL 2.0
296  ON_CALL(epoxy, glGetString(GL_VENDOR))
297  .WillByDefault(
298  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
299  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
300  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20));
301 
302  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
303  g_autoptr(FlCompositorOpenGL) compositor =
304  fl_compositor_opengl_new(task_runner, opengl_manager, FALSE);
305  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
306 
307  g_autoptr(FlFramebuffer) framebuffer =
308  fl_framebuffer_new(GL_RGB, width, height, FALSE);
309  FlutterBackingStore backing_store = {
310  .type = kFlutterBackingStoreTypeOpenGL,
311  .open_gl = {.framebuffer = {.user_data = framebuffer}}};
312  FlutterLayer layer = {.type = kFlutterLayerContentTypeBackingStore,
313  .backing_store = &backing_store,
314  .offset = {0, 0},
315  .size = {width, height}};
316  const FlutterLayer* layers[1] = {&layer};
317 
318  // Present layer and render.
319  std::thread([&]() {
320  fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
321  }).join();
322  int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
323  g_autofree unsigned char* image_data =
324  static_cast<unsigned char*>(malloc(height * stride));
325  cairo_surface_t* surface = cairo_image_surface_create_for_data(
326  image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
327  cairo_t* cr = cairo_create(surface);
328  fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr);
329  cairo_surface_destroy(surface);
330  cairo_destroy(cr);
331 }
332 
333 TEST(FlCompositorOpenGLTest, BlitFramebufferNvidia) {
334  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
335  g_autoptr(FlDartProject) project = fl_dart_project_new();
336  g_autoptr(FlEngine) engine = fl_engine_new(project);
337  g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
338  g_autoptr(FlOpenGLManager) opengl_manager = fl_opengl_manager_new();
339 
340  constexpr size_t width = 100;
341  constexpr size_t height = 100;
342 
343  // OpenGL 3.0, but on NVIDIA driver so temporarily disabled due to
344  // https://github.com/flutter/flutter/issues/152099
345  ON_CALL(epoxy, glGetString(GL_VENDOR))
346  .WillByDefault(
347  ::testing::Return(reinterpret_cast<const GLubyte*>("NVIDIA")));
348  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
349  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
350 
351  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
352  g_autoptr(FlCompositorOpenGL) compositor =
353  fl_compositor_opengl_new(task_runner, opengl_manager, FALSE);
354  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
355 
356  g_autoptr(FlFramebuffer) framebuffer =
357  fl_framebuffer_new(GL_RGB, width, height, FALSE);
358  FlutterBackingStore backing_store = {
359  .type = kFlutterBackingStoreTypeOpenGL,
360  .open_gl = {.framebuffer = {.user_data = framebuffer}}};
361  FlutterLayer layer = {.type = kFlutterLayerContentTypeBackingStore,
362  .backing_store = &backing_store,
363  .offset = {0, 0},
364  .size = {width, height}};
365  const FlutterLayer* layers[1] = {&layer};
366 
367  // Present layer and render.
368  std::thread([&]() {
369  fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
370  }).join();
371  int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
372  g_autofree unsigned char* image_data =
373  static_cast<unsigned char*>(malloc(height * stride));
374  cairo_surface_t* surface = cairo_image_surface_create_for_data(
375  image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
376  cairo_t* cr = cairo_create(surface);
377  fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr);
378  cairo_surface_destroy(surface);
379  cairo_destroy(cr);
380 }
gboolean fl_compositor_render(FlCompositor *self, cairo_t *cr, GdkWindow *window)
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)
G_BEGIN_DECLS FlOpenGLManager * opengl_manager
TEST(FlCompositorOpenGLTest, Render)
const FlutterLayer ** layers
self height
g_autoptr(GMutexLocker) locker
self width
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
void fl_engine_set_implicit_view(FlEngine *self, FlRenderable *renderable)
Definition: fl_engine.cc:881
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition: fl_engine.cc:697
FlFramebuffer * fl_framebuffer_new(GLint format, size_t width, size_t height, gboolean shareable)
FlOpenGLManager * fl_opengl_manager_new()
FlTaskRunner * fl_task_runner_new(FlEngine *engine)