| 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(WindowsRenderTarget* render_target,
233 EGLint width,
234 EGLint height,
235 bool vsync_enabled) {
236 if (!render_target || !initialize_succeeded_) {
237 return false;
238 }
239
240 EGLSurface surface = EGL_NO_SURFACE;
241
242 const EGLint surfaceAttributes[] = {
243 EGL_FIXED_SIZE_ANGLE, EGL_TRUE, EGL_WIDTH, width,
244 EGL_HEIGHT, height, EGL_NONE};
245
246 surface = eglCreateWindowSurface(
247 egl_display_, egl_config_,
248 static_cast<EGLNativeWindowType>(std::get<HWND>(*render_target)),
249 surfaceAttributes);
250 if (surface == EGL_NO_SURFACE) {
251 LogEglError("Surface creation failed.");
252 return false;
253 }
254
255 surface_width_ = width;
256 surface_height_ = height;
257 render_surface_ = surface;
258
259 if (!MakeCurrent()) {
260 LogEglError("Unable to make surface current to update the swap interval");
261 return false;
262 }
263
264 SetVSyncEnabled(vsync_enabled);
265 return true;
266 }
267
268 void AngleSurfaceManager::ResizeSurface(WindowsRenderTarget* render_target,
269 EGLint width,
270 EGLint height,
271 bool vsync_enabled) {
272 EGLint existing_width, existing_height;
273 GetSurfaceDimensions(&existing_width, &existing_height);
274 if (width != existing_width || height != existing_height) {
275 surface_width_ = width;
276 surface_height_ = height;
277
278 ClearContext();
279 DestroySurface();
280 if (!CreateSurface(render_target, width, height, vsync_enabled)) {
281 FML_LOG(ERROR)
282 << "AngleSurfaceManager::ResizeSurface failed to create surface";
283 }
284 }
285 }
286
287 void AngleSurfaceManager::GetSurfaceDimensions(EGLint* width, EGLint* height) {
288 if (render_surface_ == EGL_NO_SURFACE || !initialize_succeeded_) {
289 *width = 0;
290 *height = 0;
291 return;
292 }
293
294 // Can't use eglQuerySurface here; Because we're not using
295 // EGL_FIXED_SIZE_ANGLE flag anymore, Angle may resize the surface before
296 // Flutter asks it to, which breaks 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 // OpenGL swap intervals can be used to prevent screen tearing.
346 // If enabled, the raster thread blocks until the v-blank.
347 // This is unnecessary if DWM composition is enabled.
348 // See: https://www.khronos.org/opengl/wiki/Swap_Interval
349 // See: https://learn.microsoft.com/windows/win32/dwm/composition-ovw
350 if (eglSwapInterval(egl_display_, enabled ? 1 : 0) != EGL_TRUE) {
351 LogEglError("Unable to update the swap interval");
352 return;
353 }
354 }
355
356 bool AngleSurfaceManager::GetDevice(ID3D11Device** device) {
357 if (!resolved_device_) {
358 PFNEGLQUERYDISPLAYATTRIBEXTPROC egl_query_display_attrib_EXT =
359 reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
360 eglGetProcAddress("eglQueryDisplayAttribEXT"));
361
362 PFNEGLQUERYDEVICEATTRIBEXTPROC egl_query_device_attrib_EXT =
363 reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
364 eglGetProcAddress("eglQueryDeviceAttribEXT"));
365
366 if (!egl_query_display_attrib_EXT || !egl_query_device_attrib_EXT) {
367 return false;
368 }
369
370 EGLAttrib egl_device = 0;
371 EGLAttrib angle_device = 0;
372 if (egl_query_display_attrib_EXT(egl_display_, EGL_DEVICE_EXT,
373 &egl_device) == EGL_TRUE) {
374 if (egl_query_device_attrib_EXT(
375 reinterpret_cast<EGLDeviceEXT>(egl_device),
376 EGL_D3D11_DEVICE_ANGLE, &angle_device) == EGL_TRUE) {
377 resolved_device_ = reinterpret_cast<ID3D11Device*>(angle_device);
378 }
379 }
380 }
381
382 resolved_device_.CopyTo(device);
383 return (resolved_device_ != nullptr);
384 }
385
386 } // namespace flutter
flutter::AngleSurfaceManager::ClearContext
bool ClearContext()
Definition: angle_surface_manager.cc:322
flutter::AngleSurfaceManager::HasContextCurrent
bool HasContextCurrent()
Definition: angle_surface_manager.cc:308
flutter::WindowsRenderTarget
std::variant< HWND > WindowsRenderTarget
Definition: window_binding_handler.h:44
angle_surface_manager.h
flutter::AngleSurfaceManager::CreateSurface
virtual bool CreateSurface(WindowsRenderTarget *render_target, EGLint width, EGLint height, bool enable_vsync)
Definition: angle_surface_manager.cc:232
flutter::AngleSurfaceManager::ClearCurrent
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
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:356
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
flutter::AngleSurfaceManager::ResizeSurface
virtual void ResizeSurface(WindowsRenderTarget *render_target, EGLint width, EGLint height, bool enable_vsync)
Definition: angle_surface_manager.cc:268
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:287