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"
19
20#include <epoxy/egl.h>
21
23 protected:
24 void SetUp() override {
26 renderable = fl_mock_renderable_new();
29 }
30
32 g_clear_object(&compositor);
33 g_clear_object(&renderable);
34 g_clear_object(&opengl_manager);
35 }
36
37 ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
38 FlOpenGLManager* opengl_manager = nullptr;
39 FlMockRenderable* renderable = nullptr;
40 FlCompositorOpenGL* compositor = nullptr;
41};
42
44 // Present layer from a thread.
45 constexpr size_t width = 100;
46 constexpr size_t height = 100;
47 g_autoptr(FlFramebuffer) framebuffer =
48 fl_framebuffer_new(GL_RGB, width, height, FALSE);
49 FlutterBackingStore backing_store = {
51 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
53 .backing_store = &backing_store,
54 .offset = {0, 0},
55 .size = {width, height}};
56 const FlutterLayer* layers[1] = {&layer};
57 std::thread([&]() {
59 }).join();
60
61 size_t frame_width, frame_height;
62 fl_compositor_opengl_get_frame_size(compositor, &frame_width, &frame_height);
63 EXPECT_EQ(frame_width, width);
64 EXPECT_EQ(frame_height, height);
65
66 // Render presented layer.
67 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
68 g_autofree unsigned char* image_data =
69 static_cast<unsigned char*>(malloc(height * stride));
70 cairo_surface_t* surface = cairo_image_surface_create_for_data(
71 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
72 cairo_t* cr = cairo_create(surface);
73 fl_compositor_opengl_render(compositor, cr, nullptr);
74 cairo_surface_destroy(surface);
75 cairo_destroy(cr);
76}
77
79 // Present a layer that is the old size.
80 constexpr size_t width1 = 90;
81 constexpr size_t height1 = 90;
82 g_autoptr(FlFramebuffer) framebuffer1 =
83 fl_framebuffer_new(GL_RGB, width1, height1, FALSE);
84 FlutterBackingStore backing_store1 = {
86 .open_gl = {.framebuffer = {.user_data = framebuffer1}}};
88 .backing_store = &backing_store1,
89 .offset = {0, 0},
90 .size = {width1, height1}};
91 const FlutterLayer* layers1[1] = {&layer1};
92 fl_compositor_opengl_composite_layers(compositor, layers1, 1);
93
94 // Present layer in current size.
95 constexpr size_t width2 = 100;
96 constexpr size_t height2 = 100;
97 g_autoptr(FlFramebuffer) framebuffer2 =
98 fl_framebuffer_new(GL_RGB, width2, height2, FALSE);
99 FlutterBackingStore backing_store2 = {
101 .open_gl = {.framebuffer = {.user_data = framebuffer2}}};
103 .backing_store = &backing_store2,
104 .offset = {0, 0},
105 .size = {width2, height2}};
106 const FlutterLayer* layers2[1] = {&layer2};
107 fl_compositor_opengl_composite_layers(compositor, layers2, 1);
108
109 // The stored frame is now the new size.
110 size_t frame_width, frame_height;
111 fl_compositor_opengl_get_frame_size(compositor, &frame_width, &frame_height);
112 EXPECT_EQ(frame_width, width2);
113 EXPECT_EQ(frame_height, height2);
114
115 // Render the presented layer.
116 int stride2 = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width2);
117 g_autofree unsigned char* image_data =
118 static_cast<unsigned char*>(malloc(height2 * stride2));
119 cairo_surface_t* surface = cairo_image_surface_create_for_data(
120 image_data, CAIRO_FORMAT_ARGB32, width2, height2, stride2);
121 cairo_t* cr = cairo_create(surface);
122 fl_compositor_opengl_render(compositor, cr, nullptr);
123 cairo_surface_destroy(surface);
124 cairo_destroy(cr);
125}
126
128 constexpr size_t width = 100;
129 constexpr size_t height = 100;
130
131 // OpenGL 3.0
132 ON_CALL(epoxy, glGetString(GL_VENDOR))
133 .WillByDefault(
134 ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
135 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
136 ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(30));
137
138 g_autoptr(FlFramebuffer) framebuffer =
139 fl_framebuffer_new(GL_RGB, width, height, FALSE);
140 FlutterBackingStore backing_store = {
142 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
144 .backing_store = &backing_store,
145 .offset = {0, 0},
146 .size = {width, height}};
147 const FlutterLayer* layers[1] = {&layer};
148
149 constexpr GLuint kFakeTextureName = 123;
150 glBindTexture(GL_TEXTURE_2D, kFakeTextureName);
151 glDisable(GL_BLEND);
152 glEnable(GL_SCISSOR_TEST);
153
154 // Present layer and render.
155 std::thread([&]() {
157 }).join();
158 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
159 g_autofree unsigned char* image_data =
160 static_cast<unsigned char*>(malloc(height * stride));
161 cairo_surface_t* surface = cairo_image_surface_create_for_data(
162 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
163 cairo_t* cr = cairo_create(surface);
164 fl_compositor_opengl_render(compositor, cr, nullptr);
165 cairo_surface_destroy(surface);
166 cairo_destroy(cr);
167
168 GLuint texture_2d_binding;
169 glGetIntegerv(GL_TEXTURE_BINDING_2D,
170 reinterpret_cast<GLint*>(&texture_2d_binding));
171 EXPECT_EQ(texture_2d_binding, kFakeTextureName);
172 EXPECT_EQ(glIsEnabled(GL_BLEND), GL_FALSE);
173 EXPECT_EQ(glIsEnabled(GL_SCISSOR_TEST), GL_TRUE);
174}
175
177 constexpr size_t width = 100;
178 constexpr size_t height = 100;
179
180 // OpenGL 3.0
181 ON_CALL(epoxy, glGetString(GL_VENDOR))
182 .WillByDefault(
183 ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
184 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
185 EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
186
187 EXPECT_CALL(epoxy, glBlitFramebuffer);
188
189 g_autoptr(FlFramebuffer) framebuffer =
190 fl_framebuffer_new(GL_RGB, width, height, FALSE);
191 FlutterBackingStore backing_store = {
193 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
195 .backing_store = &backing_store,
196 .offset = {0, 0},
197 .size = {width, height}};
198 const FlutterLayer* layers[1] = {&layer};
199
200 // Present layer and render.
201 std::thread([&]() {
203 }).join();
204 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
205 g_autofree unsigned char* image_data =
206 static_cast<unsigned char*>(malloc(height * stride));
207 cairo_surface_t* surface = cairo_image_surface_create_for_data(
208 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
209 cairo_t* cr = cairo_create(surface);
210 fl_compositor_opengl_render(compositor, cr, nullptr);
211 cairo_surface_destroy(surface);
212 cairo_destroy(cr);
213}
214
215TEST_F(FlCompositorOpenGLTest, BlitFramebufferExtension) {
216 constexpr size_t width = 100;
217 constexpr size_t height = 100;
218
219 // OpenGL 2.0 with GL_EXT_framebuffer_blit extension
220 ON_CALL(epoxy, glGetString(GL_VENDOR))
221 .WillByDefault(
222 ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
223 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
224 EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20));
225 EXPECT_CALL(epoxy, epoxy_has_gl_extension(::testing::_))
226 .WillRepeatedly(::testing::Return(false));
227 EXPECT_CALL(epoxy, epoxy_has_gl_extension(
228 ::testing::StrEq("GL_EXT_framebuffer_blit")))
229 .WillRepeatedly(::testing::Return(true));
230
231 EXPECT_CALL(epoxy, glBlitFramebuffer);
232
233 g_autoptr(FlFramebuffer) framebuffer =
234 fl_framebuffer_new(GL_RGB, width, height, FALSE);
235 FlutterBackingStore backing_store = {
237 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
239 .backing_store = &backing_store,
240 .offset = {0, 0},
241 .size = {width, height}};
242 const FlutterLayer* layers[1] = {&layer};
243
244 // Present layer and render.
245 std::thread([&]() {
247 }).join();
248 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
249 g_autofree unsigned char* image_data =
250 static_cast<unsigned char*>(malloc(height * stride));
251 cairo_surface_t* surface = cairo_image_surface_create_for_data(
252 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
253 cairo_t* cr = cairo_create(surface);
254 fl_compositor_opengl_render(compositor, cr, nullptr);
255 cairo_surface_destroy(surface);
256 cairo_destroy(cr);
257}
258
259TEST_F(FlCompositorOpenGLTest, NoBlitFramebuffer) {
260 constexpr size_t width = 100;
261 constexpr size_t height = 100;
262
263 // OpenGL 2.0
264 ON_CALL(epoxy, glGetString(GL_VENDOR))
265 .WillByDefault(
266 ::testing::Return(reinterpret_cast<const GLubyte*>("Intel")));
267 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
268 EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20));
269
270 g_autoptr(FlFramebuffer) framebuffer =
271 fl_framebuffer_new(GL_RGB, width, height, FALSE);
272 FlutterBackingStore backing_store = {
274 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
276 .backing_store = &backing_store,
277 .offset = {0, 0},
278 .size = {width, height}};
279 const FlutterLayer* layers[1] = {&layer};
280
281 // Present layer and render.
282 std::thread([&]() {
284 }).join();
285 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
286 g_autofree unsigned char* image_data =
287 static_cast<unsigned char*>(malloc(height * stride));
288 cairo_surface_t* surface = cairo_image_surface_create_for_data(
289 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
290 cairo_t* cr = cairo_create(surface);
291 fl_compositor_opengl_render(compositor, cr, nullptr);
292 cairo_surface_destroy(surface);
293 cairo_destroy(cr);
294}
295
296TEST_F(FlCompositorOpenGLTest, BlitFramebufferNvidia) {
297 constexpr size_t width = 100;
298 constexpr size_t height = 100;
299
300 // OpenGL 3.0, but on NVIDIA driver so temporarily disabled due to
301 // https://github.com/flutter/flutter/issues/152099
302 ON_CALL(epoxy, glGetString(GL_VENDOR))
303 .WillByDefault(
304 ::testing::Return(reinterpret_cast<const GLubyte*>("NVIDIA")));
305 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true));
306 EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30));
307
308 g_autoptr(FlFramebuffer) framebuffer =
309 fl_framebuffer_new(GL_RGB, width, height, FALSE);
310 FlutterBackingStore backing_store = {
312 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
314 .backing_store = &backing_store,
315 .offset = {0, 0},
316 .size = {width, height}};
317 const FlutterLayer* layers[1] = {&layer};
318
319 // Present layer and render.
320 std::thread([&]() {
322 }).join();
323 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
324 g_autofree unsigned char* image_data =
325 static_cast<unsigned char*>(malloc(height * stride));
326 cairo_surface_t* surface = cairo_image_surface_create_for_data(
327 image_data, CAIRO_FORMAT_ARGB32, width, height, stride);
328 cairo_t* cr = cairo_create(surface);
329 fl_compositor_opengl_render(compositor, cr, nullptr);
330 cairo_surface_destroy(surface);
331 cairo_destroy(cr);
332}
333
334TEST_F(FlCompositorOpenGLTest, RenderResizeCrash) {
335 // Present layer of size 100x100.
336 constexpr size_t width = 100;
337 constexpr size_t height = 100;
338 g_autoptr(FlFramebuffer) framebuffer =
339 fl_framebuffer_new(GL_RGB, width, height, FALSE);
340 FlutterBackingStore backing_store = {
342 .open_gl = {.framebuffer = {.user_data = framebuffer}}};
344 .backing_store = &backing_store,
345 .offset = {0, 0},
346 .size = {width, height}};
347 const FlutterLayer* layers[1] = {&layer};
348 std::thread([&]() {
350 }).join();
351
352 // Mock window size to be larger (200x200).
354 EXPECT_CALL(mock_gtk, gdk_window_get_width(::testing::_))
355 .WillRepeatedly(::testing::Return(200));
356 EXPECT_CALL(mock_gtk, gdk_window_get_height(::testing::_))
357 .WillRepeatedly(::testing::Return(200));
358
359 // Render into the larger window.
360 // This renders the 100x100 frame into the 200x200 window without waiting.
361 // If bug is present, it will try to read 200x200 from 100x100 buffer.
362 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, 200);
363 g_autofree unsigned char* image_data =
364 static_cast<unsigned char*>(g_malloc(200 * stride));
365 cairo_surface_t* surface = cairo_image_surface_create_for_data(
366 image_data, CAIRO_FORMAT_ARGB32, 200, 200, stride);
367 cairo_t* cr = cairo_create(surface);
368
369 // We expect this to not crash.
370 fl_compositor_opengl_render(compositor, cr, nullptr);
371
372 cairo_surface_destroy(surface);
373 cairo_destroy(cr);
374}
::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
FlCompositorOpenGL * fl_compositor_opengl_new(FlOpenGLManager *opengl_manager, gboolean shareable)
void fl_compositor_opengl_get_frame_size(FlCompositorOpenGL *self, size_t *width, size_t *height)
gboolean fl_compositor_opengl_render(FlCompositorOpenGL *self, cairo_t *cr, GdkWindow *window)
gboolean fl_compositor_opengl_composite_layers(FlCompositorOpenGL *self, const FlutterLayer **layers, size_t layers_count)
TEST_F(FlCompositorOpenGLTest, Render)
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()
const FlutterLayer ** layers
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