Flutter Windows Embedder
angle_surface_manager.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 
6 
7 #include <vector>
8 
9 #include "flutter/fml/logging.h"
10 
11 // Logs an EGL error to stderr. This automatically calls eglGetError()
12 // and logs the error code.
13 static void LogEglError(std::string message) {
14  EGLint error = eglGetError();
15  FML_LOG(ERROR) << "EGL: " << message;
16  FML_LOG(ERROR) << "EGL: eglGetError returned " << error;
17 }
18 
19 namespace flutter {
20 
21 int AngleSurfaceManager::instance_count_ = 0;
22 
23 std::unique_ptr<AngleSurfaceManager> AngleSurfaceManager::Create(
24  bool enable_impeller) {
25  std::unique_ptr<AngleSurfaceManager> manager;
26  manager.reset(new AngleSurfaceManager(enable_impeller));
27  if (!manager->initialize_succeeded_) {
28  return nullptr;
29  }
30  return std::move(manager);
31 }
32 
34  : egl_config_(nullptr),
35  egl_display_(EGL_NO_DISPLAY),
36  egl_context_(EGL_NO_CONTEXT) {
37  initialize_succeeded_ = Initialize(enable_impeller);
38  ++instance_count_;
39 }
40 
42  CleanUp();
43  --instance_count_;
44 }
45 
46 bool AngleSurfaceManager::InitializeEGL(
47  PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT,
48  const EGLint* config,
49  bool should_log) {
50  egl_display_ = egl_get_platform_display_EXT(EGL_PLATFORM_ANGLE_ANGLE,
51  EGL_DEFAULT_DISPLAY, config);
52 
53  if (egl_display_ == EGL_NO_DISPLAY) {
54  if (should_log) {
55  LogEglError("Failed to get a compatible EGLdisplay");
56  }
57  return false;
58  }
59 
60  if (eglInitialize(egl_display_, nullptr, nullptr) == EGL_FALSE) {
61  if (should_log) {
62  LogEglError("Failed to initialize EGL via ANGLE");
63  }
64  return false;
65  }
66 
67  return true;
68 }
69 
70 bool AngleSurfaceManager::Initialize(bool enable_impeller) {
71  const EGLint config_attributes[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
72  EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
73  EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8,
74  EGL_NONE};
75 
76  const EGLint impeller_config_attributes[] = {
77  EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
78  EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8,
79  EGL_SAMPLE_BUFFERS, 1, EGL_SAMPLES, 4, EGL_NONE};
80  const EGLint impeller_config_attributes_no_msaa[] = {
81  EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
82  EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8,
83  EGL_NONE};
84 
85  const EGLint display_context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
86  EGL_NONE};
87 
88  // These are preferred display attributes and request ANGLE's D3D11
89  // renderer. eglInitialize will only succeed with these attributes if the
90  // hardware supports D3D11 Feature Level 10_0+.
91  const EGLint d3d11_display_attributes[] = {
92  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
93  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
94 
95  // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
96  // enable ANGLE to automatically call the IDXGIDevice3::Trim method on
97  // behalf of the application when it gets suspended.
98  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
99  EGL_TRUE,
100 
101  // This extension allows angle to render directly on a D3D swapchain
102  // in the correct orientation on D3D11.
103  EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
104  EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
105 
106  EGL_NONE,
107  };
108 
109  // These are used to request ANGLE's D3D11 renderer, with D3D11 Feature
110  // Level 9_3.
111  const EGLint d3d11_fl_9_3_display_attributes[] = {
112  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
113  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
114  EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
115  9,
116  EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
117  3,
118  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
119  EGL_TRUE,
120  EGL_NONE,
121  };
122 
123  // These attributes request D3D11 WARP (software rendering fallback) in case
124  // hardware-backed D3D11 is unavailable.
125  const EGLint d3d11_warp_display_attributes[] = {
126  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
127  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
128  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
129  EGL_TRUE,
130  EGL_NONE,
131  };
132 
133  std::vector<const EGLint*> display_attributes_configs = {
134  d3d11_display_attributes,
135  d3d11_fl_9_3_display_attributes,
136  d3d11_warp_display_attributes,
137  };
138 
139  PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT =
140  reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
141  eglGetProcAddress("eglGetPlatformDisplayEXT"));
142  if (!egl_get_platform_display_EXT) {
143  LogEglError("eglGetPlatformDisplayEXT not available");
144  return false;
145  }
146 
147  // Attempt to initialize ANGLE's renderer in order of: D3D11, D3D11 Feature
148  // Level 9_3 and finally D3D11 WARP.
149  for (auto config : display_attributes_configs) {
150  bool should_log = (config == display_attributes_configs.back());
151  if (InitializeEGL(egl_get_platform_display_EXT, config, should_log)) {
152  break;
153  }
154  }
155 
156  EGLint numConfigs = 0;
157  if (enable_impeller) {
158  // First try the MSAA configuration.
159  if ((eglChooseConfig(egl_display_, impeller_config_attributes, &egl_config_,
160  1, &numConfigs) == EGL_FALSE) ||
161  (numConfigs == 0)) {
162  // Next fall back to disabled MSAA.
163  if ((eglChooseConfig(egl_display_, impeller_config_attributes_no_msaa,
164  &egl_config_, 1, &numConfigs) == EGL_FALSE) ||
165  (numConfigs == 0)) {
166  LogEglError("Failed to choose first context");
167  return false;
168  }
169  }
170  } else {
171  if ((eglChooseConfig(egl_display_, config_attributes, &egl_config_, 1,
172  &numConfigs) == EGL_FALSE) ||
173  (numConfigs == 0)) {
174  LogEglError("Failed to choose first context");
175  return false;
176  }
177  }
178 
179  egl_context_ = eglCreateContext(egl_display_, egl_config_, EGL_NO_CONTEXT,
180  display_context_attributes);
181  if (egl_context_ == EGL_NO_CONTEXT) {
182  LogEglError("Failed to create EGL context");
183  return false;
184  }
185 
186  egl_resource_context_ = eglCreateContext(
187  egl_display_, egl_config_, egl_context_, display_context_attributes);
188 
189  if (egl_resource_context_ == EGL_NO_CONTEXT) {
190  LogEglError("Failed to create EGL resource context");
191  return false;
192  }
193 
194  return true;
195 }
196 
197 void AngleSurfaceManager::CleanUp() {
198  EGLBoolean result = EGL_FALSE;
199 
200  // Needs to be reset before destroying the EGLContext.
201  resolved_device_.Reset();
202 
203  if (egl_display_ != EGL_NO_DISPLAY && egl_context_ != EGL_NO_CONTEXT) {
204  result = eglDestroyContext(egl_display_, egl_context_);
205  egl_context_ = EGL_NO_CONTEXT;
206 
207  if (result == EGL_FALSE) {
208  LogEglError("Failed to destroy context");
209  }
210  }
211 
212  if (egl_display_ != EGL_NO_DISPLAY &&
213  egl_resource_context_ != EGL_NO_CONTEXT) {
214  result = eglDestroyContext(egl_display_, egl_resource_context_);
215  egl_resource_context_ = EGL_NO_CONTEXT;
216 
217  if (result == EGL_FALSE) {
218  LogEglError("Failed to destroy resource context");
219  }
220  }
221 
222  if (egl_display_ != EGL_NO_DISPLAY) {
223  // Display is reused between instances so only terminate display
224  // if destroying last instance
225  if (instance_count_ == 1) {
226  eglTerminate(egl_display_);
227  }
228  egl_display_ = EGL_NO_DISPLAY;
229  }
230 }
231 
233  EGLint width,
234  EGLint height) {
235  if (!hwnd || !initialize_succeeded_) {
236  return false;
237  }
238 
239  EGLSurface surface = EGL_NO_SURFACE;
240 
241  // Disable ANGLE's automatic surface resizing and provide an explicit size.
242  // The surface will need to be destroyed and re-created if the HWND is
243  // resized.
244  const EGLint surfaceAttributes[] = {
245  EGL_FIXED_SIZE_ANGLE, EGL_TRUE, EGL_WIDTH, width,
246  EGL_HEIGHT, height, EGL_NONE};
247 
248  surface = eglCreateWindowSurface(egl_display_, egl_config_,
249  static_cast<EGLNativeWindowType>(hwnd),
250  surfaceAttributes);
251  if (surface == EGL_NO_SURFACE) {
252  LogEglError("Surface creation failed.");
253  return false;
254  }
255 
256  surface_width_ = width;
257  surface_height_ = height;
258  render_surface_ = surface;
259  return true;
260 }
261 
263  EGLint width,
264  EGLint height,
265  bool vsync_enabled) {
266  EGLint existing_width, existing_height;
267  GetSurfaceDimensions(&existing_width, &existing_height);
268  if (width != existing_width || height != existing_height) {
269  surface_width_ = width;
270  surface_height_ = height;
271 
272  // TODO: Destroying the surface and re-creating it is expensive.
273  // Ideally this would use ANGLE's automatic surface sizing instead.
274  // See: https://github.com/flutter/flutter/issues/79427
275  ClearContext();
276  DestroySurface();
277  if (!CreateSurface(hwnd, width, height)) {
278  FML_LOG(ERROR)
279  << "AngleSurfaceManager::ResizeSurface failed to create surface";
280  }
281  }
282 
283  SetVSyncEnabled(vsync_enabled);
284 }
285 
286 void AngleSurfaceManager::GetSurfaceDimensions(EGLint* width, EGLint* height) {
287  if (render_surface_ == EGL_NO_SURFACE || !initialize_succeeded_) {
288  *width = 0;
289  *height = 0;
290  return;
291  }
292 
293  // This avoids eglQuerySurface as ideally surfaces would be automatically
294  // sized by ANGLE to avoid expensive surface destroy & re-create. With
295  // automatic sizing, ANGLE could resize the surface before Flutter asks it to,
296  // which would break resize redraw synchronization.
297  *width = surface_width_;
298  *height = surface_height_;
299 }
300 
302  if (egl_display_ != EGL_NO_DISPLAY && render_surface_ != EGL_NO_SURFACE) {
303  eglDestroySurface(egl_display_, render_surface_);
304  }
305  render_surface_ = EGL_NO_SURFACE;
306 }
307 
309  return eglGetCurrentContext() != EGL_NO_CONTEXT;
310 }
311 
313  return (eglMakeCurrent(egl_display_, render_surface_, render_surface_,
314  egl_context_) == EGL_TRUE);
315 }
316 
318  return (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE,
319  EGL_NO_CONTEXT) == EGL_TRUE);
320 }
321 
323  return (eglMakeCurrent(egl_display_, nullptr, nullptr, egl_context_) ==
324  EGL_TRUE);
325 }
326 
328  return (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE,
329  egl_resource_context_) == EGL_TRUE);
330 }
331 
333  return (eglSwapBuffers(egl_display_, render_surface_));
334 }
335 
337  EGLenum handle_type,
338  EGLClientBuffer handle,
339  const EGLint* attributes) const {
340  return eglCreatePbufferFromClientBuffer(egl_display_, handle_type, handle,
341  egl_config_, attributes);
342 }
343 
345  if (!MakeCurrent()) {
346  LogEglError("Unable to make surface current to update the swap interval");
347  return;
348  }
349 
350  // OpenGL swap intervals can be used to prevent screen tearing.
351  // If enabled, the raster thread blocks until the v-blank.
352  // This is unnecessary if DWM composition is enabled.
353  // See: https://www.khronos.org/opengl/wiki/Swap_Interval
354  // See: https://learn.microsoft.com/windows/win32/dwm/composition-ovw
355  if (eglSwapInterval(egl_display_, enabled ? 1 : 0) != EGL_TRUE) {
356  LogEglError("Unable to update the swap interval");
357  return;
358  }
359 }
360 
361 bool AngleSurfaceManager::GetDevice(ID3D11Device** device) {
362  if (!resolved_device_) {
363  PFNEGLQUERYDISPLAYATTRIBEXTPROC egl_query_display_attrib_EXT =
364  reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
365  eglGetProcAddress("eglQueryDisplayAttribEXT"));
366 
367  PFNEGLQUERYDEVICEATTRIBEXTPROC egl_query_device_attrib_EXT =
368  reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
369  eglGetProcAddress("eglQueryDeviceAttribEXT"));
370 
371  if (!egl_query_display_attrib_EXT || !egl_query_device_attrib_EXT) {
372  return false;
373  }
374 
375  EGLAttrib egl_device = 0;
376  EGLAttrib angle_device = 0;
377  if (egl_query_display_attrib_EXT(egl_display_, EGL_DEVICE_EXT,
378  &egl_device) == EGL_TRUE) {
379  if (egl_query_device_attrib_EXT(
380  reinterpret_cast<EGLDeviceEXT>(egl_device),
381  EGL_D3D11_DEVICE_ANGLE, &angle_device) == EGL_TRUE) {
382  resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device);
383  }
384  }
385  }
386 
387  resolved_device_.CopyTo(device);
388  return (resolved_device_ != nullptr);
389 }
390 
391 } // namespace flutter
flutter::AngleSurfaceManager::ClearContext
bool ClearContext()
Definition: angle_surface_manager.cc:322
flutter::AngleSurfaceManager::CreateSurface
virtual bool CreateSurface(HWND hwnd, EGLint width, EGLint height)
Definition: angle_surface_manager.cc:232
flutter::AngleSurfaceManager::HasContextCurrent
bool HasContextCurrent()
Definition: angle_surface_manager.cc:308
angle_surface_manager.h
flutter::AngleSurfaceManager::ClearCurrent
virtual bool ClearCurrent()
Definition: angle_surface_manager.cc:317
LogEglError
static void LogEglError(std::string message)
Definition: angle_surface_manager.cc:13
flutter::AngleSurfaceManager::AngleSurfaceManager
AngleSurfaceManager(bool enable_impeller)
Definition: angle_surface_manager.cc:33
flutter::AngleSurfaceManager::DestroySurface
virtual void DestroySurface()
Definition: angle_surface_manager.cc:301
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::AngleSurfaceManager::MakeCurrent
virtual bool MakeCurrent()
Definition: angle_surface_manager.cc:312
flutter::AngleSurfaceManager::~AngleSurfaceManager
virtual ~AngleSurfaceManager()
Definition: angle_surface_manager.cc:41
flutter::AngleSurfaceManager::MakeResourceCurrent
bool MakeResourceCurrent()
Definition: angle_surface_manager.cc:327
flutter::AngleSurfaceManager::GetDevice
bool GetDevice(ID3D11Device **device)
Definition: angle_surface_manager.cc:361
flutter::AngleSurfaceManager::SwapBuffers
EGLBoolean SwapBuffers()
Definition: angle_surface_manager.cc:332
flutter::AngleSurfaceManager::SetVSyncEnabled
virtual void SetVSyncEnabled(bool enabled)
Definition: angle_surface_manager.cc:344
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter::AngleSurfaceManager::Create
static std::unique_ptr< AngleSurfaceManager > Create(bool enable_impeller)
Definition: angle_surface_manager.cc:23
flutter::AngleSurfaceManager::CreateSurfaceFromHandle
EGLSurface CreateSurfaceFromHandle(EGLenum handle_type, EGLClientBuffer handle, const EGLint *attributes) const
Definition: angle_surface_manager.cc:336
flutter::AngleSurfaceManager::GetSurfaceDimensions
void GetSurfaceDimensions(EGLint *width, EGLint *height)
Definition: angle_surface_manager.cc:286
flutter::AngleSurfaceManager::ResizeSurface
virtual void ResizeSurface(HWND hwnd, EGLint width, EGLint height, bool enable_vsync)
Definition: angle_surface_manager.cc:262