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() {
18  std::unique_ptr<Manager> manager;
19  manager.reset(new Manager());
20  if (!manager->IsValid()) {
21  return nullptr;
22  }
23  return std::move(manager);
24 }
25 
27  ++instance_count_;
28 
29  if (!InitializeDisplay()) {
30  return;
31  }
32 
33  if (!InitializeConfig()) {
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() {
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  EGLint num_config = 0;
150 
151  EGLBoolean result =
152  ::eglChooseConfig(display_, config_attributes, &config_, 1, &num_config);
153 
154  if (result == EGL_TRUE && num_config > 0) {
155  return true;
156  }
157 
158  LogEGLError("Failed to choose EGL config");
159  return false;
160 }
161 
162 bool Manager::InitializeContexts() {
163  const EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
164 
165  auto const render_context =
166  ::eglCreateContext(display_, config_, EGL_NO_CONTEXT, context_attributes);
167  if (render_context == EGL_NO_CONTEXT) {
168  LogEGLError("Failed to create EGL render context");
169  return false;
170  }
171 
172  auto const resource_context =
173  ::eglCreateContext(display_, config_, render_context, context_attributes);
174  if (resource_context == EGL_NO_CONTEXT) {
175  LogEGLError("Failed to create EGL resource context");
176  return false;
177  }
178 
179  render_context_ = std::make_unique<Context>(display_, render_context);
180  resource_context_ = std::make_unique<Context>(display_, resource_context);
181  return true;
182 }
183 
184 bool Manager::InitializeDevice() {
185  const auto query_display_attrib_EXT =
186  reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
187  ::eglGetProcAddress("eglQueryDisplayAttribEXT"));
188  const auto query_device_attrib_EXT =
189  reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
190  ::eglGetProcAddress("eglQueryDeviceAttribEXT"));
191 
192  if (query_display_attrib_EXT == nullptr ||
193  query_device_attrib_EXT == nullptr) {
194  return false;
195  }
196 
197  EGLAttrib egl_device = 0;
198  EGLAttrib angle_device = 0;
199 
200  auto result = query_display_attrib_EXT(display_, EGL_DEVICE_EXT, &egl_device);
201  if (result != EGL_TRUE) {
202  return false;
203  }
204 
205  result = query_device_attrib_EXT(reinterpret_cast<EGLDeviceEXT>(egl_device),
206  EGL_D3D11_DEVICE_ANGLE, &angle_device);
207  if (result != EGL_TRUE) {
208  return false;
209  }
210 
211  resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device);
212  return true;
213 }
214 
215 void Manager::CleanUp() {
216  EGLBoolean result = EGL_FALSE;
217 
218  // Needs to be reset before destroying the contexts.
219  resolved_device_.Reset();
220 
221  // Needs to be reset before destroying the EGLDisplay.
222  render_context_.reset();
223  resource_context_.reset();
224 
225  if (display_ != EGL_NO_DISPLAY) {
226  // Display is reused between instances so only terminate display
227  // if destroying last instance
228  if (instance_count_ == 1) {
229  ::eglTerminate(display_);
230  }
231  display_ = EGL_NO_DISPLAY;
232  }
233 }
234 
235 bool Manager::IsValid() const {
236  return is_valid_;
237 }
238 
239 std::unique_ptr<WindowSurface> Manager::CreateWindowSurface(HWND hwnd,
240  size_t width,
241  size_t height) {
242  if (!hwnd || !is_valid_) {
243  return nullptr;
244  }
245 
246  // Disable ANGLE's automatic surface resizing and provide an explicit size.
247  // The surface will need to be destroyed and re-created if the HWND is
248  // resized.
249  const EGLint surface_attributes[] = {EGL_FIXED_SIZE_ANGLE,
250  EGL_TRUE,
251  EGL_WIDTH,
252  static_cast<EGLint>(width),
253  EGL_HEIGHT,
254  static_cast<EGLint>(height),
255  EGL_NONE};
256 
257  auto const surface = ::eglCreateWindowSurface(
258  display_, config_, static_cast<EGLNativeWindowType>(hwnd),
259  surface_attributes);
260  if (surface == EGL_NO_SURFACE) {
261  LogEGLError("Surface creation failed.");
262  return nullptr;
263  }
264 
265  return std::make_unique<WindowSurface>(display_, render_context_->GetHandle(),
266  surface, width, height);
267 }
268 
270  return ::eglGetCurrentContext() != EGL_NO_CONTEXT;
271 }
272 
273 EGLSurface Manager::CreateSurfaceFromHandle(EGLenum handle_type,
274  EGLClientBuffer handle,
275  const EGLint* attributes) const {
276  return ::eglCreatePbufferFromClientBuffer(display_, handle_type, handle,
277  config_, attributes);
278 }
279 
280 bool Manager::GetDevice(ID3D11Device** device) {
281  if (!resolved_device_) {
282  if (!InitializeDevice()) {
283  return false;
284  }
285  }
286 
287  resolved_device_.CopyTo(device);
288  return (resolved_device_ != nullptr);
289 }
290 
292  return render_context_.get();
293 }
294 
296  return resource_context_.get();
297 }
298 
299 } // namespace egl
300 } // namespace flutter
flutter::egl::Manager::render_context
virtual Context * render_context() const
Definition: manager.cc:291
flutter::egl::Manager::IsValid
bool IsValid() const
Definition: manager.cc:235
flutter::egl::Manager::Manager
Manager()
Definition: manager.cc:26
egl.h
flutter::egl::Manager::HasContextCurrent
bool HasContextCurrent()
Definition: manager.cc:269
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:273
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
flutter::egl::Manager::Create
static std::unique_ptr< Manager > Create()
Definition: manager.cc:17
manager.h
flutter::egl::Manager::resource_context
virtual Context * resource_context() const
Definition: manager.cc:295
flutter::egl::Manager::GetDevice
bool GetDevice(ID3D11Device **device)
Definition: manager.cc:280
flutter::egl::Manager::CreateWindowSurface
virtual std::unique_ptr< WindowSurface > CreateWindowSurface(HWND hwnd, size_t width, size_t height)
Definition: manager.cc:239