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