| Flutter Windows Embedder |

angle_surface_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/angle_surface_manager.h"
6
7 #include <vector>
8
9 #include "flutter/fml/logging.h"
10
11 // Logs an EGL error to stderr. This automatically calls eglGetError()
12 // and logs the error code.
13 static void LogEglError(std::string message) {
14 EGLint error = eglGetError();
15 FML_LOG(ERROR) << "EGL: " << message;
16 FML_LOG(ERROR) << "EGL: eglGetError returned " << error;
17 }
18
19 namespace flutter {
20
21 int AngleSurfaceManager::instance_count_ = 0;
22
23 std::unique_ptr<AngleSurfaceManager> AngleSurfaceManager::Create(
24 bool enable_impeller) {
25 std::unique_ptr<AngleSurfaceManager> manager;
26 manager.reset(new AngleSurfaceManager(enable_impeller));
27 if (!manager->initialize_succeeded_) {
28 return nullptr;
29 }
30 return std::move(manager);
31 }
32
33 AngleSurfaceManager::AngleSurfaceManager(bool enable_impeller)
34 : egl_config_(nullptr),
35 egl_display_(EGL_NO_DISPLAY),
36 egl_context_(EGL_NO_CONTEXT) {
37 initialize_succeeded_ = Initialize(enable_impeller);
38 ++instance_count_;
39 }
40
41 AngleSurfaceManager::~AngleSurfaceManager() {
42 CleanUp();
43 --instance_count_;
44 }
45
46 bool AngleSurfaceManager::InitializeEGL(
47 PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT,
48 const EGLint* config,
49 bool should_log) {
50 egl_display_ = egl_get_platform_display_EXT(EGL_PLATFORM_ANGLE_ANGLE,
51 EGL_DEFAULT_DISPLAY, config);
52
53 if (egl_display_ == EGL_NO_DISPLAY) {
54 if (should_log) {
55 LogEglError("Failed to get a compatible EGLdisplay");
56 }
57 return false;
58 }
59
60 if (eglInitialize(egl_display_, nullptr, nullptr) == EGL_FALSE) {
61 if (should_log) {
62 LogEglError("Failed to initialize EGL via ANGLE");
63 }
64 return false;
65 }
66
67 return true;
68 }
69
70 bool AngleSurfaceManager::Initialize(bool enable_impeller) {
71 const EGLint config_attributes[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
72 EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
73 EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8,
74 EGL_NONE};
75
76 const EGLint impeller_config_attributes[] = {
77 EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
78 EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8,
79 EGL_SAMPLE_BUFFERS, 1, EGL_SAMPLES, 4, EGL_NONE};
80 const EGLint impeller_config_attributes_no_msaa[] = {
81 EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
82 EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 8,
83 EGL_NONE};
84
85 const EGLint display_context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
86 EGL_NONE};
87
88 // These are preferred display attributes and request ANGLE's D3D11
89 // renderer. eglInitialize will only succeed with these attributes if the
90 // hardware supports D3D11 Feature Level 10_0+.
91 const EGLint d3d11_display_attributes[] = {
92 EGL_PLATFORM_ANGLE_TYPE_ANGLE,
93 EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
94
95 // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that will
96 // enable ANGLE to automatically call the IDXGIDevice3::Trim method on
97 // behalf of the application when it gets suspended.
98 EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
99 EGL_TRUE,
100
101 // This extension allows angle to render directly on a D3D swapchain
102 // in the correct orientation on D3D11.
103 EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
104 EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
105
106 EGL_NONE,
107 };
108
109 // These are used to request ANGLE's D3D11 renderer, with D3D11 Feature
110 // Level 9_3.
111 const EGLint d3d11_fl_9_3_display_attributes[] = {
112 EGL_PLATFORM_ANGLE_TYPE_ANGLE,
113 EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
114 EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
115 9,
116 EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
117 3,
118 EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
119 EGL_TRUE,
120 EGL_NONE,
121 };
122
123 // These attributes request D3D11 WARP (software rendering fallback) in case
124 // hardware-backed D3D11 is unavailable.
125 const EGLint d3d11_warp_display_attributes[] = {
126 EGL_PLATFORM_ANGLE_TYPE_ANGLE,
127 EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
128 EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
129 EGL_TRUE,
130 EGL_NONE,
131 };
132
133 std::vector<const EGLint*> display_attributes_configs = {
134 d3d11_display_attributes,
135 d3d11_fl_9_3_display_attributes,
136 d3d11_warp_display_attributes,
137 };
138
139 PFNEGLGETPLATFORMDISPLAYEXTPROC egl_get_platform_display_EXT =
140 reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
141 eglGetProcAddress("eglGetPlatformDisplayEXT"));
142 if (!egl_get_platform_display_EXT) {
143 LogEglError("eglGetPlatformDisplayEXT not available");
144 return false;
145 }
146
147 // Attempt to initialize ANGLE's renderer in order of: D3D11, D3D11 Feature
148 // Level 9_3 and finally D3D11 WARP.
149 for (auto config : display_attributes_configs) {
150 bool should_log = (config == display_attributes_configs.back());
151 if (InitializeEGL(egl_get_platform_display_EXT, config, should_log)) {
152 break;
153 }
154 }
155
156 EGLint numConfigs = 0;
157 if (enable_impeller) {
158 // First try the MSAA configuration.
159 if ((eglChooseConfig(egl_display_, impeller_config_attributes, &egl_config_,
160 1, &numConfigs) == EGL_FALSE) ||
161 (numConfigs == 0)) {
162 // Next fall back to disabled MSAA.
163 if ((eglChooseConfig(egl_display_, impeller_config_attributes_no_msaa,
164 &egl_config_, 1, &numConfigs) == EGL_FALSE) ||
165 (numConfigs == 0)) {
166 LogEglError("Failed to choose first context");
167 return false;
168 }
169 }
170 } else {
171 if ((eglChooseConfig(egl_display_, config_attributes, &egl_config_, 1,
172 &numConfigs) == EGL_FALSE) ||
173 (numConfigs == 0)) {
174 LogEglError("Failed to choose first context");
175 return false;
176 }
177 }
178
179 egl_context_ = eglCreateContext(egl_display_, egl_config_, EGL_NO_CONTEXT,
180 display_context_attributes);
181 if (egl_context_ == EGL_NO_CONTEXT) {
182 LogEglError("Failed to create EGL context");
183 return false;
184 }
185
186 egl_resource_context_ = eglCreateContext(
187 egl_display_, egl_config_, egl_context_, display_context_attributes);
188
189 if (egl_resource_context_ == EGL_NO_CONTEXT) {
190 LogEglError("Failed to create EGL resource context");
191 return false;
192 }
193
194 return true;
195 }
196
197 void AngleSurfaceManager::CleanUp() {
198 EGLBoolean result = EGL_FALSE;
199
200 // Needs to be reset before destroying the EGLContext.
201 resolved_device_.Reset();
202
203 if (egl_display_ != EGL_NO_DISPLAY && egl_context_ != EGL_NO_CONTEXT) {
204 result = eglDestroyContext(egl_display_, egl_context_);
205 egl_context_ = EGL_NO_CONTEXT;
206
207 if (result == EGL_FALSE) {
208 LogEglError("Failed to destroy context");
209 }
210 }
211
212 if (egl_display_ != EGL_NO_DISPLAY &&
213 egl_resource_context_ != EGL_NO_CONTEXT) {
214 result = eglDestroyContext(egl_display_, egl_resource_context_);
215 egl_resource_context_ = EGL_NO_CONTEXT;
216
217 if (result == EGL_FALSE) {
218 LogEglError("Failed to destroy resource context");
219 }
220 }
221
222 if (egl_display_ != EGL_NO_DISPLAY) {
223 // Display is reused between instances so only terminate display
224 // if destroying last instance
225 if (instance_count_ == 1) {
226 eglTerminate(egl_display_);
227 }
228 egl_display_ = EGL_NO_DISPLAY;
229 }
230 }
231
232 bool AngleSurfaceManager::CreateSurface(HWND hwnd,
233 EGLint width,
234 EGLint height) {
235 if (!hwnd || !initialize_succeeded_) {
236 return false;
237 }
238
239 EGLSurface surface = EGL_NO_SURFACE;
240
241 // Disable ANGLE's automatic surface resizing and provide an explicit size.
242 // The surface will need to be destroyed and re-created if the HWND is
243 // resized.
244 const EGLint surfaceAttributes[] = {
245 EGL_FIXED_SIZE_ANGLE, EGL_TRUE, EGL_WIDTH, width,
246 EGL_HEIGHT, height, EGL_NONE};
247
248 surface = eglCreateWindowSurface(egl_display_, egl_config_,
249 static_cast<EGLNativeWindowType>(hwnd),
250 surfaceAttributes);
251 if (surface == EGL_NO_SURFACE) {
252 LogEglError("Surface creation failed.");
253 return false;
254 }
255
256 surface_width_ = width;
257 surface_height_ = height;
258 render_surface_ = surface;
259 return true;
260 }
261
262 void AngleSurfaceManager::ResizeSurface(HWND hwnd,
263 EGLint width,
264 EGLint height,
265 bool vsync_enabled) {
266 EGLint existing_width, existing_height;
267 GetSurfaceDimensions(&existing_width, &existing_height);
268 if (width != existing_width || height != existing_height) {
269 surface_width_ = width;
270 surface_height_ = height;
271
272 // TODO: Destroying the surface and re-creating it is expensive.
273 // Ideally this would use ANGLE's automatic surface sizing instead.
274 // See: https://github.com/flutter/flutter/issues/79427
275 ClearContext();
276 DestroySurface();
277 if (!CreateSurface(hwnd, width, height)) {
278 FML_LOG(ERROR)
279 << "AngleSurfaceManager::ResizeSurface failed to create surface";
280 }
281 }
282
283 SetVSyncEnabled(vsync_enabled);
284 }
285
286 void AngleSurfaceManager::GetSurfaceDimensions(EGLint* width, EGLint* height) {
287 if (render_surface_ == EGL_NO_SURFACE || !initialize_succeeded_) {
288 *width = 0;
289 *height = 0;
290 return;
291 }
292
293 // This avoids eglQuerySurface as ideally surfaces would be automatically
294 // sized by ANGLE to avoid expensive surface destroy & re-create. With
295 // automatic sizing, ANGLE could resize the surface before Flutter asks it to,
296 // which would break resize redraw synchronization.
297 *width = surface_width_;
298 *height = surface_height_;
299 }
300
301 void AngleSurfaceManager::DestroySurface() {
302 if (egl_display_ != EGL_NO_DISPLAY && render_surface_ != EGL_NO_SURFACE) {
303 eglDestroySurface(egl_display_, render_surface_);
304 }
305 render_surface_ = EGL_NO_SURFACE;
306 }
307
308 bool AngleSurfaceManager::HasContextCurrent() {
309 return eglGetCurrentContext() != EGL_NO_CONTEXT;
310 }
311
312 bool AngleSurfaceManager::MakeCurrent() {
313 return (eglMakeCurrent(egl_display_, render_surface_, render_surface_,
314 egl_context_) == EGL_TRUE);
315 }
316
317 bool AngleSurfaceManager::ClearCurrent() {
318 return (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE,
319 EGL_NO_CONTEXT) == EGL_TRUE);
320 }
321
322 bool AngleSurfaceManager::ClearContext() {
323 return (eglMakeCurrent(egl_display_, nullptr, nullptr, egl_context_) ==
324 EGL_TRUE);
325 }
326
327 bool AngleSurfaceManager::MakeResourceCurrent() {
328 return (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE,
329 egl_resource_context_) == EGL_TRUE);
330 }
331
332 EGLBoolean AngleSurfaceManager::SwapBuffers() {
333 return (eglSwapBuffers(egl_display_, render_surface_));
334 }
335
336 EGLSurface AngleSurfaceManager::CreateSurfaceFromHandle(
337 EGLenum handle_type,
338 EGLClientBuffer handle,
339 const EGLint* attributes) const {
340 return eglCreatePbufferFromClientBuffer(egl_display_, handle_type, handle,
341 egl_config_, attributes);
342 }
343
344 void AngleSurfaceManager::SetVSyncEnabled(bool enabled) {
345 if (!MakeCurrent()) {
346 LogEglError("Unable to make surface current to update the swap interval");
347 return;
348 }
349
350 // OpenGL swap intervals can be used to prevent screen tearing.
351 // If enabled, the raster thread blocks until the v-blank.
352 // This is unnecessary if DWM composition is enabled.
353 // See: https://www.khronos.org/opengl/wiki/Swap_Interval
354 // See: https://learn.microsoft.com/windows/win32/dwm/composition-ovw
355 if (eglSwapInterval(egl_display_, enabled ? 1 : 0) != EGL_TRUE) {
356 LogEglError("Unable to update the swap interval");
357 return;
358 }
359 }
360
361 bool AngleSurfaceManager::GetDevice(ID3D11Device** device) {
362 if (!resolved_device_) {
363 PFNEGLQUERYDISPLAYATTRIBEXTPROC egl_query_display_attrib_EXT =
364 reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
365 eglGetProcAddress("eglQueryDisplayAttribEXT"));
366
367 PFNEGLQUERYDEVICEATTRIBEXTPROC egl_query_device_attrib_EXT =
368 reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
369 eglGetProcAddress("eglQueryDeviceAttribEXT"));
370
371 if (!egl_query_display_attrib_EXT || !egl_query_device_attrib_EXT) {
372 return false;
373 }
374
375 EGLAttrib egl_device = 0;
376 EGLAttrib angle_device = 0;
377 if (egl_query_display_attrib_EXT(egl_display_, EGL_DEVICE_EXT,
378 &egl_device) == EGL_TRUE) {
379 if (egl_query_device_attrib_EXT(
380 reinterpret_cast<EGLDeviceEXT>(egl_device),
381 EGL_D3D11_DEVICE_ANGLE, &angle_device) == EGL_TRUE) {
382 resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device);
383 }
384 }
385 }
386
387 resolved_device_.CopyTo(device);
388 return (resolved_device_ != nullptr);
389 }
390
391 } // namespace flutter
flutter::AngleSurfaceManager::ClearContext
bool ClearContext()
Definition: angle_surface_manager.cc:322
flutter::AngleSurfaceManager::CreateSurface
virtual bool CreateSurface(HWND hwnd, EGLint width, EGLint height)
Definition: angle_surface_manager.cc:232
flutter::AngleSurfaceManager::HasContextCurrent
bool HasContextCurrent()
Definition: angle_surface_manager.cc:308
angle_surface_manager.h
flutter::AngleSurfaceManager::ClearCurrent
virtual bool ClearCurrent()
Definition: angle_surface_manager.cc:317
LogEglError
static void LogEglError(std::string message)
Definition: angle_surface_manager.cc:13
flutter::AngleSurfaceManager::AngleSurfaceManager
AngleSurfaceManager(bool enable_impeller)
Definition: angle_surface_manager.cc:33
flutter::AngleSurfaceManager::DestroySurface
virtual void DestroySurface()
Definition: angle_surface_manager.cc:301
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::AngleSurfaceManager::MakeCurrent
virtual bool MakeCurrent()
Definition: angle_surface_manager.cc:312
flutter::AngleSurfaceManager::~AngleSurfaceManager
virtual ~AngleSurfaceManager()
Definition: angle_surface_manager.cc:41
flutter::AngleSurfaceManager::MakeResourceCurrent
bool MakeResourceCurrent()
Definition: angle_surface_manager.cc:327
flutter::AngleSurfaceManager::GetDevice
bool GetDevice(ID3D11Device **device)
Definition: angle_surface_manager.cc:361
flutter::AngleSurfaceManager::SwapBuffers
EGLBoolean SwapBuffers()
Definition: angle_surface_manager.cc:332
flutter::AngleSurfaceManager::SetVSyncEnabled
virtual void SetVSyncEnabled(bool enabled)
Definition: angle_surface_manager.cc:344
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter::AngleSurfaceManager::Create
static std::unique_ptr< AngleSurfaceManager > Create(bool enable_impeller)
Definition: angle_surface_manager.cc:23
flutter::AngleSurfaceManager::CreateSurfaceFromHandle
EGLSurface CreateSurfaceFromHandle(EGLenum handle_type, EGLClientBuffer handle, const EGLint *attributes) const
Definition: angle_surface_manager.cc:336
flutter::AngleSurfaceManager::GetSurfaceDimensions
void GetSurfaceDimensions(EGLint *width, EGLint *height)
Definition: angle_surface_manager.cc:286
flutter::AngleSurfaceManager::ResizeSurface
virtual void ResizeSurface(HWND hwnd, EGLint width, EGLint height, bool enable_vsync)
Definition: angle_surface_manager.cc:262