Flutter Windows Embedder
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 
8 #include "flutter/impeller/renderer/backend/gles/gles.h"
12 #include "flutter/shell/platform/windows/testing/egl/mock_context.h"
13 #include "flutter/shell/platform/windows/testing/egl/mock_manager.h"
14 #include "flutter/shell/platform/windows/testing/egl/mock_window_surface.h"
15 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
16 #include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
17 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
18 #include "flutter/shell/platform/windows/testing/view_modifier.h"
19 #include "flutter/shell/platform/windows/testing/windows_test.h"
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
22 
23 namespace flutter {
24 namespace testing {
25 
26 namespace {
27 using ::testing::AnyNumber;
28 using ::testing::Return;
29 
30 void MockGetIntegerv(GLenum name, int* value) {
31  if (name == GL_NUM_EXTENSIONS) {
32  *value = 1;
33  } else {
34  *value = 0;
35  }
36 }
37 
38 const 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 
48 const 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 
56 GLenum MockGetError() {
57  return GL_NO_ERROR;
58 }
59 
60 void MockGetIntegervWithMSAA(GLenum name, int* value) {
61  if (name == GL_NUM_EXTENSIONS) {
62  *value = 2;
63  } else {
64  *value = 0;
65  }
66 }
67 
68 const 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 
77 void DoNothing() {}
78 
79 const 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 
95 const 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 
107 void MockGetIntegervWithOffscreenMSAA(GLenum name, int* value) {
108  if (name == GL_NUM_EXTENSIONS) {
109  *value = 1;
110  } else if (name == GL_MAX_SAMPLES) {
111  *value = 4;
112  } else {
113  *value = 0;
114  }
115 }
116 
117 const unsigned char* MockGetStringWithOffscreenMSAA(GLenum name) {
118  switch (name) {
119  case GL_VERSION:
120  return reinterpret_cast<const unsigned char*>("OpenGL ES 3.0");
121  case GL_SHADING_LANGUAGE_VERSION:
122  return reinterpret_cast<const unsigned char*>("OpenGL ES GLSL ES 3.0");
123  default:
124  return reinterpret_cast<const unsigned char*>("");
125  }
126 }
127 
128 const impeller::ProcTableGLES::Resolver kMockResolverWithOffscreenMSAA =
129  [](const char* name) -> void* {
130  std::string_view function_name{name};
131 
132  if (function_name == "glGetString") {
133  return reinterpret_cast<void*>(&MockGetStringWithOffscreenMSAA);
134  } else if (function_name == "glGetIntegerv") {
135  return reinterpret_cast<void*>(&MockGetIntegervWithOffscreenMSAA);
136  }
137  return kMockResolver(name);
138 };
139 
140 class CompositorOpenGLTest : public WindowsTest {
141  public:
142  CompositorOpenGLTest() = default;
143  virtual ~CompositorOpenGLTest() = default;
144 
145  protected:
146  FlutterWindowsEngine* engine() { return engine_.get(); }
147  FlutterWindowsView* view() { return view_.get(); }
148  egl::MockManager* egl_manager() { return egl_manager_; }
149  egl::MockContext* render_context() { return render_context_.get(); }
150  egl::MockWindowSurface* surface() { return surface_; }
151 
152  void UseHeadlessEngine() {
153  auto egl_manager = std::make_unique<egl::MockManager>();
154  render_context_ = std::make_unique<egl::MockContext>();
155  egl_manager_ = egl_manager.get();
156 
157  EXPECT_CALL(*egl_manager_, render_context)
158  .Times(AnyNumber())
159  .WillRepeatedly(Return(render_context_.get()));
160 
161  FlutterWindowsEngineBuilder builder{GetContext()};
162 
163  engine_ = builder.Build();
164  EngineModifier modifier{engine_.get()};
165  modifier.SetEGLManager(std::move(egl_manager));
166  }
167 
168  void UseEngineWithView(bool add_surface = true) {
169  UseHeadlessEngine();
170 
171  auto window = std::make_unique<MockWindowBindingHandler>();
172  EXPECT_CALL(*window.get(), SetView).Times(1);
173  EXPECT_CALL(*window.get(), GetWindowHandle).WillRepeatedly(Return(nullptr));
174 
175  view_ = std::make_unique<FlutterWindowsView>(kImplicitViewId, engine_.get(),
176  std::move(window), false,
177  BoxConstraints());
178 
179  if (add_surface) {
180  auto surface = std::make_unique<egl::MockWindowSurface>();
181  surface_ = surface.get();
182 
183  EXPECT_CALL(*surface_, Destroy).Times(AnyNumber());
184 
185  ViewModifier modifier{view_.get()};
186  modifier.SetSurface(std::move(surface));
187  }
188  }
189 
190  private:
191  std::unique_ptr<FlutterWindowsEngine> engine_;
192  std::unique_ptr<FlutterWindowsView> view_;
193  std::unique_ptr<egl::MockContext> render_context_;
194  egl::MockWindowSurface* surface_;
195  egl::MockManager* egl_manager_;
196 
197  FML_DISALLOW_COPY_AND_ASSIGN(CompositorOpenGLTest);
198 };
199 
200 } // namespace
201 
202 TEST_F(CompositorOpenGLTest, CreateBackingStore) {
203  UseHeadlessEngine();
204 
205  auto compositor =
206  CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
207 
208  FlutterBackingStoreConfig config = {};
209  FlutterBackingStore backing_store = {};
210 
211  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
212  ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
213  ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
214 }
215 
216 TEST_F(CompositorOpenGLTest, CreateBackingStoreImpellerNoMSAA) {
217  UseHeadlessEngine();
218 
219  static int framebuffer_texture2d_calls = 0;
220  static int framebuffer_texture2d_multisample_calls = 0;
221  framebuffer_texture2d_calls = 0;
222  framebuffer_texture2d_multisample_calls = 0;
223 
224  const impeller::ProcTableGLES::Resolver resolver =
225  [](const char* name) -> void* {
226  std::string_view function_name{name};
227  if (function_name == "glFramebufferTexture2D") {
228  return reinterpret_cast<void*>(
229  +[](GLenum, GLenum, GLenum, GLuint, GLint) {
230  framebuffer_texture2d_calls++;
231  });
232  } else if (function_name == "glFramebufferTexture2DMultisampleEXT") {
233  return reinterpret_cast<void*>(
234  +[](GLenum, GLenum, GLenum, GLuint, GLint, GLsizei) {
235  framebuffer_texture2d_multisample_calls++;
236  });
237  }
238  return kMockResolver(name);
239  };
240 
241  auto compositor =
242  CompositorOpenGL{engine(), resolver, /*enable_impeller=*/true};
243  FlutterBackingStoreConfig config = {};
244  FlutterBackingStore backing_store = {};
245 
246  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
247  ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
248  EXPECT_EQ(framebuffer_texture2d_calls, 1);
249  EXPECT_EQ(framebuffer_texture2d_multisample_calls, 0);
250  ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
251 }
252 
253 TEST_F(CompositorOpenGLTest, CreateBackingStoreImpellerMSAA) {
254  UseHeadlessEngine();
255 
256  static int framebuffer_texture2d_calls = 0;
257  static int framebuffer_texture2d_multisample_calls = 0;
258  framebuffer_texture2d_calls = 0;
259  framebuffer_texture2d_multisample_calls = 0;
260 
261  const impeller::ProcTableGLES::Resolver resolver =
262  [](const char* name) -> void* {
263  std::string_view function_name{name};
264  if (function_name == "glFramebufferTexture2D") {
265  return reinterpret_cast<void*>(
266  +[](GLenum, GLenum, GLenum, GLuint, GLint) {
267  framebuffer_texture2d_calls++;
268  });
269  } else if (function_name == "glFramebufferTexture2DMultisampleEXT") {
270  return reinterpret_cast<void*>(
271  +[](GLenum, GLenum, GLenum, GLuint, GLint, GLsizei) {
272  framebuffer_texture2d_multisample_calls++;
273  });
274  }
275  return kMockResolverWithMSAA(name);
276  };
277 
278  auto compositor =
279  CompositorOpenGL{engine(), resolver, /*enable_impeller=*/true};
280  FlutterBackingStoreConfig config = {};
281  FlutterBackingStore backing_store = {};
282 
283  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
284  ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
285  EXPECT_EQ(framebuffer_texture2d_calls, 0);
286  EXPECT_EQ(framebuffer_texture2d_multisample_calls, 1);
287  ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
288 }
289 
290 // Verifies that when implicit MSAA is unsupported but offscreen MSAA is
291 // supported (OpenGL ES 3.0+ with GL_MAX_SAMPLES >= 4), CompositorOpenGL
292 // creates 4x MSAA color and depth/stencil renderbuffers.
293 TEST_F(CompositorOpenGLTest, CreateBackingStoreImpellerOffscreenMSAA) {
294  UseHeadlessEngine();
295 
296  static int framebuffer_texture2d_calls = 0;
297  static int framebuffer_texture2d_multisample_calls = 0;
298  static int renderbuffer_storage_multisample_calls = 0;
299  static int renderbuffer_storage_multisample_samples = 0;
300  static int framebuffer_renderbuffer_calls = 0;
301  static int delete_renderbuffers_calls = 0;
302  static int delete_textures_calls = 0;
303 
304  framebuffer_texture2d_calls = 0;
305  framebuffer_texture2d_multisample_calls = 0;
306  renderbuffer_storage_multisample_calls = 0;
307  renderbuffer_storage_multisample_samples = 0;
308  framebuffer_renderbuffer_calls = 0;
309  delete_renderbuffers_calls = 0;
310  delete_textures_calls = 0;
311 
312  const impeller::ProcTableGLES::Resolver resolver =
313  [](const char* name) -> void* {
314  std::string_view function_name{name};
315  if (function_name == "glFramebufferTexture2D") {
316  return reinterpret_cast<void*>(
317  +[](GLenum, GLenum, GLenum, GLuint, GLint) {
318  framebuffer_texture2d_calls++;
319  });
320  } else if (function_name == "glFramebufferTexture2DMultisampleEXT") {
321  return reinterpret_cast<void*>(
322  +[](GLenum, GLenum, GLenum, GLuint, GLint, GLsizei) {
323  framebuffer_texture2d_multisample_calls++;
324  });
325  } else if (function_name == "glGenRenderbuffers") {
326  return reinterpret_cast<void*>(+[](GLsizei n, GLuint* renderbuffers) {
327  static GLuint next_id = 1;
328  for (GLsizei i = 0; i < n; i++) {
329  renderbuffers[i] = next_id++;
330  }
331  });
332  } else if (function_name == "glRenderbufferStorageMultisample") {
333  return reinterpret_cast<void*>(+[](GLenum target, GLsizei samples,
334  GLenum internalformat, GLsizei width,
335  GLsizei height) {
336  renderbuffer_storage_multisample_calls++;
337  renderbuffer_storage_multisample_samples = samples;
338  });
339  } else if (function_name == "glFramebufferRenderbuffer") {
340  return reinterpret_cast<void*>(+[](GLenum, GLenum, GLenum, GLuint) {
341  framebuffer_renderbuffer_calls++;
342  });
343  } else if (function_name == "glDeleteRenderbuffers") {
344  return reinterpret_cast<void*>(
345  +[](GLsizei n, const GLuint* renderbuffers) {
346  delete_renderbuffers_calls += n;
347  });
348  } else if (function_name == "glDeleteTextures") {
349  return reinterpret_cast<void*>(+[](GLsizei n, const GLuint* textures) {
350  delete_textures_calls += n;
351  });
352  }
353  return kMockResolverWithOffscreenMSAA(name);
354  };
355 
356  auto compositor =
357  CompositorOpenGL{engine(), resolver, /*enable_impeller=*/true};
358  FlutterBackingStoreConfig config = {};
359  FlutterBackingStore backing_store = {};
360 
361  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
362  ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
363  EXPECT_EQ(framebuffer_texture2d_calls, 0);
364  EXPECT_EQ(framebuffer_texture2d_multisample_calls, 0);
365  EXPECT_EQ(renderbuffer_storage_multisample_calls, 2);
366  EXPECT_EQ(renderbuffer_storage_multisample_samples, 4);
367  EXPECT_EQ(framebuffer_renderbuffer_calls, 3);
368 
369  ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
370  EXPECT_EQ(delete_renderbuffers_calls, 2);
371  EXPECT_EQ(delete_textures_calls, 0);
372 }
373 
374 TEST_F(CompositorOpenGLTest, InitializationFailure) {
375  UseHeadlessEngine();
376 
377  auto compositor =
378  CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
379 
380  FlutterBackingStoreConfig config = {};
381  FlutterBackingStore backing_store = {};
382 
383  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(false));
384  EXPECT_FALSE(compositor.CreateBackingStore(config, &backing_store));
385 }
386 
387 TEST_F(CompositorOpenGLTest, InitializationRequiresBlit) {
388  UseHeadlessEngine();
389 
390  const impeller::ProcTableGLES::Resolver resolver = [](const char* name) {
391  std::string function_name{name};
392 
393  if (function_name == "glBlitFramebuffer" ||
394  function_name == "glBlitFramebufferANGLE") {
395  return (void*)nullptr;
396  }
397 
398  return kMockResolver(name);
399  };
400 
401  auto compositor =
402  CompositorOpenGL{engine(), resolver, /*enable_impeller=*/false};
403 
404  FlutterBackingStoreConfig config = {};
405  FlutterBackingStore backing_store = {};
406 
407  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
408  ASSERT_FALSE(compositor.CreateBackingStore(config, &backing_store));
409 }
410 
411 TEST_F(CompositorOpenGLTest, Present) {
412  UseEngineWithView();
413 
414  auto compositor =
415  CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
416 
417  FlutterBackingStoreConfig config = {};
418  FlutterBackingStore backing_store = {};
419 
420  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
421  ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
422 
423  FlutterLayer layer = {};
424  layer.type = kFlutterLayerContentTypeBackingStore;
425  layer.backing_store = &backing_store;
426  const FlutterLayer* layer_ptr = &layer;
427 
428  EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
429  EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
430  EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true));
431  EXPECT_TRUE(compositor.Present(view(), &layer_ptr, 1));
432 
433  ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
434 }
435 
436 TEST_F(CompositorOpenGLTest, PresentEmpty) {
437  UseEngineWithView();
438 
439  auto compositor =
440  CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
441 
442  // The context will be bound twice: first to initialize the compositor, second
443  // to clear the surface.
444  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
445  EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
446  EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
447  EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true));
448  EXPECT_TRUE(compositor.Present(view(), nullptr, 0));
449 }
450 
451 TEST_F(CompositorOpenGLTest, NoSurfaceIgnored) {
452  UseEngineWithView(/*add_surface = */ false);
453 
454  auto compositor =
455  CompositorOpenGL{engine(), kMockResolver, /*enable_impeller=*/false};
456 
457  FlutterBackingStoreConfig config = {};
458  FlutterBackingStore backing_store = {};
459 
460  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
461  ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
462 
463  FlutterLayer layer = {};
464  layer.type = kFlutterLayerContentTypeBackingStore;
465  layer.backing_store = &backing_store;
466  const FlutterLayer* layer_ptr = &layer;
467 
468  EXPECT_FALSE(compositor.Present(view(), &layer_ptr, 1));
469 
470  ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
471 }
472 
473 TEST_F(CompositorOpenGLTest, PresentUsingANGLEBlitExtension) {
474  UseEngineWithView();
475 
476  bool resolved_ANGLE_blit = false;
477  const impeller::ProcTableGLES::Resolver resolver =
478  [&resolved_ANGLE_blit](const char* name) {
479  std::string function_name{name};
480 
481  if (function_name == "glBlitFramebuffer") {
482  return (void*)nullptr;
483  } else if (function_name == "glBlitFramebufferANGLE") {
484  resolved_ANGLE_blit = true;
485  return reinterpret_cast<void*>(&DoNothing);
486  }
487 
488  return kMockResolver(name);
489  };
490 
491  auto compositor =
492  CompositorOpenGL{engine(), resolver, /*enable_impeller=*/false};
493 
494  FlutterBackingStoreConfig config = {};
495  FlutterBackingStore backing_store = {};
496 
497  EXPECT_CALL(*render_context(), MakeCurrent).WillOnce(Return(true));
498  ASSERT_TRUE(compositor.CreateBackingStore(config, &backing_store));
499 
500  FlutterLayer layer = {};
501  layer.type = kFlutterLayerContentTypeBackingStore;
502  layer.backing_store = &backing_store;
503  const FlutterLayer* layer_ptr = &layer;
504 
505  EXPECT_CALL(*surface(), IsValid).WillRepeatedly(Return(true));
506  EXPECT_CALL(*surface(), MakeCurrent).WillOnce(Return(true));
507  EXPECT_CALL(*surface(), SwapBuffers).WillOnce(Return(true));
508  EXPECT_TRUE(compositor.Present(view(), &layer_ptr, 1));
509  EXPECT_TRUE(resolved_ANGLE_blit);
510 
511  ASSERT_TRUE(compositor.CollectBackingStore(&backing_store));
512 }
513 
514 } // namespace testing
515 } // namespace flutter
TEST_F(AccessibilityPluginTest, DirectAnnounceCall)
constexpr FlutterViewId kImplicitViewId