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(GpuPreference gpu_preference) {
18  std::unique_ptr<Manager> manager;
19  manager.reset(new Manager(gpu_preference));
20  if (!manager->IsValid()) {
21  return nullptr;
22  }
23  return std::move(manager);
24 }
25 
27  ++instance_count_;
28 
29  if (!InitializeDisplay(gpu_preference)) {
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(GpuPreference gpu_preference) {
50  // If the request for a low power GPU is provided,
51  // we will attempt to select GPU explicitly, via ANGLE extension
52  // that allows to specify the GPU to use via LUID.
53  std::optional<LUID> luid = std::nullopt;
54  switch (gpu_preference) {
56  luid = GetLowPowerGpuLuid();
57  break;
60  break;
62  break;
63  }
64 
65  // These are preferred display attributes and request ANGLE's D3D11
66  // renderer (use only in case of valid LUID returned from above).
67  const EGLint d3d11_display_attributes_with_luid[] = {
68  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
69  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
70 
71  // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
72  // enable ANGLE to automatically call the IDXGIDevice3::Trim method on
73  // behalf of the application when it gets suspended.
74  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
75  EGL_TRUE,
76 
77  // This extension allows angle to render directly on a D3D swapchain
78  // in the correct orientation on D3D11.
79  EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
80  EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
81 
82  // Specify the LUID of the GPU to use.
83  EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE,
84  static_cast<EGLint>(luid.has_value() ? luid->HighPart : 0),
85  EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE,
86  static_cast<EGLint>(luid.has_value() ? luid->LowPart : 0),
87  EGL_NONE,
88  };
89 
90  // These are preferred display attributes and request ANGLE's D3D11
91  // renderer. eglInitialize will only succeed with these attributes if the
92  // hardware supports D3D11 Feature Level 10_0+.
93  const EGLint d3d11_display_attributes[] = {
94  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
95  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
96 
97  // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
98  // enable ANGLE to automatically call the IDXGIDevice3::Trim method on
99  // behalf of the application when it gets suspended.
100  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
101  EGL_TRUE,
102 
103  // This extension allows angle to render directly on a D3D swapchain
104  // in the correct orientation on D3D11.
105  EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
106  EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
107 
108  EGL_NONE,
109  };
110 
111  // These are used to request ANGLE's D3D11 renderer, with D3D11 Feature
112  // Level 9_3.
113  const EGLint d3d11_fl_9_3_display_attributes[] = {
114  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
115  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
116  EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
117  9,
118  EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
119  3,
120  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
121  EGL_TRUE,
122  EGL_NONE,
123  };
124 
125  // These attributes request D3D11 WARP (software rendering fallback) in case
126  // hardware-backed D3D11 is unavailable.
127  const EGLint d3d11_warp_display_attributes[] = {
128  EGL_PLATFORM_ANGLE_TYPE_ANGLE,
129  EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
130  EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
131  EGL_TRUE,
132  EGL_NONE,
133  };
134 
135  std::vector<const EGLint*> display_attributes_configs;
136 
137  if (luid) {
138  // If LUID value is present, obtain an adapter with that luid.
139  display_attributes_configs.push_back(d3d11_display_attributes_with_luid);
140  }
141  display_attributes_configs.push_back(d3d11_display_attributes);
142  display_attributes_configs.push_back(d3d11_fl_9_3_display_attributes);
143  display_attributes_configs.push_back(d3d11_warp_display_attributes);
144 
145  PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT =
146  reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
147  ::eglGetProcAddress("eglGetPlatformDisplayEXT"));
148  if (!egl_get_platform_display_EXT) {
149  LogEGLError("eglGetPlatformDisplayEXT not available");
150  return false;
151  }
152 
153  // Attempt to initialize ANGLE's renderer in order of: D3D11, D3D11 Feature
154  // Level 9_3 and finally D3D11 WARP.
155  for (auto config : display_attributes_configs) {
156  bool is_last = (config == display_attributes_configs.back());
157 
158  display_ = egl_get_platform_display_EXT(EGL_PLATFORM_ANGLE_ANGLE,
159  EGL_DEFAULT_DISPLAY, config);
160 
161  if (display_ == EGL_NO_DISPLAY) {
162  if (is_last) {
163  LogEGLError("Failed to get a compatible EGLdisplay");
164  return false;
165  }
166 
167  // Try the next config.
168  continue;
169  }
170 
171  if (::eglInitialize(display_, nullptr, nullptr) == EGL_FALSE) {
172  if (is_last) {
173  LogEGLError("Failed to initialize EGL via ANGLE");
174  return false;
175  }
176 
177  // Try the next config.
178  continue;
179  }
180 
181  return true;
182  }
183 
184  FML_UNREACHABLE();
185 }
186 
187 bool Manager::InitializeConfig() {
188  const EGLint config_attributes[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
189  EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
190  EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8,
191  EGL_NONE};
192 
193  EGLint num_config = 0;
194 
195  EGLBoolean result =
196  ::eglChooseConfig(display_, config_attributes, &config_, 1, &num_config);
197 
198  if (result == EGL_TRUE && num_config > 0) {
199  return true;
200  }
201 
202  LogEGLError("Failed to choose EGL config");
203  return false;
204 }
205 
206 bool Manager::InitializeContexts() {
207  const EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
208 
209  auto const render_context =
210  ::eglCreateContext(display_, config_, EGL_NO_CONTEXT, context_attributes);
211  if (render_context == EGL_NO_CONTEXT) {
212  LogEGLError("Failed to create EGL render context");
213  return false;
214  }
215 
216  auto const resource_context =
217  ::eglCreateContext(display_, config_, render_context, context_attributes);
218  if (resource_context == EGL_NO_CONTEXT) {
219  LogEGLError("Failed to create EGL resource context");
220  return false;
221  }
222 
223  render_context_ = std::make_unique<Context>(display_, render_context);
224  resource_context_ = std::make_unique<Context>(display_, resource_context);
225  return true;
226 }
227 
228 bool Manager::InitializeDevice() {
229  const auto query_display_attrib_EXT =
230  reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
231  ::eglGetProcAddress("eglQueryDisplayAttribEXT"));
232  const auto query_device_attrib_EXT =
233  reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
234  ::eglGetProcAddress("eglQueryDeviceAttribEXT"));
235 
236  if (query_display_attrib_EXT == nullptr ||
237  query_device_attrib_EXT == nullptr) {
238  return false;
239  }
240 
241  EGLAttrib egl_device = 0;
242  EGLAttrib angle_device = 0;
243 
244  auto result = query_display_attrib_EXT(display_, EGL_DEVICE_EXT, &egl_device);
245  if (result != EGL_TRUE) {
246  return false;
247  }
248 
249  result = query_device_attrib_EXT(reinterpret_cast<EGLDeviceEXT>(egl_device),
250  EGL_D3D11_DEVICE_ANGLE, &angle_device);
251  if (result != EGL_TRUE) {
252  return false;
253  }
254 
255  resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device);
256  return true;
257 }
258 
259 void Manager::CleanUp() {
260  EGLBoolean result = EGL_FALSE;
261 
262  // Needs to be reset before destroying the contexts.
263  resolved_device_.Reset();
264 
265  // Needs to be reset before destroying the EGLDisplay.
266  render_context_.reset();
267  resource_context_.reset();
268 
269  if (display_ != EGL_NO_DISPLAY) {
270  // Display is reused between instances so only terminate display
271  // if destroying last instance
272  if (instance_count_ == 1) {
273  ::eglTerminate(display_);
274  }
275  display_ = EGL_NO_DISPLAY;
276  }
277 }
278 
279 bool Manager::IsValid() const {
280  return is_valid_;
281 }
282 
283 std::unique_ptr<WindowSurface> Manager::CreateWindowSurface(HWND hwnd,
284  size_t width,
285  size_t height) {
286  if (!hwnd || !is_valid_) {
287  return nullptr;
288  }
289 
290  // Disable ANGLE's automatic surface resizing and provide an explicit size.
291  // The surface will need to be destroyed and re-created if the HWND is
292  // resized.
293  const EGLint surface_attributes[] = {EGL_FIXED_SIZE_ANGLE,
294  EGL_TRUE,
295  EGL_WIDTH,
296  static_cast<EGLint>(width),
297  EGL_HEIGHT,
298  static_cast<EGLint>(height),
299  EGL_NONE};
300 
301  auto const surface = ::eglCreateWindowSurface(
302  display_, config_, static_cast<EGLNativeWindowType>(hwnd),
303  surface_attributes);
304  if (surface == EGL_NO_SURFACE) {
305  LogEGLError("Surface creation failed.");
306  return nullptr;
307  }
308 
309  return std::make_unique<WindowSurface>(display_, render_context_->GetHandle(),
310  surface, width, height);
311 }
312 
314  return ::eglGetCurrentContext() != EGL_NO_CONTEXT;
315 }
316 
317 EGLSurface Manager::CreateSurfaceFromHandle(EGLenum handle_type,
318  EGLClientBuffer handle,
319  const EGLint* attributes) const {
320  return ::eglCreatePbufferFromClientBuffer(display_, handle_type, handle,
321  config_, attributes);
322 }
323 
324 bool Manager::GetDevice(ID3D11Device** device) {
325  if (!resolved_device_) {
326  if (!InitializeDevice()) {
327  return false;
328  }
329  }
330 
331  resolved_device_.CopyTo(device);
332  return (resolved_device_ != nullptr);
333 }
334 
336  return render_context_.get();
337 }
338 
340  return resource_context_.get();
341 }
342 
343 std::optional<LUID> Manager::GetGpuLuidByPreference(
344  DXGI_GPU_PREFERENCE preference) {
345  Microsoft::WRL::ComPtr<IDXGIFactory1> factory1;
346  HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&factory1));
347  if (FAILED(hr)) {
348  return std::nullopt;
349  }
350 
351  Microsoft::WRL::ComPtr<IDXGIFactory6> factory6;
352  hr = factory1->QueryInterface(IID_PPV_ARGS(&factory6));
353  if (FAILED(hr)) {
354  // No support for IDXGIFactory6, so we will not use the selected GPU.
355  // We will follow with the default ANGLE selection.
356  return std::nullopt;
357  }
358 
359  Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
360  hr = factory6->EnumAdapterByGpuPreference(0, preference,
361  IID_PPV_ARGS(&adapter));
362  if (FAILED(hr) || !adapter) {
363  return std::nullopt;
364  }
365 
366  // Get the LUID of the adapter.
367  DXGI_ADAPTER_DESC desc;
368  hr = adapter->GetDesc(&desc);
369  if (FAILED(hr)) {
370  return std::nullopt;
371  }
372 
373  return desc.AdapterLuid;
374 }
375 
376 std::optional<LUID> Manager::GetLowPowerGpuLuid() {
377  return GetGpuLuidByPreference(DXGI_GPU_PREFERENCE_MINIMUM_POWER);
378 }
379 
380 std::optional<LUID> Manager::GetHighPerformanceGpuLuid() {
381  return GetGpuLuidByPreference(DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE);
382 }
383 
384 } // namespace egl
385 } // namespace flutter
virtual Context * resource_context() const
Definition: manager.cc:339
Manager(GpuPreference gpu_preference)
Definition: manager.cc:26
virtual ~Manager()
Definition: manager.cc:44
bool HasContextCurrent()
Definition: manager.cc:313
EGLSurface CreateSurfaceFromHandle(EGLenum handle_type, EGLClientBuffer handle, const EGLint *attributes) const
Definition: manager.cc:317
static std::optional< LUID > GetLowPowerGpuLuid()
Definition: manager.cc:376
virtual Context * render_context() const
Definition: manager.cc:335
static std::optional< LUID > GetHighPerformanceGpuLuid()
Definition: manager.cc:380
static std::unique_ptr< Manager > Create(GpuPreference gpu_preference)
Definition: manager.cc:17
virtual std::unique_ptr< WindowSurface > CreateWindowSurface(HWND hwnd, size_t width, size_t height)
Definition: manager.cc:283
bool GetDevice(ID3D11Device **device)
Definition: manager.cc:324
bool IsValid() const
Definition: manager.cc:279
void LogEGLError(std::string_view message)
Log the last EGL error with an error message.
Definition: egl.cc:55