Flutter Engine
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 
5 #include "flutter/shell/platform/windows/angle_surface_manager.h"
6 
7 #include <iostream>
8 #include <vector>
9 
10 #ifdef WINUWP
11 #include <third_party/cppwinrt/generated/winrt/Windows.UI.Composition.h>
12 #include <windows.ui.core.h>
13 #endif
14 
15 #if defined(WINUWP) && defined(USECOREWINDOW)
16 #include <winrt/Windows.UI.Core.h>
17 #endif
18 
19 // Logs an EGL error to stderr. This automatically calls eglGetError()
20 // and logs the error code.
21 static void LogEglError(std::string message) {
22  EGLint error = eglGetError();
23  std::cerr << "EGL: " << message << std::endl;
24  std::cerr << "EGL: eglGetError returned " << error << std::endl;
25 }
26 
27 namespace flutter {
28 
29 int AngleSurfaceManager::instance_count_ = 0;
30 
31 std::unique_ptr<AngleSurfaceManager> AngleSurfaceManager::Create() {
32  std::unique_ptr<AngleSurfaceManager> manager;
33  manager.reset(new AngleSurfaceManager());
34  if (!manager->initialize_succeeded_) {
35  return nullptr;
36  }
37  return std::move(manager);
38 }
39 
40 AngleSurfaceManager::AngleSurfaceManager()
41  : egl_config_(nullptr),
42  egl_display_(EGL_NO_DISPLAY),
43  egl_context_(EGL_NO_CONTEXT) {
44  initialize_succeeded_ = Initialize();
45  ++instance_count_;
46 }
47 
49  CleanUp();
50  --instance_count_;
51 }
52 
53 bool AngleSurfaceManager::InitializeEGL(
54  PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT,
55  const EGLint* config,
56  bool should_log) {
57  egl_display_ = egl_get_platform_display_EXT(EGL_PLATFORM_ANGLE_ANGLE,
58  EGL_DEFAULT_DISPLAY, config);
59 
60  if (egl_display_ == EGL_NO_DISPLAY) {
61  if (should_log) {
62  LogEglError("Failed to get a compatible EGLdisplay");
63  }
64  return false;
65  }
66 
67  if (eglInitialize(egl_display_, nullptr, nullptr) == EGL_FALSE) {
68  if (should_log) {
69  LogEglError("Failed to initialize EGL via ANGLE");
70  }
71  return false;
72  }
73 
74  return true;
75 }
76 
77 bool AngleSurfaceManager::Initialize() {
78  const EGLint config_attributes[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
79  EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
80  EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8,
81  EGL_NONE};
82 
83  const EGLint display_context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
84  EGL_NONE};
85 
86  // These are preferred display attributes and request ANGLE's D3D11
87  // renderer. eglInitialize will only succeed with these attributes if the
88  // hardware supports D3D11 Feature Level 10_0+.
89  const EGLint d3d11_display_attributes[] = {
90  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
91  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
92 
93  // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
94  // enable ANGLE to automatically call the IDXGIDevice3::Trim method on
95  // behalf of the application when it gets suspended.
96  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
97  EGL_TRUE,
98  EGL_NONE,
99  };
100 
101  // These are used to request ANGLE's D3D11 renderer, with D3D11 Feature
102  // Level 9_3.
103  const EGLint d3d11_fl_9_3_display_attributes[] = {
104  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
105  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
106  EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
107  9,
108  EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
109  3,
110  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
111  EGL_TRUE,
112  EGL_NONE,
113  };
114 
115  // These attributes request D3D11 WARP (software rendering fallback) in case
116  // hardware-backed D3D11 is unavailable.
117  const EGLint d3d11_warp_display_attributes[] = {
118  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
119  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
120  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
121  EGL_TRUE,
122  EGL_NONE,
123  };
124 
125  // These are used to request ANGLE's D3D9 renderer as a fallback if D3D11
126  // is not available.
127  const EGLint d3d9_display_attributes[] = {
128  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
129  EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
130  EGL_TRUE,
131  EGL_NONE,
132  };
133 
134  std::vector<const EGLint*> display_attributes_configs = {
135  d3d11_display_attributes,
136  d3d11_fl_9_3_display_attributes,
137  d3d11_warp_display_attributes,
138  d3d9_display_attributes,
139  };
140 
141  PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT =
142  reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
143  eglGetProcAddress("eglGetPlatformDisplayEXT"));
144  if (!egl_get_platform_display_EXT) {
145  LogEglError("eglGetPlatformDisplayEXT not available");
146  return false;
147  }
148 
149  // Attempt to initialize ANGLE's renderer in order of: D3D11, D3D11 Feature
150  // Level 9_3, D3D11 WARP and finally D3D9.
151  for (auto config : display_attributes_configs) {
152  bool should_log = (config == display_attributes_configs.back());
153  if (InitializeEGL(egl_get_platform_display_EXT, config, should_log)) {
154  break;
155  }
156  }
157 
158  EGLint numConfigs = 0;
159  if ((eglChooseConfig(egl_display_, config_attributes, &egl_config_, 1,
160  &numConfigs) == EGL_FALSE) ||
161  (numConfigs == 0)) {
162  LogEglError("Failed to choose first context");
163  return false;
164  }
165 
166  egl_context_ = eglCreateContext(egl_display_, egl_config_, EGL_NO_CONTEXT,
167  display_context_attributes);
168  if (egl_context_ == EGL_NO_CONTEXT) {
169  LogEglError("Failed to create EGL context");
170  return false;
171  }
172 
173  egl_resource_context_ = eglCreateContext(
174  egl_display_, egl_config_, egl_context_, display_context_attributes);
175 
176  if (egl_resource_context_ == EGL_NO_CONTEXT) {
177  LogEglError("Failed to create EGL resource context");
178  return false;
179  }
180 
181  return true;
182 }
183 
184 void AngleSurfaceManager::CleanUp() {
185  EGLBoolean result = EGL_FALSE;
186 
187  if (egl_display_ != EGL_NO_DISPLAY && egl_context_ != EGL_NO_CONTEXT) {
188  result = eglDestroyContext(egl_display_, egl_context_);
189  egl_context_ = EGL_NO_CONTEXT;
190 
191  if (result == EGL_FALSE) {
192  LogEglError("Failed to destroy context");
193  }
194  }
195 
196  if (egl_display_ != EGL_NO_DISPLAY &&
197  egl_resource_context_ != EGL_NO_CONTEXT) {
198  result = eglDestroyContext(egl_display_, egl_resource_context_);
199  egl_resource_context_ = EGL_NO_CONTEXT;
200 
201  if (result == EGL_FALSE) {
202  LogEglError("Failed to destroy resource context");
203  }
204  }
205 
206  if (egl_display_ != EGL_NO_DISPLAY) {
207  // Display is reused between instances so only terminate display
208  // if destroying last instance
209  if (instance_count_ == 1) {
210  eglTerminate(egl_display_);
211  }
212  egl_display_ = EGL_NO_DISPLAY;
213  }
214 }
215 
217  EGLint width,
218  EGLint height) {
219  if (!render_target || !initialize_succeeded_) {
220  return false;
221  }
222 
223  EGLSurface surface = EGL_NO_SURFACE;
224 
225 #ifdef WINUWP
226  const EGLint surfaceAttributes[] = {EGL_NONE};
227 #else
228  const EGLint surfaceAttributes[] = {
229  EGL_FIXED_SIZE_ANGLE, EGL_TRUE, EGL_WIDTH, width,
230  EGL_HEIGHT, height, EGL_NONE};
231 #endif
232 
233 #ifdef WINUWP
234 #ifdef USECOREWINDOW
235  auto target = std::get<winrt::Windows::UI::Core::CoreWindow>(*render_target);
236 #else
237  auto target =
238  std::get<winrt::Windows::UI::Composition::SpriteVisual>(*render_target);
239 #endif
240  surface = eglCreateWindowSurface(
241  egl_display_, egl_config_,
242  static_cast<EGLNativeWindowType>(winrt::get_abi(target)),
243  surfaceAttributes);
244 #else
245  surface = eglCreateWindowSurface(
246  egl_display_, egl_config_,
247  static_cast<EGLNativeWindowType>(std::get<HWND>(*render_target)),
248  surfaceAttributes);
249 #endif
250  if (surface == EGL_NO_SURFACE) {
251  LogEglError("Surface creation failed.");
252  }
253 
254  surface_width_ = width;
255  surface_height_ = height;
256  render_surface_ = surface;
257  return true;
258 }
259 
261  EGLint width,
262  EGLint height) {
263  EGLint existing_width, existing_height;
264  GetSurfaceDimensions(&existing_width, &existing_height);
265  if (width != existing_width || height != existing_height) {
266  surface_width_ = width;
267  surface_height_ = height;
268 
269  // TODO(clarkezone) convert ifdef to use use final implementation of angle
270  // resize API prototyped here
271  // https://github.com/clarkezone/angle/tree/resizeswapchaintest to eliminate
272  // unnecessary surface creation / desctruction by use ResizeSwapchain
273  // https://github.com/flutter/flutter/issues/79427
274 #ifdef WINUWP
275  // Resize render_surface_. Internaly this calls mSwapChain->ResizeBuffers
276  // avoiding the need to destory and recreate the underlying SwapChain.
277  eglPostSubBufferNV(egl_display_, render_surface_, 1, 1, width, height);
278 #else
279  ClearContext();
280  DestroySurface();
281  if (!CreateSurface(render_target, width, height)) {
282  std::cerr << "AngleSurfaceManager::ResizeSurface failed to create surface"
283  << std::endl;
284  }
285 #endif
286  }
287 }
288 
290  if (render_surface_ == EGL_NO_SURFACE || !initialize_succeeded_) {
291  *width = 0;
292  *height = 0;
293  return;
294  }
295 
296  // Can't use eglQuerySurface here; Because we're not using
297  // EGL_FIXED_SIZE_ANGLE flag anymore, Angle may resize the surface before
298  // Flutter asks it to, which breaks resize redraw synchronization
299  *width = surface_width_;
300  *height = surface_height_;
301 }
302 
304  if (egl_display_ != EGL_NO_DISPLAY && render_surface_ != EGL_NO_SURFACE) {
305  eglDestroySurface(egl_display_, render_surface_);
306  }
307  render_surface_ = EGL_NO_SURFACE;
308 }
309 
311  return (eglMakeCurrent(egl_display_, render_surface_, render_surface_,
312  egl_context_) == EGL_TRUE);
313 }
314 
316  return (eglMakeCurrent(egl_display_, nullptr, nullptr, egl_context_) ==
317  EGL_TRUE);
318 }
319 
321  return (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE,
322  egl_resource_context_) == EGL_TRUE);
323 }
324 
326  return (eglSwapBuffers(egl_display_, render_surface_));
327 }
328 
329 } // namespace flutter
static std::unique_ptr< AngleSurfaceManager > Create()
void GetSurfaceDimensions(EGLint *width, EGLint *height)
const uint8_t uint32_t uint32_t GError ** error
std::variant< HWND > WindowsRenderTarget
GAsyncResult * result
void ResizeSurface(WindowsRenderTarget *render_target, EGLint width, EGLint height)
uint32_t * target
int32_t width
int32_t height
bool CreateSurface(WindowsRenderTarget *render_target, EGLint width, EGLint height)
static void LogEglError(std::string message)