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