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

[Windows] Improve logic to update swap intervals #46172

Merged
merged 2 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions shell/platform/windows/angle_surface_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand Down
20 changes: 15 additions & 5 deletions shell/platform/windows/angle_surface_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,21 @@ 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,
bool enable_vsync);

// 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,
Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions shell/platform/windows/flutter_windows_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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_) {
Expand Down
2 changes: 1 addition & 1 deletion shell/platform/windows/flutter_windows_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
25 changes: 23 additions & 2 deletions shell/platform/windows/flutter_windows_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down
7 changes: 7 additions & 0 deletions shell/platform/windows/flutter_windows_view_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,13 @@ TEST(FlutterWindowsViewTest, UpdatesVSyncOnDwmUpdates) {
std::unique_ptr<MockAngleSurfaceManager> surface_manager =
std::make_unique<MockAngleSurfaceManager>();

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));
Expand Down