| 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(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
44 Manager::~Manager() {
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
296 bool Manager::HasContextCurrent() {
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
318 Context* Manager::render_context() const {
319 return render_context_.get();
320 }
321
322 Context* Manager::resource_context() const {
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