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"
14 #include "flutter/shell/platform/linux/testing/mock_epoxy.h"
15 #include "flutter/shell/platform/linux/testing/mock_renderable.h"
16 
17 #include <epoxy/egl.h>
18 
19 TEST(FlCompositorOpenGLTest, BackgroundColor) {
20  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
21  g_autoptr(FlDartProject) project = fl_dart_project_new();
22  g_autoptr(FlEngine) engine = fl_engine_new(project);
23 
24  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
25  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
26  ON_CALL(epoxy, glGetString(GL_VENDOR))
27  .WillByDefault(
28  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
29  EXPECT_CALL(epoxy, glClearColor(0.2, 0.3, 0.4, 0.5));
30 
31  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
32  g_autoptr(FlCompositorOpenGL) compositor = fl_compositor_opengl_new(engine);
33  fl_compositor_opengl_setup(compositor);
34  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
35  fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), 1024, 1024);
36  FlutterBackingStoreConfig config = {
37  .struct_size = sizeof(FlutterBackingStoreConfig),
38  .size = {.width = 1024, .height = 1024}};
39  FlutterBackingStore backing_store;
40  fl_compositor_create_backing_store(FL_COMPOSITOR(compositor), &config,
41  &backing_store);
42 
43  fml::AutoResetWaitableEvent latch;
44 
45  // Simulate raster thread.
46  std::thread([&]() {
47  const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer),
48  .type = kFlutterLayerContentTypeBackingStore,
49  .backing_store = &backing_store,
50  .size = {.width = 1024, .height = 1024}};
51  const FlutterLayer* layers[] = {&layer0};
52 
53  fl_compositor_present_layers(FL_COMPOSITOR(compositor),
54  flutter::kFlutterImplicitViewId, layers, 1);
55  latch.Signal();
56  }).detach();
57 
58  while (fl_mock_renderable_get_redraw_count(renderable) == 0) {
59  g_main_context_iteration(nullptr, true);
60  }
61 
62  GdkRGBA background_color = {
63  .red = 0.2, .green = 0.3, .blue = 0.4, .alpha = 0.5};
64  fl_compositor_opengl_render(compositor, flutter::kFlutterImplicitViewId, 1024,
65  1024, &background_color);
66 
67  // Wait until the raster thread has finished before letting
68  // the engine go out of scope.
69  latch.Wait();
70 }
71 
72 TEST(FlCompositorOpenGLTest, RestoresGLState) {
73  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
74  g_autoptr(FlDartProject) project = fl_dart_project_new();
75  g_autoptr(FlEngine) engine = fl_engine_new(project);
76 
77  constexpr int kWidth = 100;
78  constexpr int kHeight = 100;
79 
80  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
81  g_autoptr(FlCompositorOpenGL) compositor = fl_compositor_opengl_new(engine);
82  g_autoptr(FlFramebuffer) framebuffer =
83  fl_framebuffer_new(GL_RGB, kWidth, kHeight);
84 
85  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
86  fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), kWidth, kHeight);
87 
88  FlutterBackingStore backing_store;
89  backing_store.type = kFlutterBackingStoreTypeOpenGL;
90  backing_store.open_gl.framebuffer.user_data = framebuffer;
91 
92  FlutterLayer layer;
93  layer.type = kFlutterLayerContentTypeBackingStore;
94  layer.backing_store = &backing_store;
95  layer.offset = {0, 0};
96  layer.size = {kWidth, kHeight};
97 
98  std::array<const FlutterLayer*, 1> layers = {&layer};
99 
100  constexpr GLuint kFakeTextureName = 123;
101  glBindTexture(GL_TEXTURE_2D, kFakeTextureName);
102 
103  fml::AutoResetWaitableEvent latch;
104 
105  // Simulate raster thread.
106  std::thread([&]() {
107  fl_compositor_present_layers(FL_COMPOSITOR(compositor),
108  flutter::kFlutterImplicitViewId, layers.data(),
109  layers.size());
110  latch.Signal();
111  }).detach();
112 
113  while (fl_mock_renderable_get_redraw_count(renderable) == 0) {
114  g_main_context_iteration(nullptr, true);
115  }
116 
117  GdkRGBA background_color = {
118  .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
119  fl_compositor_opengl_render(compositor, flutter::kFlutterImplicitViewId,
120  kWidth, kHeight, &background_color);
121 
122  GLuint texture_2d_binding;
123  glGetIntegerv(GL_TEXTURE_BINDING_2D,
124  reinterpret_cast<GLint*>(&texture_2d_binding));
125  EXPECT_EQ(texture_2d_binding, kFakeTextureName);
126 
127  // Wait until the raster thread has finished before letting
128  // the engine go out of scope.
129  latch.Wait();
130 }
131 
132 TEST(FlCompositorOpenGLTest, BlitFramebuffer) {
133  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
134  g_autoptr(FlDartProject) project = fl_dart_project_new();
135  g_autoptr(FlEngine) engine = fl_engine_new(project);
136 
137  // OpenGL 3.0
138  ON_CALL(epoxy, glGetString(GL_VENDOR))
139  .WillByDefault(
140  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
141  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
142  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
143 
144  EXPECT_CALL(epoxy, glBlitFramebuffer);
145 
146  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
147  g_autoptr(FlCompositorOpenGL) compositor = fl_compositor_opengl_new(engine);
148  fl_compositor_opengl_setup(compositor);
149  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
150  fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), 1024, 1024);
151  FlutterBackingStoreConfig config = {
152  .struct_size = sizeof(FlutterBackingStoreConfig),
153  .size = {.width = 1024, .height = 1024}};
154  FlutterBackingStore backing_store;
155  fl_compositor_create_backing_store(FL_COMPOSITOR(compositor), &config,
156  &backing_store);
157 
158  fml::AutoResetWaitableEvent latch;
159 
160  // Simulate raster thread.
161  std::thread([&]() {
162  const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer),
163  .type = kFlutterLayerContentTypeBackingStore,
164  .backing_store = &backing_store,
165  .size = {.width = 1024, .height = 1024}};
166  const FlutterLayer* layers[] = {&layer0};
167  fl_compositor_present_layers(FL_COMPOSITOR(compositor),
168  flutter::kFlutterImplicitViewId, layers, 1);
169  latch.Signal();
170  }).detach();
171 
172  while (fl_mock_renderable_get_redraw_count(renderable) == 0) {
173  g_main_context_iteration(nullptr, true);
174  }
175 
176  GdkRGBA background_color = {
177  .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
178  fl_compositor_opengl_render(compositor, flutter::kFlutterImplicitViewId, 1024,
179  1024, &background_color);
180 
181  latch.Wait();
182 }
183 
184 TEST(FlCompositorOpenGLTest, BlitFramebufferExtension) {
185  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
186  g_autoptr(FlDartProject) project = fl_dart_project_new();
187  g_autoptr(FlEngine) engine = fl_engine_new(project);
188 
189  // OpenGL 2.0 with GL_EXT_framebuffer_blit extension
190  ON_CALL(epoxy, glGetString(GL_VENDOR))
191  .WillByDefault(
192  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
193  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
194  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20));
195  EXPECT_CALL(epoxy, epoxy_has_gl_extension(::testing::_))
196  .WillRepeatedly(::testing::Return(false));
197  EXPECT_CALL(epoxy, epoxy_has_gl_extension(
198  ::testing::StrEq("GL_EXT_framebuffer_blit")))
199  .WillRepeatedly(::testing::Return(true));
200 
201  EXPECT_CALL(epoxy, glBlitFramebuffer);
202 
203  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
204  g_autoptr(FlCompositorOpenGL) compositor = fl_compositor_opengl_new(engine);
205  fl_compositor_opengl_setup(compositor);
206  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
207  fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), 1024, 1024);
208  FlutterBackingStoreConfig config = {
209  .struct_size = sizeof(FlutterBackingStoreConfig),
210  .size = {.width = 1024, .height = 1024}};
211  FlutterBackingStore backing_store;
212  fl_compositor_create_backing_store(FL_COMPOSITOR(compositor), &config,
213  &backing_store);
214 
215  fml::AutoResetWaitableEvent latch;
216 
217  // Simulate raster thread.
218  std::thread([&]() {
219  const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer),
220  .type = kFlutterLayerContentTypeBackingStore,
221  .backing_store = &backing_store,
222  .size = {.width = 1024, .height = 1024}};
223  const FlutterLayer* layers[] = {&layer0};
224  fl_compositor_present_layers(FL_COMPOSITOR(compositor),
225  flutter::kFlutterImplicitViewId, layers, 1);
226  latch.Signal();
227  }).detach();
228 
229  while (fl_mock_renderable_get_redraw_count(renderable) == 0) {
230  g_main_context_iteration(nullptr, true);
231  }
232  GdkRGBA background_color = {
233  .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
234  fl_compositor_opengl_render(compositor, flutter::kFlutterImplicitViewId, 1024,
235  1024, &background_color);
236  // Wait until the raster thread has finished before letting
237  // the engine go out of scope.
238  latch.Wait();
239 }
240 
241 TEST(FlCompositorOpenGLTest, NoBlitFramebuffer) {
242  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
243  g_autoptr(FlDartProject) project = fl_dart_project_new();
244  g_autoptr(FlEngine) engine = fl_engine_new(project);
245 
246  // OpenGL 2.0
247  ON_CALL(epoxy, glGetString(GL_VENDOR))
248  .WillByDefault(
249  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
250  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
251  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20));
252 
253  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
254  g_autoptr(FlCompositorOpenGL) compositor = fl_compositor_opengl_new(engine);
255  fl_compositor_opengl_setup(compositor);
256  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
257  fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), 1024, 1024);
258  FlutterBackingStoreConfig config = {
259  .struct_size = sizeof(FlutterBackingStoreConfig),
260  .size = {.width = 1024, .height = 1024}};
261  FlutterBackingStore backing_store;
262  fl_compositor_create_backing_store(FL_COMPOSITOR(compositor), &config,
263  &backing_store);
264 
265  fml::AutoResetWaitableEvent latch;
266 
267  // Simulate raster thread.
268  std::thread([&]() {
269  const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer),
270  .type = kFlutterLayerContentTypeBackingStore,
271  .backing_store = &backing_store,
272  .size = {.width = 1024, .height = 1024}};
273  const FlutterLayer* layers[] = {&layer0};
274  fl_compositor_present_layers(FL_COMPOSITOR(compositor),
275  flutter::kFlutterImplicitViewId, layers, 1);
276  latch.Signal();
277  }).detach();
278 
279  while (fl_mock_renderable_get_redraw_count(renderable) == 0) {
280  g_main_context_iteration(nullptr, true);
281  }
282 
283  GdkRGBA background_color = {
284  .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
285  fl_compositor_opengl_render(compositor, flutter::kFlutterImplicitViewId, 1024,
286  1024, &background_color);
287 
288  // Wait until the raster thread has finished before letting
289  // the engine go out of scope.
290  latch.Wait();
291 }
292 
293 TEST(FlCompositorOpenGLTest, BlitFramebufferNvidia) {
294  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
295  g_autoptr(FlDartProject) project = fl_dart_project_new();
296  g_autoptr(FlEngine) engine = fl_engine_new(project);
297 
298  // OpenGL 3.0, but on NVIDIA driver so temporarily disabled due to
299  // https://github.com/flutter/flutter/issues/152099
300  ON_CALL(epoxy, glGetString(GL_VENDOR))
301  .WillByDefault(
302  ::testing::Return(reinterpret_cast<const GLubyte*>("NVIDIA")));
303  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
304  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
305 
306  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
307  g_autoptr(FlCompositorOpenGL) compositor = fl_compositor_opengl_new(engine);
308  fl_compositor_opengl_setup(compositor);
309  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
310  fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), 1024, 1024);
311  FlutterBackingStoreConfig config = {
312  .struct_size = sizeof(FlutterBackingStoreConfig),
313  .size = {.width = 1024, .height = 1024}};
314  FlutterBackingStore backing_store;
315  fl_compositor_create_backing_store(FL_COMPOSITOR(compositor), &config,
316  &backing_store);
317 
318  fml::AutoResetWaitableEvent latch;
319 
320  // Simulate raster thread.
321  std::thread([&]() {
322  const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer),
323  .type = kFlutterLayerContentTypeBackingStore,
324  .backing_store = &backing_store,
325  .size = {.width = 1024, .height = 1024}};
326  const FlutterLayer* layers[] = {&layer0};
327  fl_compositor_present_layers(FL_COMPOSITOR(compositor),
328  flutter::kFlutterImplicitViewId, layers, 1);
329  latch.Signal();
330  }).detach();
331 
332  while (fl_mock_renderable_get_redraw_count(renderable) == 0) {
333  g_main_context_iteration(nullptr, true);
334  }
335 
336  GdkRGBA background_color = {
337  .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
338  fl_compositor_opengl_render(compositor, flutter::kFlutterImplicitViewId, 1024,
339  1024, &background_color);
340 
341  // Wait until the raster thread has finished before letting
342  // the engine go out of scope.
343  latch.Wait();
344 }
345 
346 TEST(FlCompositorOpenGLTest, MultiView) {
347  ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
348  g_autoptr(FlDartProject) project = fl_dart_project_new();
349  g_autoptr(FlEngine) engine = fl_engine_new(project);
350 
351  // OpenGL 3.0
352  ON_CALL(epoxy, glGetString(GL_VENDOR))
353  .WillByDefault(
354  ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
355  ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
356  EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
357 
358  g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
359  g_autoptr(FlMockRenderable) secondary_renderable = fl_mock_renderable_new();
360 
361  g_autoptr(FlCompositorOpenGL) compositor = fl_compositor_opengl_new(engine);
362  fl_compositor_opengl_setup(compositor);
363  fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
364  FlutterViewId view_id =
365  fl_engine_add_view(engine, FL_RENDERABLE(secondary_renderable), 1024, 768,
366  1.0, nullptr, nullptr, nullptr);
367  fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), 1024, 1024);
368 
369  EXPECT_EQ(fl_mock_renderable_get_redraw_count(renderable),
370  static_cast<size_t>(0));
371  EXPECT_EQ(fl_mock_renderable_get_redraw_count(secondary_renderable),
372  static_cast<size_t>(0));
373 
374  FlutterBackingStoreConfig config = {
375  .struct_size = sizeof(FlutterBackingStoreConfig),
376  .size = {.width = 1024, .height = 1024}};
377  FlutterBackingStore backing_store;
378  fl_compositor_create_backing_store(FL_COMPOSITOR(compositor), &config,
379  &backing_store);
380 
381  fml::AutoResetWaitableEvent latch;
382 
383  // Simulate raster thread.
384  std::thread([&]() {
385  const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer),
386  .type = kFlutterLayerContentTypeBackingStore,
387  .backing_store = &backing_store,
388  .size = {.width = 1024, .height = 1024}};
389  const FlutterLayer* layers[] = {&layer0};
390  fl_compositor_present_layers(FL_COMPOSITOR(compositor), view_id, layers, 1);
391  latch.Signal();
392  }).detach();
393 
394  while (fl_mock_renderable_get_redraw_count(secondary_renderable) == 0) {
395  g_main_context_iteration(nullptr, true);
396  }
397 
398  EXPECT_EQ(fl_mock_renderable_get_redraw_count(renderable),
399  static_cast<size_t>(0));
400  EXPECT_EQ(fl_mock_renderable_get_redraw_count(secondary_renderable),
401  static_cast<size_t>(1));
402 
403  // Wait until the raster thread has finished before letting
404  // the engine go out of scope.
405  latch.Wait();
406 }
gboolean fl_compositor_present_layers(FlCompositor *self, FlutterViewId view_id, const FlutterLayer **layers, size_t layers_count)
void fl_compositor_wait_for_frame(FlCompositor *self, int target_width, int target_height)
gboolean fl_compositor_create_backing_store(FlCompositor *self, const FlutterBackingStoreConfig *config, FlutterBackingStore *backing_store_out)
void fl_compositor_opengl_setup(FlCompositorOpenGL *self)
FlCompositorOpenGL * fl_compositor_opengl_new(FlEngine *engine)
void fl_compositor_opengl_render(FlCompositorOpenGL *self, FlutterViewId view_id, int width, int height, const GdkRGBA *background_color)
TEST(FlCompositorOpenGLTest, BackgroundColor)
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
void fl_engine_set_implicit_view(FlEngine *self, FlRenderable *renderable)
Definition: fl_engine.cc:743
FlutterViewId fl_engine_add_view(FlEngine *self, FlRenderable *renderable, size_t width, size_t height, double pixel_ratio, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_engine.cc:750
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition: fl_engine.cc:584
FlFramebuffer * fl_framebuffer_new(GLint format, size_t width, size_t height)
G_BEGIN_DECLS FlutterViewId view_id