| 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"
11 #include "flutter/shell/platform/linux/fl_compositor_opengl.h"
12 #include "flutter/shell/platform/linux/fl_engine_private.h"
13 #include "flutter/shell/platform/linux/fl_framebuffer.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 }
fl_compositor_present_layers
gboolean fl_compositor_present_layers(FlCompositor *self, FlutterViewId view_id, const FlutterLayer **layers, size_t layers_count)
Definition: fl_compositor.cc:40
fl_compositor_wait_for_frame
void fl_compositor_wait_for_frame(FlCompositor *self, int target_width, int target_height)
Definition: fl_compositor.cc:32
fl_compositor_create_backing_store
gboolean fl_compositor_create_backing_store(FlCompositor *self, const FlutterBackingStoreConfig *config, FlutterBackingStore *backing_store_out)
Definition: fl_compositor.cc:15
fl_compositor_opengl_setup
void fl_compositor_opengl_setup(FlCompositorOpenGL *self)
Definition: fl_compositor_opengl.cc:582
fl_compositor_opengl_new
FlCompositorOpenGL * fl_compositor_opengl_new(FlEngine *engine)
Definition: fl_compositor_opengl.cc:571
fl_compositor_opengl_render
void fl_compositor_opengl_render(FlCompositorOpenGL *self, FlutterViewId view_id, int width, int height, const GdkRGBA *background_color)
Definition: fl_compositor_opengl.cc:597
fl_compositor_opengl.h
TEST
TEST(FlCompositorOpenGLTest, BackgroundColor)
Definition: fl_compositor_opengl_test.cc:19
fl_dart_project_new
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
Definition: fl_dart_project.cc:50
fl_engine_set_implicit_view
void fl_engine_set_implicit_view(FlEngine *self, FlRenderable *renderable)
Definition: fl_engine.cc:743
fl_engine_add_view
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
fl_engine_new
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition: fl_engine.cc:584
fl_engine_private.h
fl_framebuffer_new
FlFramebuffer * fl_framebuffer_new(GLint format, size_t width, size_t height)
Definition: fl_framebuffer.cc:42
fl_framebuffer.h
type
uint8_t type
Definition: fl_standard_message_codec_test.cc:1117
view_id
G_BEGIN_DECLS FlutterViewId view_id
Definition: fl_view_accessible.h:49