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  bool vsync_enabled) {
236  if (!render_target || !initialize_succeeded_) {
237  return false;
238  }
239 
240  EGLSurface surface = EGL_NO_SURFACE;
241 
242  const EGLint surfaceAttributes[] = {
243  EGL_FIXED_SIZE_ANGLE, EGL_TRUE, EGL_WIDTH, width,
244  EGL_HEIGHT, height, EGL_NONE};
245 
246  surface = eglCreateWindowSurface(
247  egl_display_, egl_config_,
248  static_cast<EGLNativeWindowType>(std::get<HWND>(*render_target)),
249  surfaceAttributes);
250  if (surface == EGL_NO_SURFACE) {
251  LogEglError("Surface creation failed.");
252  return false;
253  }
254 
255  surface_width_ = width;
256  surface_height_ = height;
257  render_surface_ = surface;
258 
259  if (!MakeCurrent()) {
260  LogEglError("Unable to make surface current to update the swap interval");
261  return false;
262  }
263 
264  SetVSyncEnabled(vsync_enabled);
265  return true;
266 }
267 
269  EGLint width,
270  EGLint height,
271  bool vsync_enabled) {
272  EGLint existing_width, existing_height;
273  GetSurfaceDimensions(&existing_width, &existing_height);
274  if (width != existing_width || height != existing_height) {
275  surface_width_ = width;
276  surface_height_ = height;
277 
278  ClearContext();
279  DestroySurface();
280  if (!CreateSurface(render_target, width, height, vsync_enabled)) {
281  FML_LOG(ERROR)
282  << "AngleSurfaceManager::ResizeSurface failed to create surface";
283  }
284  }
285 }
286 
287 void AngleSurfaceManager::GetSurfaceDimensions(EGLint* width, EGLint* height) {
288  if (render_surface_ == EGL_NO_SURFACE || !initialize_succeeded_) {
289  *width = 0;
290  *height = 0;
291  return;
292  }
293 
294  // Can't use eglQuerySurface here; Because we're not using
295  // EGL_FIXED_SIZE_ANGLE flag anymore, Angle may resize the surface before
296  // Flutter asks it to, which breaks 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  // OpenGL swap intervals can be used to prevent screen tearing.
346  // If enabled, the raster thread blocks until the v-blank.
347  // This is unnecessary if DWM composition is enabled.
348  // See: https://www.khronos.org/opengl/wiki/Swap_Interval
349  // See: https://learn.microsoft.com/windows/win32/dwm/composition-ovw
350  if (eglSwapInterval(egl_display_, enabled ? 1 : 0) != EGL_TRUE) {
351  LogEglError("Unable to update the swap interval");
352  return;
353  }
354 }
355 
356 bool AngleSurfaceManager::GetDevice(ID3D11Device** device) {
357  if (!resolved_device_) {
358  PFNEGLQUERYDISPLAYATTRIBEXTPROC egl_query_display_attrib_EXT =
359  reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
360  eglGetProcAddress("eglQueryDisplayAttribEXT"));
361 
362  PFNEGLQUERYDEVICEATTRIBEXTPROC egl_query_device_attrib_EXT =
363  reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
364  eglGetProcAddress("eglQueryDeviceAttribEXT"));
365 
366  if (!egl_query_display_attrib_EXT || !egl_query_device_attrib_EXT) {
367  return false;
368  }
369 
370  EGLAttrib egl_device = 0;
371  EGLAttrib angle_device = 0;
372  if (egl_query_display_attrib_EXT(egl_display_, EGL_DEVICE_EXT,
373  &egl_device) == EGL_TRUE) {
374  if (egl_query_device_attrib_EXT(
375  reinterpret_cast<EGLDeviceEXT>(egl_device),
376  EGL_D3D11_DEVICE_ANGLE, &angle_device) == EGL_TRUE) {
377  resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device);
378  }
379  }
380  }
381 
382  resolved_device_.CopyTo(device);
383  return (resolved_device_ != nullptr);
384 }
385 
386 } // namespace flutter
flutter::AngleSurfaceManager::ClearContext
bool ClearContext()
Definition: angle_surface_manager.cc:322
flutter::AngleSurfaceManager::HasContextCurrent
bool HasContextCurrent()
Definition: angle_surface_manager.cc:308
flutter::WindowsRenderTarget
std::variant< HWND > WindowsRenderTarget
Definition: window_binding_handler.h:44
angle_surface_manager.h
flutter::AngleSurfaceManager::CreateSurface
virtual bool CreateSurface(WindowsRenderTarget *render_target, EGLint width, EGLint height, bool enable_vsync)
Definition: angle_surface_manager.cc:232
flutter::AngleSurfaceManager::ClearCurrent
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
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:356
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
flutter::AngleSurfaceManager::ResizeSurface
virtual void ResizeSurface(WindowsRenderTarget *render_target, EGLint width, EGLint height, bool enable_vsync)
Definition: angle_surface_manager.cc:268
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:287