| 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
5 #include "flutter/shell/platform/windows/egl/manager.h"
6
7 #include <vector>
8
9 #include "flutter/fml/logging.h"
10 #include "flutter/shell/platform/windows/egl/egl.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
26 Manager::Manager(GpuPreference gpu_preference) {
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
44 Manager::~Manager() {
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) {
55 case GpuPreference::LowPowerPreference:
56 luid = GetLowPowerGpuLuid();
57 break;
58 case GpuPreference::HighPerformancePreference:
59 luid = GetHighPerformanceGpuLuid();
60 break;
61 case GpuPreference::NoPreference:
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
313 bool Manager::HasContextCurrent() {
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
335 Context* Manager::render_context() const {
336 return render_context_.get();
337 }
338
339 Context* Manager::resource_context() const {
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
flutter::egl::Context
Definition: context.h:20
flutter::egl::Manager::resource_context
virtual Context * resource_context() const
Definition: manager.cc:339
flutter::egl::Manager::Manager
Manager(GpuPreference gpu_preference)
Definition: manager.cc:26
flutter::egl::Manager::~Manager
virtual ~Manager()
Definition: manager.cc:44
flutter::egl::Manager::HasContextCurrent
bool HasContextCurrent()
Definition: manager.cc:313
flutter::egl::Manager::CreateSurfaceFromHandle
EGLSurface CreateSurfaceFromHandle(EGLenum handle_type, EGLClientBuffer handle, const EGLint *attributes) const
Definition: manager.cc:317
flutter::egl::Manager::GetLowPowerGpuLuid
static std::optional< LUID > GetLowPowerGpuLuid()
Definition: manager.cc:376
flutter::egl::Manager::render_context
virtual Context * render_context() const
Definition: manager.cc:335
flutter::egl::Manager::GetHighPerformanceGpuLuid
static std::optional< LUID > GetHighPerformanceGpuLuid()
Definition: manager.cc:380
flutter::egl::Manager::Create
static std::unique_ptr< Manager > Create(GpuPreference gpu_preference)
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:283
flutter::egl::Manager::GetDevice
bool GetDevice(ID3D11Device **device)
Definition: manager.cc:324
flutter::egl::Manager::IsValid
bool IsValid() const
Definition: manager.cc:279
egl.h
manager.h
flutter::egl::LogEGLError
void LogEGLError(std::string_view message)
Log the last EGL error with an error message.
Definition: egl.cc:55
flutter::egl::GpuPreference
GpuPreference
Definition: manager.h:32
flutter::egl::GpuPreference::HighPerformancePreference
@ HighPerformancePreference
flutter::egl::GpuPreference::NoPreference
@ NoPreference
flutter::egl::GpuPreference::LowPowerPreference
@ LowPowerPreference
flutter
Definition: accessibility_bridge_windows.cc:11