Flutter Windows Embedder
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"
11 
12 namespace flutter {
13 namespace egl {
14 
15 int Manager::instance_count_ = 0;
16 
17 std::unique_ptr<Manager> Manager::Create(bool enable_impeller) {
18  std::unique_ptr<Manager> manager;
19  manager.reset(new Manager(enable_impeller));
20  if (!manager->IsValid()) {
21  return nullptr;
22  }
23  return std::move(manager);
24 }
25 
26 Manager::Manager(bool enable_impeller) {
27  ++instance_count_;
28 
29  if (!InitializeDisplay()) {
30  return;
31  }
32 
33  if (!InitializeConfig(enable_impeller)) {
34  return;
35  }
36 
37  if (!InitializeContexts()) {
38  return;
39  }
40 
41  is_valid_ = true;
42 }
43 
45  CleanUp();
46  --instance_count_;
47 }
48 
49 bool Manager::InitializeDisplay() {
50  // These are preferred display attributes and request ANGLE's D3D11
51  // renderer. eglInitialize will only succeed with these attributes if the
52  // hardware supports D3D11 Feature Level 10_0+.
53  const EGLint d3d11_display_attributes[] = {
54  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
55  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
56 
57  // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
58  // enable ANGLE to automatically call the IDXGIDevice3::Trim method on
59  // behalf of the application when it gets suspended.
60  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
61  EGL_TRUE,
62 
63  // This extension allows angle to render directly on a D3D swapchain
64  // in the correct orientation on D3D11.
65  EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
66  EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
67 
68  EGL_NONE,
69  };
70 
71  // These are used to request ANGLE's D3D11 renderer, with D3D11 Feature
72  // Level 9_3.
73  const EGLint d3d11_fl_9_3_display_attributes[] = {
74  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
75  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
76  EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
77  9,
78  EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
79  3,
80  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
81  EGL_TRUE,
82  EGL_NONE,
83  };
84 
85  // These attributes request D3D11 WARP (software rendering fallback) in case
86  // hardware-backed D3D11 is unavailable.
87  const EGLint d3d11_warp_display_attributes[] = {
88  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
89  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
90  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
91  EGL_TRUE,
92  EGL_NONE,
93  };
94 
95  std::vector<const EGLint*> display_attributes_configs = {
96  d3d11_display_attributes,
97  d3d11_fl_9_3_display_attributes,
98  d3d11_warp_display_attributes,
99  };
100 
101  PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT =
102  reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
103  ::eglGetProcAddress("eglGetPlatformDisplayEXT"));
104  if (!egl_get_platform_display_EXT) {
105  LogEGLError("eglGetPlatformDisplayEXT not available");
106  return false;
107  }
108 
109  // Attempt to initialize ANGLE's renderer in order of: D3D11, D3D11 Feature
110  // Level 9_3 and finally D3D11 WARP.
111  for (auto config : display_attributes_configs) {
112  bool is_last = (config == display_attributes_configs.back());
113 
114  display_ = egl_get_platform_display_EXT(EGL_PLATFORM_ANGLE_ANGLE,
115  EGL_DEFAULT_DISPLAY, config);
116 
117  if (display_ == EGL_NO_DISPLAY) {
118  if (is_last) {
119  LogEGLError("Failed to get a compatible EGLdisplay");
120  return false;
121  }
122 
123  // Try the next config.
124  continue;
125  }
126 
127  if (::eglInitialize(display_, nullptr, nullptr) == EGL_FALSE) {
128  if (is_last) {
129  LogEGLError("Failed to initialize EGL via ANGLE");
130  return false;
131  }
132 
133  // Try the next config.
134  continue;
135  }
136 
137  return true;
138  }
139 
140  FML_UNREACHABLE();
141 }
142 
143 bool Manager::InitializeConfig(bool enable_impeller) {
144  const EGLint config_attributes[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
145  EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
146  EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8,
147  EGL_NONE};
148 
149  const EGLint impeller_config_attributes[] = {
150  EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
151  EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8,
152  EGL_SAMPLE_BUFFERS, 1, EGL_SAMPLES, 4, EGL_NONE};
153  const EGLint impeller_config_attributes_no_msaa[] = {
154  EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
155  EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8,
156  EGL_NONE};
157 
158  EGLBoolean result;
159  EGLint num_config = 0;
160 
161  if (enable_impeller) {
162  // First try the MSAA configuration.
163  result = ::eglChooseConfig(display_, impeller_config_attributes, &config_,
164  1, &num_config);
165 
166  if (result == EGL_TRUE && num_config > 0) {
167  return true;
168  }
169 
170  // Next fall back to disabled MSAA.
171  result = ::eglChooseConfig(display_, impeller_config_attributes_no_msaa,
172  &config_, 1, &num_config);
173  if (result == EGL_TRUE && num_config == 0) {
174  return true;
175  }
176  } else {
177  result = ::eglChooseConfig(display_, config_attributes, &config_, 1,
178  &num_config);
179 
180  if (result == EGL_TRUE && num_config > 0) {
181  return true;
182  }
183  }
184 
185  LogEGLError("Failed to choose EGL config");
186  return false;
187 }
188 
189 bool Manager::InitializeContexts() {
190  const EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
191 
192  auto const render_context =
193  ::eglCreateContext(display_, config_, EGL_NO_CONTEXT, context_attributes);
194  if (render_context == EGL_NO_CONTEXT) {
195  LogEGLError("Failed to create EGL render context");
196  return false;
197  }
198 
199  auto const resource_context =
200  ::eglCreateContext(display_, config_, render_context, context_attributes);
201  if (resource_context == EGL_NO_CONTEXT) {
202  LogEGLError("Failed to create EGL resource context");
203  return false;
204  }
205 
206  render_context_ = std::make_unique<Context>(display_, render_context);
207  resource_context_ = std::make_unique<Context>(display_, resource_context);
208  return true;
209 }
210 
211 bool Manager::InitializeDevice() {
212  const auto query_display_attrib_EXT =
213  reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
214  ::eglGetProcAddress("eglQueryDisplayAttribEXT"));
215  const auto query_device_attrib_EXT =
216  reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
217  ::eglGetProcAddress("eglQueryDeviceAttribEXT"));
218 
219  if (query_display_attrib_EXT == nullptr ||
220  query_device_attrib_EXT == nullptr) {
221  return false;
222  }
223 
224  EGLAttrib egl_device = 0;
225  EGLAttrib angle_device = 0;
226 
227  auto result = query_display_attrib_EXT(display_, EGL_DEVICE_EXT, &egl_device);
228  if (result != EGL_TRUE) {
229  return false;
230  }
231 
232  result = query_device_attrib_EXT(reinterpret_cast<EGLDeviceEXT>(egl_device),
233  EGL_D3D11_DEVICE_ANGLE, &angle_device);
234  if (result != EGL_TRUE) {
235  return false;
236  }
237 
238  resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device);
239  return true;
240 }
241 
242 void Manager::CleanUp() {
243  EGLBoolean result = EGL_FALSE;
244 
245  // Needs to be reset before destroying the contexts.
246  resolved_device_.Reset();
247 
248  // Needs to be reset before destroying the EGLDisplay.
249  render_context_.reset();
250  resource_context_.reset();
251 
252  if (display_ != EGL_NO_DISPLAY) {
253  // Display is reused between instances so only terminate display
254  // if destroying last instance
255  if (instance_count_ == 1) {
256  ::eglTerminate(display_);
257  }
258  display_ = EGL_NO_DISPLAY;
259  }
260 }
261 
262 bool Manager::IsValid() const {
263  return is_valid_;
264 }
265 
266 std::unique_ptr<WindowSurface> Manager::CreateWindowSurface(HWND hwnd,
267  size_t width,
268  size_t height) {
269  if (!hwnd || !is_valid_) {
270  return nullptr;
271  }
272 
273  // Disable ANGLE's automatic surface resizing and provide an explicit size.
274  // The surface will need to be destroyed and re-created if the HWND is
275  // resized.
276  const EGLint surface_attributes[] = {EGL_FIXED_SIZE_ANGLE,
277  EGL_TRUE,
278  EGL_WIDTH,
279  static_cast<EGLint>(width),
280  EGL_HEIGHT,
281  static_cast<EGLint>(height),
282  EGL_NONE};
283 
284  auto const surface = ::eglCreateWindowSurface(
285  display_, config_, static_cast<EGLNativeWindowType>(hwnd),
286  surface_attributes);
287  if (surface == EGL_NO_SURFACE) {
288  LogEGLError("Surface creation failed.");
289  return nullptr;
290  }
291 
292  return std::make_unique<WindowSurface>(display_, render_context_->GetHandle(),
293  surface, width, height);
294 }
295 
297  return ::eglGetCurrentContext() != EGL_NO_CONTEXT;
298 }
299 
300 EGLSurface Manager::CreateSurfaceFromHandle(EGLenum handle_type,
301  EGLClientBuffer handle,
302  const EGLint* attributes) const {
303  return ::eglCreatePbufferFromClientBuffer(display_, handle_type, handle,
304  config_, attributes);
305 }
306 
307 bool Manager::GetDevice(ID3D11Device** device) {
308  if (!resolved_device_) {
309  if (!InitializeDevice()) {
310  return false;
311  }
312  }
313 
314  resolved_device_.CopyTo(device);
315  return (resolved_device_ != nullptr);
316 }
317 
319  return render_context_.get();
320 }
321 
323  return resource_context_.get();
324 }
325 
326 } // namespace egl
327 } // namespace flutter
flutter::egl::Manager::render_context
virtual Context * render_context() const
Definition: manager.cc:318
flutter::egl::Manager::IsValid
bool IsValid() const
Definition: manager.cc:262
egl.h
flutter::egl::Manager::HasContextCurrent
bool HasContextCurrent()
Definition: manager.cc:296
flutter::egl::Context
Definition: context.h:20
flutter::egl::Manager::~Manager
virtual ~Manager()
Definition: manager.cc:44
flutter::egl::Manager::CreateSurfaceFromHandle
EGLSurface CreateSurfaceFromHandle(EGLenum handle_type, EGLClientBuffer handle, const EGLint *attributes) const
Definition: manager.cc:300
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::egl::LogEGLError
void LogEGLError(std::string_view message)
Log the last EGL error with an error message.
Definition: egl.cc:55
manager.h
flutter::egl::Manager::resource_context
virtual Context * resource_context() const
Definition: manager.cc:322
flutter::egl::Manager::Manager
Manager(bool enable_impeller)
Definition: manager.cc:26
flutter::egl::Manager::GetDevice
bool GetDevice(ID3D11Device **device)
Definition: manager.cc:307
flutter::egl::Manager::Create
static std::unique_ptr< Manager > Create(bool enable_impeller)
Definition: manager.cc:17
flutter::egl::Manager::CreateWindowSurface
virtual std::unique_ptr< WindowSurface > CreateWindowSurface(HWND hwnd, size_t width, size_t height)
Definition: manager.cc:266