diff --git a/shell/platform/windows/angle_surface_manager.cc b/shell/platform/windows/angle_surface_manager.cc index 7deafc2d0bf8c..7005348482856 100644 --- a/shell/platform/windows/angle_surface_manager.cc +++ b/shell/platform/windows/angle_surface_manager.cc @@ -256,6 +256,11 @@ bool AngleSurfaceManager::CreateSurface(WindowsRenderTarget* render_target, surface_height_ = height; render_surface_ = surface; + if (!MakeCurrent()) { + LogEglError("Unable to make surface current to update the swap interval"); + return false; + } + SetVSyncEnabled(vsync_enabled); return true; } @@ -300,11 +305,20 @@ void AngleSurfaceManager::DestroySurface() { render_surface_ = EGL_NO_SURFACE; } +bool AngleSurfaceManager::HasContextCurrent() { + return eglGetCurrentContext() != EGL_NO_CONTEXT; +} + bool AngleSurfaceManager::MakeCurrent() { return (eglMakeCurrent(egl_display_, render_surface_, render_surface_, egl_context_) == EGL_TRUE); } +bool AngleSurfaceManager::ClearCurrent() { + return (eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT) == EGL_TRUE); +} + bool AngleSurfaceManager::ClearContext() { return (eglMakeCurrent(egl_display_, nullptr, nullptr, egl_context_) == EGL_TRUE); @@ -328,12 +342,6 @@ EGLSurface AngleSurfaceManager::CreateSurfaceFromHandle( } void AngleSurfaceManager::SetVSyncEnabled(bool enabled) { - if (eglMakeCurrent(egl_display_, render_surface_, render_surface_, - egl_context_) != EGL_TRUE) { - LogEglError("Unable to make surface current to update the swap interval"); - return; - } - // OpenGL swap intervals can be used to prevent screen tearing. // If enabled, the raster thread blocks until the v-blank. // This is unnecessary if DWM composition is enabled. diff --git a/shell/platform/windows/angle_surface_manager.h b/shell/platform/windows/angle_surface_manager.h index 576704f630054..2f6302b9659ed 100644 --- a/shell/platform/windows/angle_surface_manager.h +++ b/shell/platform/windows/angle_surface_manager.h @@ -33,8 +33,10 @@ class AngleSurfaceManager { // Creates an EGLSurface wrapper and backing DirectX 11 SwapChain // associated with window, in the appropriate format for display. - // Target represents the visual entity to bind to. Width and + // Target represents the visual entity to bind to. Width and // height represent dimensions surface is created at. + // + // This binds |egl_context_| to the current thread. virtual bool CreateSurface(WindowsRenderTarget* render_target, EGLint width, EGLint height, @@ -42,8 +44,10 @@ class AngleSurfaceManager { // Resizes backing surface from current size to newly requested size // based on width and height for the specific case when width and height do - // not match current surface dimensions. Target represents the visual entity + // not match current surface dimensions. Target represents the visual entity // to bind to. + // + // This binds |egl_context_| to the current thread. virtual void ResizeSurface(WindowsRenderTarget* render_target, EGLint width, EGLint height, @@ -56,11 +60,17 @@ class AngleSurfaceManager { // Releases the pass-in EGLSurface wrapping and backing resources if not null. virtual void DestroySurface(); - // Binds egl_context_ to the current rendering thread and to the draw and read - // surfaces returning a boolean result reflecting success. + // Check if the current thread has a context bound. + bool HasContextCurrent(); + + // Binds |egl_context_| to the current rendering thread and to the draw and + // read surfaces returning a boolean result reflecting success. bool MakeCurrent(); - // Clears current egl_context_ + // Unbinds the current EGL context from the current thread. + bool ClearCurrent(); + + // Clears the |egl_context_| draw and read surfaces. bool ClearContext(); // Binds egl_resource_context_ to the current rendering thread and to the draw diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index fce1c0531e48c..2e04c14f2446d 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -390,6 +390,10 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { args.aot_data = aot_data_.get(); } + // The platform thread creates OpenGL contexts. These + // must be released to be used by the engine's threads. + FML_DCHECK(!surface_manager_ || !surface_manager_->HasContextCurrent()); + FlutterRendererConfig renderer_config; if (enable_impeller_) { diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index cdbcd375fd119..04d33e7583d4b 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -199,7 +199,7 @@ class FlutterWindowsEngine { bool MarkExternalTextureFrameAvailable(int64_t texture_id); // Posts the given callback onto the raster thread. - bool PostRasterThreadTask(fml::closure callback); + virtual bool PostRasterThreadTask(fml::closure callback); // Invoke on the embedder's vsync callback to schedule a frame. void OnVsync(intptr_t baton); diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index d668d2f447b6c..11effebe45f6f 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -594,6 +594,12 @@ void FlutterWindowsView::CreateRenderSurface() { engine_->surface_manager()->CreateSurface(GetRenderTarget(), bounds.width, bounds.height, enable_vsync); + // The EGL context cannot be current on multiple threads. + // Creating the render surface runs on the platform thread and + // makes the EGL context current. Thus, the EGL context must be + // released so that the raster thread can use it for rendering. + engine_->surface_manager()->ClearCurrent(); + resize_target_width_ = bounds.width; resize_target_height_ = bounds.height; } @@ -668,9 +674,24 @@ void FlutterWindowsView::UpdateSemanticsEnabled(bool enabled) { } void FlutterWindowsView::OnDwmCompositionChanged() { - if (engine_->surface_manager()) { - engine_->surface_manager()->SetVSyncEnabled(binding_handler_->NeedsVSync()); + AngleSurfaceManager* surface_manager = engine_->surface_manager(); + if (!surface_manager) { + return; } + + // Update the surface with the new composition state. + // Switch to the raster thread as the render EGL context can only be + // current on a single thread a time. + auto needs_vsync = binding_handler_->NeedsVSync(); + engine_->PostRasterThreadTask([surface_manager, needs_vsync]() { + if (!surface_manager->MakeCurrent()) { + FML_LOG(ERROR) + << "Unable to make surface current to update the swap interval"; + return; + } + + surface_manager->SetVSyncEnabled(needs_vsync); + }); } void FlutterWindowsView::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { diff --git a/shell/platform/windows/flutter_windows_view_unittests.cc b/shell/platform/windows/flutter_windows_view_unittests.cc index b7ce34c05b3bd..fee0333dbd048 100644 --- a/shell/platform/windows/flutter_windows_view_unittests.cc +++ b/shell/platform/windows/flutter_windows_view_unittests.cc @@ -1287,6 +1287,13 @@ TEST(FlutterWindowsViewTest, UpdatesVSyncOnDwmUpdates) { std::unique_ptr surface_manager = std::make_unique(); + EXPECT_CALL(*engine.get(), PostRasterThreadTask) + .Times(2) + .WillRepeatedly([](fml::closure callback) { + callback(); + return true; + }); + EXPECT_CALL(*window_binding_handler.get(), NeedsVSync) .WillOnce(Return(true)) .WillOnce(Return(false));