Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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>
7#include "gtest/gtest.h"
8
10#include "flutter/fml/logging.h"
20
21#include <epoxy/egl.h>
22
24 protected:
32
34 g_clear_object(&compositor);
35 g_clear_object(&renderable);
36 g_clear_object(&opengl_manager);
37 g_clear_object(&task_runner);
38 }
39
40 ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
41 FlTaskRunner* task_runner = nullptr;
42 FlOpenGLManager* opengl_manager = nullptr;
43 FlMockRenderable* renderable = nullptr;
44 FlCompositorOpenGL* compositor = nullptr;
45};
46
48 // Present layer from a thread.
49 constexpr size_t width = 100;
50 constexpr size_t height = 100;
51 g_autoptr(FlFramebuffer) framebuffer =
52 fl_framebuffer_new(GL_RGB, width, height, FALSE);
53 FlutterBackingStore backing_store = {
55 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
57 .backing_store = &backing_store,
58 .offset = {0, 0},
59 .size = {width, height}};
60 const FlutterLayer* layers[1] = {&layer};
61 std::thread([&]() {
62 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
63 }).join();
64
65 size_t frame_width, frame_height;
66 fl_compositor_get_frame_size(FL_COMPOSITOR(compositor), &frame_width,
67 &frame_height);
68 EXPECT_EQ(frame_width, width);
69 EXPECT_EQ(frame_height, height);
70
71 // Render presented layer.
72 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
73 g_autofree unsigned char* image_data =
74 static_cast<unsigned char*>(malloc(height * stride));
75 cairo_surface_t* surface = cairo_image_surface_create_for_data(
76 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
77 cairo_t* cr = cairo_create(surface);
78 fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr, TRUE);
79 cairo_surface_destroy(surface);
80 cairo_destroy(cr);
81}
82
84 // Present a layer that is the old size.
85 constexpr size_t width1 = 90;
86 constexpr size_t height1 = 90;
87 g_autoptr(FlFramebuffer) framebuffer1 =
88 fl_framebuffer_new(GL_RGB, width1, height1, FALSE);
89 FlutterBackingStore backing_store1 = {
91 .open_gl = {.framebuffer = {.user_data = framebuffer1}}};
93 .backing_store = &backing_store1,
94 .offset = {0, 0},
95 .size = {width1, height1}};
96 const FlutterLayer* layers1[1] = {&layer1};
97 std::thread([&]() {
98 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers1, 1);
99 }).join();
100
101 // Present layer in current size.
102 constexpr size_t width2 = 100;
103 constexpr size_t height2 = 100;
104 g_autoptr(FlFramebuffer) framebuffer2 =
105 fl_framebuffer_new(GL_RGB, width2, height2, FALSE);
106 FlutterBackingStore backing_store2 = {
108 .open_gl = {.framebuffer = {.user_data = framebuffer2}}};
110 .backing_store = &backing_store2,
111 .offset = {0, 0},
112 .size = {width2, height2}};
113 const FlutterLayer* layers2[1] = {&layer2};
115 std::thread([&]() {
116 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers2, 1);
117 latch.Signal();
118 }).detach();
119
120 // Render, will wait for the second layer if necessary.
121 int stride2 = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width2);
122 g_autofree unsigned char* image_data =
123 static_cast<unsigned char*>(malloc(height2 * stride2));
124 cairo_surface_t* surface = cairo_image_surface_create_for_data(
125 image_data, CAIRO_FORMAT_ARGB32, width2, height2, stride2);
126 cairo_t* cr = cairo_create(surface);
127 fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr, TRUE);
128 cairo_surface_destroy(surface);
129 cairo_destroy(cr);
130
131 latch.Wait();
132}
133
135 constexpr size_t width = 100;
136 constexpr size_t height = 100;
137
138 // OpenGL 3.0
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 ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(30));
144
145 g_autoptr(FlFramebuffer) framebuffer =
146 fl_framebuffer_new(GL_RGB, width, height, FALSE);
147 FlutterBackingStore backing_store = {
149 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
151 .backing_store = &backing_store,
152 .offset = {0, 0},
153 .size = {width, height}};
154 const FlutterLayer* layers[1] = {&layer};
155
156 constexpr GLuint kFakeTextureName = 123;
157 glBindTexture(GL_TEXTURE_2D, kFakeTextureName);
158 glDisable(GL_BLEND);
159 glEnable(GL_SCISSOR_TEST);
160
161 // Present layer and render.
162 std::thread([&]() {
163 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
164 }).join();
165 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
166 g_autofree unsigned char* image_data =
167 static_cast<unsigned char*>(malloc(height * stride));
168 cairo_surface_t* surface = cairo_image_surface_create_for_data(
169 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
170 cairo_t* cr = cairo_create(surface);
171 fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr, TRUE);
172 cairo_surface_destroy(surface);
173 cairo_destroy(cr);
174
175 GLuint texture_2d_binding;
176 glGetIntegerv(GL_TEXTURE_BINDING_2D,
177 reinterpret_cast<GLint*>(&texture_2d_binding));
178 EXPECT_EQ(texture_2d_binding, kFakeTextureName);
179 EXPECT_EQ(glIsEnabled(GL_BLEND), GL_FALSE);
180 EXPECT_EQ(glIsEnabled(GL_SCISSOR_TEST), GL_TRUE);
181}
182
184 constexpr size_t width = 100;
185 constexpr size_t height = 100;
186
187 // OpenGL 3.0
188 ON_CALL(epoxy, glGetString(GL_VENDOR))
189 .WillByDefault(
190 ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
191 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
192 EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
193
194 EXPECT_CALL(epoxy, glBlitFramebuffer);
195
196 g_autoptr(FlFramebuffer) framebuffer =
197 fl_framebuffer_new(GL_RGB, width, height, FALSE);
198 FlutterBackingStore backing_store = {
200 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
202 .backing_store = &backing_store,
203 .offset = {0, 0},
204 .size = {width, height}};
205 const FlutterLayer* layers[1] = {&layer};
206
207 // Present layer and render.
208 std::thread([&]() {
209 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
210 }).join();
211 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
212 g_autofree unsigned char* image_data =
213 static_cast<unsigned char*>(malloc(height * stride));
214 cairo_surface_t* surface = cairo_image_surface_create_for_data(
215 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
216 cairo_t* cr = cairo_create(surface);
217 fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr, TRUE);
218 cairo_surface_destroy(surface);
219 cairo_destroy(cr);
220}
221
222TEST_F(FlCompositorOpenGLTest, BlitFramebufferExtension) {
223 constexpr size_t width = 100;
224 constexpr size_t height = 100;
225
226 // OpenGL 2.0 with GL_EXT_framebuffer_blit extension
227 ON_CALL(epoxy, glGetString(GL_VENDOR))
228 .WillByDefault(
229 ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
230 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
231 EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20));
232 EXPECT_CALL(epoxy, epoxy_has_gl_extension(::testing::_))
233 .WillRepeatedly(::testing::Return(false));
234 EXPECT_CALL(epoxy, epoxy_has_gl_extension(
235 ::testing::StrEq("GL_EXT_framebuffer_blit")))
236 .WillRepeatedly(::testing::Return(true));
237
238 EXPECT_CALL(epoxy, glBlitFramebuffer);
239
240 g_autoptr(FlFramebuffer) framebuffer =
241 fl_framebuffer_new(GL_RGB, width, height, FALSE);
242 FlutterBackingStore backing_store = {
244 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
246 .backing_store = &backing_store,
247 .offset = {0, 0},
248 .size = {width, height}};
249 const FlutterLayer* layers[1] = {&layer};
250
251 // Present layer and render.
252 std::thread([&]() {
253 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
254 }).join();
255 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
256 g_autofree unsigned char* image_data =
257 static_cast<unsigned char*>(malloc(height * stride));
258 cairo_surface_t* surface = cairo_image_surface_create_for_data(
259 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
260 cairo_t* cr = cairo_create(surface);
261 fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr, TRUE);
262 cairo_surface_destroy(surface);
263 cairo_destroy(cr);
264}
265
266TEST_F(FlCompositorOpenGLTest, NoBlitFramebuffer) {
267 constexpr size_t width = 100;
268 constexpr size_t height = 100;
269
270 // OpenGL 2.0
271 ON_CALL(epoxy, glGetString(GL_VENDOR))
272 .WillByDefault(
273 ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
274 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
275 EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20));
276
277 g_autoptr(FlFramebuffer) framebuffer =
278 fl_framebuffer_new(GL_RGB, width, height, FALSE);
279 FlutterBackingStore backing_store = {
281 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
283 .backing_store = &backing_store,
284 .offset = {0, 0},
285 .size = {width, height}};
286 const FlutterLayer* layers[1] = {&layer};
287
288 // Present layer and render.
289 std::thread([&]() {
290 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
291 }).join();
292 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
293 g_autofree unsigned char* image_data =
294 static_cast<unsigned char*>(malloc(height * stride));
295 cairo_surface_t* surface = cairo_image_surface_create_for_data(
296 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
297 cairo_t* cr = cairo_create(surface);
298 fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr, TRUE);
299 cairo_surface_destroy(surface);
300 cairo_destroy(cr);
301}
302
303TEST_F(FlCompositorOpenGLTest, BlitFramebufferNvidia) {
304 constexpr size_t width = 100;
305 constexpr size_t height = 100;
306
307 // OpenGL 3.0, but on NVIDIA driver so temporarily disabled due to
308 // https://github.com/flutter/flutter/issues/152099
309 ON_CALL(epoxy, glGetString(GL_VENDOR))
310 .WillByDefault(
311 ::testing::Return(reinterpret_cast<const GLubyte*>("NVIDIA")));
312 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
313 EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
314
315 g_autoptr(FlFramebuffer) framebuffer =
316 fl_framebuffer_new(GL_RGB, width, height, FALSE);
317 FlutterBackingStore backing_store = {
319 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
321 .backing_store = &backing_store,
322 .offset = {0, 0},
323 .size = {width, height}};
324 const FlutterLayer* layers[1] = {&layer};
325
326 // Present layer and render.
327 std::thread([&]() {
328 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
329 }).join();
330 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
331 g_autofree unsigned char* image_data =
332 static_cast<unsigned char*>(malloc(height * stride));
333 cairo_surface_t* surface = cairo_image_surface_create_for_data(
334 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
335 cairo_t* cr = cairo_create(surface);
336 fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr, TRUE);
337 cairo_surface_destroy(surface);
338 cairo_destroy(cr);
339}
340
341TEST_F(FlCompositorOpenGLTest, RenderResizeCrash) {
342 // Present layer of size 100x100.
343 constexpr size_t width = 100;
344 constexpr size_t height = 100;
345 g_autoptr(FlFramebuffer) framebuffer =
346 fl_framebuffer_new(GL_RGB, width, height, FALSE);
347 FlutterBackingStore backing_store = {
349 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
351 .backing_store = &backing_store,
352 .offset = {0, 0},
353 .size = {width, height}};
354 const FlutterLayer* layers[1] = {&layer};
355 std::thread([&]() {
356 fl_compositor_present_layers(FL_COMPOSITOR(compositor), layers, 1);
357 }).join();
358
359 // Mock window size to be larger (200x200).
361 EXPECT_CALL(mock_gtk, gdk_window_get_width(::testing::_))
362 .WillRepeatedly(::testing::Return(200));
363 EXPECT_CALL(mock_gtk, gdk_window_get_height(::testing::_))
364 .WillRepeatedly(::testing::Return(200));
365
366 // Render with wait_for_frame = FALSE.
367 // This should not wait for a new frame, and try to render the 100x100 frame
368 // into the 200x200 window.
369 // If bug is present, it will try to read 200x200 from 100x100 buffer.
370 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, 200);
371 g_autofree unsigned char* image_data =
372 static_cast<unsigned char*>(g_malloc(200 * stride));
373 cairo_surface_t* surface = cairo_image_surface_create_for_data(
374 image_data, CAIRO_FORMAT_ARGB32, 200, 200, stride);
375 cairo_t* cr = cairo_create(surface);
376
377 // We expect this to not crash.
378 fl_compositor_render(FL_COMPOSITOR(compositor), cr, nullptr, FALSE);
379
380 cairo_surface_destroy(surface);
381 cairo_destroy(cr);
382}
::testing::NiceMock< flutter::testing::MockEpoxy > epoxy
@ kFlutterLayerContentTypeBackingStore
Definition embedder.h:2157
@ kFlutterBackingStoreTypeOpenGL
Definition embedder.h:2106
VkSurfaceKHR surface
Definition main.cc:65
g_autoptr(FlEngine) engine
gboolean fl_compositor_render(FlCompositor *self, cairo_t *cr, GdkWindow *window, gboolean wait_for_frame)
void fl_compositor_get_frame_size(FlCompositor *self, size_t *width, size_t *height)
gboolean fl_compositor_present_layers(FlCompositor *self, const FlutterLayer **layers, size_t layers_count)
FlFramebuffer * framebuffer
glBindTexture(GL_TEXTURE_2D, texture_id)
FlCompositorOpenGL * fl_compositor_opengl_new(FlTaskRunner *task_runner, FlOpenGLManager *opengl_manager, gboolean shareable)
TEST_F(FlCompositorOpenGLTest, Render)
const FlutterLayer ** layers
return TRUE
void fl_engine_set_implicit_view(FlEngine *self, FlRenderable *renderable)
Definition fl_engine.cc:937
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)
bool epoxy_has_gl_extension(const char *extension)
bool epoxy_is_desktop_gl(void)
int epoxy_gl_version(void)
int gdk_window_get_width(GdkWindow *window)
Definition mock_gtk.cc:79
int gdk_window_get_height(GdkWindow *window)
Definition mock_gtk.cc:87
int32_t height
int32_t width
FlutterBackingStoreType type
Specifies the type of backing store.
Definition embedder.h:2126
FlutterLayerContentType type
Definition embedder.h:2189