Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 9d28c72

Browse files
authored
[Windows] Improve logic to update swap intervals (#46172)
This relands #45310 to unblock the ANGLE roll with the fix for flutter/flutter#134262. ## Background ### Swap interval If the Windows system compositor is enabled, the Windows embedder disables the swap interval so that presenting to a surface does not block until the v-blank. If the Windows system compositor is disabled (which is possible on Windows 7), the Windows embedder enables swap interval to prevent screen tearing. ### GL context threading Our current version of ANGLE allows making a GL context current on multiple threads. However, the latest version of ANGLE errors if a GL context is made current on multiple threads. This is causing the ANGLE roll to fail ([example](https://ci.chromium.org/ui/p/flutter/builders/try/Windows%20Engine%20Drone/203788/overview)). The Windows embedder has two GL context threading issues: 1. At startup, the platform thread creates and binds the GL context. This change ensures the GL context is released from the platform thread so that the raster thread can use the GL context for rendering. 2. When the system compositor updates, the GL context is bound to the platform thread to update the swap interval. This change ensures the swap interval update happens on the raster thread. ### Window resizing Resizing the window recreates the GL surface and resets the swap interval. The previous fix released the current GL context after updating the swap interval (this ensured the platform thread released the GL context at startup). This broke window resizing as it caused the engine to "lose" its GL context during rendering (see flutter/flutter#134262). This reland releases the GL context only if on the startup case. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 35ba2a3 commit 9d28c72

6 files changed

+64
-14
lines changed

shell/platform/windows/angle_surface_manager.cc

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ bool AngleSurfaceManager::CreateSurface(WindowsRenderTarget* render_target,
256256
surface_height_ = height;
257257
render_surface_ = surface;
258258

259+
if (!MakeCurrent()) {
260+
LogEglError("Unable to make surface current to update the swap interval");
261+
return false;
262+
}
263+
259264
SetVSyncEnabled(vsync_enabled);
260265
return true;
261266
}
@@ -300,11 +305,20 @@ void AngleSurfaceManager::DestroySurface() {
300305
render_surface_ = EGL_NO_SURFACE;
301306
}
302307

308+
bool AngleSurfaceManager::HasContextCurrent() {
309+
return eglGetCurrentContext() != EGL_NO_CONTEXT;
310+
}
311+
303312
bool AngleSurfaceManager::MakeCurrent() {
304313
return (eglMakeCurrent(egl_display_, render_surface_, render_surface_,
305314
egl_context_) == EGL_TRUE);
306315
}
307316

317+
bool AngleSurfaceManager::ClearCurrent() {
318+
return (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE,
319+
EGL_NO_CONTEXT) == EGL_TRUE);
320+
}
321+
308322
bool AngleSurfaceManager::ClearContext() {
309323
return (eglMakeCurrent(egl_display_, nullptr, nullptr, egl_context_) ==
310324
EGL_TRUE);
@@ -328,12 +342,6 @@ EGLSurface AngleSurfaceManager::CreateSurfaceFromHandle(
328342
}
329343

330344
void AngleSurfaceManager::SetVSyncEnabled(bool enabled) {
331-
if (eglMakeCurrent(egl_display_, render_surface_, render_surface_,
332-
egl_context_) != EGL_TRUE) {
333-
LogEglError("Unable to make surface current to update the swap interval");
334-
return;
335-
}
336-
337345
// OpenGL swap intervals can be used to prevent screen tearing.
338346
// If enabled, the raster thread blocks until the v-blank.
339347
// This is unnecessary if DWM composition is enabled.

shell/platform/windows/angle_surface_manager.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,21 @@ class AngleSurfaceManager {
3333

3434
// Creates an EGLSurface wrapper and backing DirectX 11 SwapChain
3535
// associated with window, in the appropriate format for display.
36-
// Target represents the visual entity to bind to. Width and
36+
// Target represents the visual entity to bind to. Width and
3737
// height represent dimensions surface is created at.
38+
//
39+
// This binds |egl_context_| to the current thread.
3840
virtual bool CreateSurface(WindowsRenderTarget* render_target,
3941
EGLint width,
4042
EGLint height,
4143
bool enable_vsync);
4244

4345
// Resizes backing surface from current size to newly requested size
4446
// based on width and height for the specific case when width and height do
45-
// not match current surface dimensions. Target represents the visual entity
47+
// not match current surface dimensions. Target represents the visual entity
4648
// to bind to.
49+
//
50+
// This binds |egl_context_| to the current thread.
4751
virtual void ResizeSurface(WindowsRenderTarget* render_target,
4852
EGLint width,
4953
EGLint height,
@@ -56,11 +60,17 @@ class AngleSurfaceManager {
5660
// Releases the pass-in EGLSurface wrapping and backing resources if not null.
5761
virtual void DestroySurface();
5862

59-
// Binds egl_context_ to the current rendering thread and to the draw and read
60-
// surfaces returning a boolean result reflecting success.
63+
// Check if the current thread has a context bound.
64+
bool HasContextCurrent();
65+
66+
// Binds |egl_context_| to the current rendering thread and to the draw and
67+
// read surfaces returning a boolean result reflecting success.
6168
bool MakeCurrent();
6269

63-
// Clears current egl_context_
70+
// Unbinds the current EGL context from the current thread.
71+
bool ClearCurrent();
72+
73+
// Clears the |egl_context_| draw and read surfaces.
6474
bool ClearContext();
6575

6676
// Binds egl_resource_context_ to the current rendering thread and to the draw

shell/platform/windows/flutter_windows_engine.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
390390
args.aot_data = aot_data_.get();
391391
}
392392

393+
// The platform thread creates OpenGL contexts. These
394+
// must be released to be used by the engine's threads.
395+
FML_DCHECK(!surface_manager_ || !surface_manager_->HasContextCurrent());
396+
393397
FlutterRendererConfig renderer_config;
394398

395399
if (enable_impeller_) {

shell/platform/windows/flutter_windows_engine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class FlutterWindowsEngine {
199199
bool MarkExternalTextureFrameAvailable(int64_t texture_id);
200200

201201
// Posts the given callback onto the raster thread.
202-
bool PostRasterThreadTask(fml::closure callback);
202+
virtual bool PostRasterThreadTask(fml::closure callback);
203203

204204
// Invoke on the embedder's vsync callback to schedule a frame.
205205
void OnVsync(intptr_t baton);

shell/platform/windows/flutter_windows_view.cc

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,12 @@ void FlutterWindowsView::CreateRenderSurface() {
594594
engine_->surface_manager()->CreateSurface(GetRenderTarget(), bounds.width,
595595
bounds.height, enable_vsync);
596596

597+
// The EGL context cannot be current on multiple threads.
598+
// Creating the render surface runs on the platform thread and
599+
// makes the EGL context current. Thus, the EGL context must be
600+
// released so that the raster thread can use it for rendering.
601+
engine_->surface_manager()->ClearCurrent();
602+
597603
resize_target_width_ = bounds.width;
598604
resize_target_height_ = bounds.height;
599605
}
@@ -668,9 +674,24 @@ void FlutterWindowsView::UpdateSemanticsEnabled(bool enabled) {
668674
}
669675

670676
void FlutterWindowsView::OnDwmCompositionChanged() {
671-
if (engine_->surface_manager()) {
672-
engine_->surface_manager()->SetVSyncEnabled(binding_handler_->NeedsVSync());
677+
AngleSurfaceManager* surface_manager = engine_->surface_manager();
678+
if (!surface_manager) {
679+
return;
673680
}
681+
682+
// Update the surface with the new composition state.
683+
// Switch to the raster thread as the render EGL context can only be
684+
// current on a single thread a time.
685+
auto needs_vsync = binding_handler_->NeedsVSync();
686+
engine_->PostRasterThreadTask([surface_manager, needs_vsync]() {
687+
if (!surface_manager->MakeCurrent()) {
688+
FML_LOG(ERROR)
689+
<< "Unable to make surface current to update the swap interval";
690+
return;
691+
}
692+
693+
surface_manager->SetVSyncEnabled(needs_vsync);
694+
});
674695
}
675696

676697
void FlutterWindowsView::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) {

shell/platform/windows/flutter_windows_view_unittests.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,13 @@ TEST(FlutterWindowsViewTest, UpdatesVSyncOnDwmUpdates) {
12871287
std::unique_ptr<MockAngleSurfaceManager> surface_manager =
12881288
std::make_unique<MockAngleSurfaceManager>();
12891289

1290+
EXPECT_CALL(*engine.get(), PostRasterThreadTask)
1291+
.Times(2)
1292+
.WillRepeatedly([](fml::closure callback) {
1293+
callback();
1294+
return true;
1295+
});
1296+
12901297
EXPECT_CALL(*window_binding_handler.get(), NeedsVSync)
12911298
.WillOnce(Return(true))
12921299
.WillOnce(Return(false));

0 commit comments

Comments
 (0)