Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
compositor_opengl_unittests.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 <memory>
6#include <vector>
7
20#include "gmock/gmock.h"
21#include "gtest/gtest.h"
22
23namespace flutter {
24namespace testing {
25
26namespace {
27using ::testing::AnyNumber;
28using ::testing::Return;
29
30void MockGetIntegerv(GLenum name, int* value) {
31 if (name == GL_NUM_EXTENSIONS) {
32 *value = 1;
33 } else {
34 *value = 0;
35 }
36}
37
38const unsigned char* MockGetString(GLenum name) {
39 switch (name) {
40 case GL_VERSION:
41 case GL_SHADING_LANGUAGE_VERSION:
42 return reinterpret_cast<const unsigned char*>("3.0");
43 default:
44 return reinterpret_cast<const unsigned char*>("");
45 }
46}
47
48const unsigned char* MockGetStringi(GLenum name, int index) {
49 if (name == GL_EXTENSIONS) {
50 return reinterpret_cast<const unsigned char*>("GL_ANGLE_framebuffer_blit");
51 } else {
52 return reinterpret_cast<const unsigned char*>("");
53 }
54}
55
56GLenum MockGetError() {
57 return GL_NO_ERROR;
58}
59
60void MockGetIntegervWithMSAA(GLenum name, int* value) {
61 if (name == GL_NUM_EXTENSIONS) {
62 *value = 2;
63 } else {
64 *value = 0;
65 }
66}
67
68const unsigned char* MockGetStringiWithMSAA(GLenum name, int index) {
69 static constexpr const char* extensions[] = {
70 "GL_ANGLE_framebuffer_blit", "GL_EXT_multisampled_render_to_texture"};
71 if (name == GL_EXTENSIONS && index < 2) {
72 return reinterpret_cast<const unsigned char*>(extensions[index]);
73 }
74 return reinterpret_cast<const unsigned char*>("");
75}
76
77void DoNothing() {}
78
79const impeller::ProcTableGLES::Resolver kMockResolver = [](const char* name) {
80 std::string function_name{name};
81
82 if (function_name == "glGetString") {
83 return reinterpret_cast<void*>(&MockGetString);
84 } else if (function_name == "glGetStringi") {
85 return reinterpret_cast<void*>(&MockGetStringi);
86 } else if (function_name == "glGetIntegerv") {
87 return reinterpret_cast<void*>(&MockGetIntegerv);
88 } else if (function_name == "glGetError") {
89 return reinterpret_cast<void*>(&MockGetError);
90 } else {
91 return reinterpret_cast<void*>(&DoNothing);
92 }
93};
94
95const impeller::ProcTableGLES::Resolver kMockResolverWithMSAA =
96 [](const char* name) {
97 std::string_view function_name{name};
98
99 if (function_name == "glGetStringi") {
100 return reinterpret_cast<void*>(&MockGetStringiWithMSAA);
101 } else if (function_name == "glGetIntegerv") {
102 return reinterpret_cast<void*>(&MockGetIntegervWithMSAA);
103 }
104 return kMockResolver(name);
105 };
106
107class CompositorOpenGLTest : public WindowsTest {
108 public:
109 CompositorOpenGLTest() = default;
110 virtual ~CompositorOpenGLTest() = default;
111
112 protected:
113 FlutterWindowsEngine* engine() { return engine_.get(); }
114 FlutterWindowsView* view() { return view_.get(); }
115 egl::MockManager* egl_manager() { return egl_manager_; }
116 egl::MockContext* render_context() { return render_context_.get(); }
117 egl::MockWindowSurface* surface() { return surface_; }
118
119 void UseHeadlessEngine() {
120 auto egl_manager = std::make_unique<egl::MockManager>();
121 render_context_ = std::make_unique<egl::MockContext>();
122 egl_manager_ = egl_manager.get();
123
124 EXPECT_CALL(*egl_manager_, render_context)
125 .Times(AnyNumber())
126 .WillRepeatedly(Return(render_context_.get()));
127
128 FlutterWindowsEngineBuilder builder{GetContext()};
129
130 engine_ = builder.Build();
131 EngineModifier modifier{engine_.get()};
132 modifier.SetEGLManager(std::move(egl_manager));
133 }
134
135 void UseEngineWithView(bool add_surface = true) {
136 UseHeadlessEngine();
137
138 auto window = std::make_unique<MockWindowBindingHandler>();
139 EXPECT_CALL(*window.get(), SetView).Times(1);
140 EXPECT_CALL(*window.get(), GetWindowHandle).WillRepeatedly(Return(nullptr));
141
142 view_ = std::make_unique<FlutterWindowsView>(kImplicitViewId, engine_.get(),
143 std::move(window), false,
144 BoxConstraints());
145
146 if (add_surface) {
147 auto surface = std::make_unique<egl::MockWindowSurface>();
148 surface_ = surface.get();
149
150 EXPECT_CALL(*surface_, Destroy).Times(AnyNumber());
151
152 ViewModifier modifier{view_.get()};
153 modifier.SetSurface(std::move(surface));
154 }
155 }
156
157 private:
158 std::unique_ptr<FlutterWindowsEngine> engine_;
159 std::unique_ptr<FlutterWindowsView> view_;
160 std::unique_ptr<egl::MockContext> render_context_;
161 egl::MockWindowSurface* surface_;
162 egl::MockManager* egl_manager_;
163
164 FML_DISALLOW_COPY_AND_ASSIGN(CompositorOpenGLTest);
165};
166
167} // namespace
168
169TEST_F(CompositorOpenGLTest, CreateBackingStore) {
170 UseHeadlessEngine();
171
172 auto compositor =
173 CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
174
175 FlutterBackingStoreConfig config = {};
176 FlutterBackingStore backing_store = {};
177
178 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
179 ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
180 ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
181}
182
183TEST_F(CompositorOpenGLTest, CreateBackingStoreImpellerNoMSAA) {
184 UseHeadlessEngine();
185
186 static int framebuffer_texture2d_calls = 0;
187 static int framebuffer_texture2d_multisample_calls = 0;
188 framebuffer_texture2d_calls = 0;
189 framebuffer_texture2d_multisample_calls = 0;
190
191 const impeller::ProcTableGLES::Resolver resolver =
192 [](const char* name) -> void* {
193 std::string_view function_name{name};
194 if (function_name == "glFramebufferTexture2D") {
195 return reinterpret_cast<void*>(
196 +[](GLenum, GLenum, GLenum, GLuint, GLint) {
197 framebuffer_texture2d_calls++;
198 });
199 } else if (function_name == "glFramebufferTexture2DMultisampleEXT") {
200 return reinterpret_cast<void*>(
201 +[](GLenum, GLenum, GLenum, GLuint, GLint, GLsizei) {
202 framebuffer_texture2d_multisample_calls++;
203 });
204 }
205 return kMockResolver(name);
206 };
207
208 auto compositor =
209 CompositorOpenGL{engine(), resolver, /*enable_impeller=*/true};
210 FlutterBackingStoreConfig config = {};
211 FlutterBackingStore backing_store = {};
212
213 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
214 ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
215 EXPECT_EQ(framebuffer_texture2d_calls, 1);
216 EXPECT_EQ(framebuffer_texture2d_multisample_calls, 0);
217 ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
218}
219
220TEST_F(CompositorOpenGLTest, CreateBackingStoreImpellerMSAA) {
221 UseHeadlessEngine();
222
223 static int framebuffer_texture2d_calls = 0;
224 static int framebuffer_texture2d_multisample_calls = 0;
225 framebuffer_texture2d_calls = 0;
226 framebuffer_texture2d_multisample_calls = 0;
227
228 const impeller::ProcTableGLES::Resolver resolver =
229 [](const char* name) -> void* {
230 std::string_view function_name{name};
231 if (function_name == "glFramebufferTexture2D") {
232 return reinterpret_cast<void*>(
233 +[](GLenum, GLenum, GLenum, GLuint, GLint) {
234 framebuffer_texture2d_calls++;
235 });
236 } else if (function_name == "glFramebufferTexture2DMultisampleEXT") {
237 return reinterpret_cast<void*>(
238 +[](GLenum, GLenum, GLenum, GLuint, GLint, GLsizei) {
239 framebuffer_texture2d_multisample_calls++;
240 });
241 }
242 return kMockResolverWithMSAA(name);
243 };
244
245 auto compositor =
246 CompositorOpenGL{engine(), resolver, /*enable_impeller=*/true};
247 FlutterBackingStoreConfig config = {};
248 FlutterBackingStore backing_store = {};
249
250 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
251 ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
252 EXPECT_EQ(framebuffer_texture2d_calls, 0);
253 EXPECT_EQ(framebuffer_texture2d_multisample_calls, 1);
254 ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
255}
256
257TEST_F(CompositorOpenGLTest, InitializationFailure) {
258 UseHeadlessEngine();
259
260 auto compositor =
261 CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
262
263 FlutterBackingStoreConfig config = {};
264 FlutterBackingStore backing_store = {};
265
266 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(false));
267 EXPECT_FALSE(compositor.CreateBackingStore(config, &backing_store));
268}
269
270TEST_F(CompositorOpenGLTest, InitializationRequiresBlit) {
271 UseHeadlessEngine();
272
273 const impeller::ProcTableGLES::Resolver resolver = [](const char* name) {
274 std::string function_name{name};
275
276 if (function_name == "glBlitFramebuffer" ||
277 function_name == "glBlitFramebufferANGLE") {
278 return (void*)nullptr;
279 }
280
281 return kMockResolver(name);
282 };
283
284 auto compositor =
285 CompositorOpenGL{engine(), resolver, /*enable_impeller=*/false};
286
287 FlutterBackingStoreConfig config = {};
288 FlutterBackingStore backing_store = {};
289
290 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
291 ASSERT_FALSE(compositor.CreateBackingStore(config, &backing_store));
292}
293
294TEST_F(CompositorOpenGLTest, Present) {
295 UseEngineWithView();
296
297 auto compositor =
298 CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
299
300 FlutterBackingStoreConfig config = {};
301 FlutterBackingStore backing_store = {};
302
303 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
304 ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
305
306 FlutterLayer layer = {};
308 layer.backing_store = &backing_store;
309 const FlutterLayer* layer_ptr = &layer;
310
311 EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
312 EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
313 EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true));
314 EXPECT_TRUE(compositor.Present(view(), &layer_ptr, 1));
315
316 ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
317}
318
319TEST_F(CompositorOpenGLTest, PresentEmpty) {
320 UseEngineWithView();
321
322 auto compositor =
323 CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
324
325 // The context will be bound twice: first to initialize the compositor, second
326 // to clear the surface.
327 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
328 EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
329 EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
330 EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true));
331 EXPECT_TRUE(compositor.Present(view(), nullptr, 0));
332}
333
334TEST_F(CompositorOpenGLTest, NoSurfaceIgnored) {
335 UseEngineWithView(/*add_surface = */ false);
336
337 auto compositor =
338 CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
339
340 FlutterBackingStoreConfig config = {};
341 FlutterBackingStore backing_store = {};
342
343 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
344 ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
345
346 FlutterLayer layer = {};
348 layer.backing_store = &backing_store;
349 const FlutterLayer* layer_ptr = &layer;
350
351 EXPECT_FALSE(compositor.Present(view(), &layer_ptr, 1));
352
353 ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
354}
355
356TEST_F(CompositorOpenGLTest, PresentUsingANGLEBlitExtension) {
357 UseEngineWithView();
358
359 bool resolved_ANGLE_blit = false;
360 const impeller::ProcTableGLES::Resolver resolver =
361 [&resolved_ANGLE_blit](const char* name) {
362 std::string function_name{name};
363
364 if (function_name == "glBlitFramebuffer") {
365 return (void*)nullptr;
366 } else if (function_name == "glBlitFramebufferANGLE") {
367 resolved_ANGLE_blit = true;
368 return reinterpret_cast<void*>(&DoNothing);
369 }
370
371 return kMockResolver(name);
372 };
373
374 auto compositor =
375 CompositorOpenGL{engine(), resolver, /*enable_impeller=*/false};
376
377 FlutterBackingStoreConfig config = {};
378 FlutterBackingStore backing_store = {};
379
380 EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
381 ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
382
383 FlutterLayer layer = {};
385 layer.backing_store = &backing_store;
386 const FlutterLayer* layer_ptr = &layer;
387
388 EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
389 EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
390 EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true));
391 EXPECT_TRUE(compositor.Present(view(), &layer_ptr, 1));
392 EXPECT_TRUE(resolved_ANGLE_blit);
393
394 ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
395}
396
397} // namespace testing
398} // namespace flutter
std::function< void *(const char *function_name)> Resolver
int32_t value
@ kFlutterLayerContentTypeBackingStore
Definition embedder.h:2157
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
VkSurfaceKHR surface
Definition main.cc:65
FlView * view
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
EGLSurface surface_
TEST_F(DisplayListTest, Defaults)
constexpr int64_t kImplicitViewId
DEF_SWITCHES_START aot vmservice shared library name
Definition switch_defs.h:27
FlutterLayerContentType type
Definition embedder.h:2189
const FlutterBackingStore * backing_store
Definition embedder.h:2193