fix: visual artifacts while resizing on Windows (#49191)

This commit is contained in:
Niklas Wenzel
2026-01-05 22:32:21 +01:00
committed by GitHub
parent a7652f68e9
commit 8fd775faca
3 changed files with 217 additions and 0 deletions

View File

@@ -141,3 +141,5 @@ allow_electron_to_depend_on_components_os_crypt_sync.patch
expose_referrerscriptinfo_hostdefinedoptionsindex.patch
chore_disable_protocol_handler_dcheck.patch
fix_check_for_file_existence_before_setting_mtime.patch
viz_create_isbufferqueuesupportedandenabled.patch
viz_fix_visual_artifacts_while_resizing_window_with_dcomp.patch

View File

@@ -0,0 +1,93 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Tue, 2 Dec 2025 17:53:11 -0800
Subject: viz: Create IsBufferQueueSupportedAndEnabled()
Manual backport of crrev.com/c/7210913
Bug: 457463689
Change-Id: I31bbaa6b5d79697c6bb5e1fc6738f6ea5a937b4f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7210913
Reviewed-by: Michael Tang <tangm@microsoft.com>
Commit-Queue: David Sanders <dsanders11@ucsbalum.com>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1553190}
diff --git a/components/viz/service/display/output_surface.cc b/components/viz/service/display/output_surface.cc
index ff795eb057ac64f40aa842fec8e053d12f57f538..71287614c627d39f8d019889498205ab3eff2a69 100644
--- a/components/viz/service/display/output_surface.cc
+++ b/components/viz/service/display/output_surface.cc
@@ -21,6 +21,16 @@
namespace viz {
+namespace {
+
+#if BUILDFLAG(IS_WIN)
+// Use BufferQueue for the primary plane instead of a DXGI swap chain or DComp
+// surface.
+BASE_FEATURE(kBufferQueue, base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
+
+} // namespace
+
OutputSurface::Capabilities::Capabilities() = default;
OutputSurface::Capabilities::~Capabilities() = default;
OutputSurface::Capabilities::Capabilities(const Capabilities& capabilities) =
@@ -94,6 +104,12 @@ bool IsDelegatedCompositingSupportedAndEnabled(
// Ensure we check the feature flag iff the feature is supported.
return features::IsDelegatedCompositingEnabled();
}
+
+bool IsBufferQueueSupportedAndEnabled(
+ OutputSurface::DCSupportLevel support_level) {
+ return support_level >= OutputSurface::DCSupportLevel::kDCompDynamicTexture &&
+ base::FeatureList::IsEnabled(kBufferQueue);
+}
#endif
} // namespace viz
diff --git a/components/viz/service/display/output_surface.h b/components/viz/service/display/output_surface.h
index 25306ab6e18a266efdc329e4ddd81f5303033f4c..589f4c10dad9c807c9e3ce7baba63795b629435b 100644
--- a/components/viz/service/display/output_surface.h
+++ b/components/viz/service/display/output_surface.h
@@ -307,6 +307,9 @@ class VIZ_SERVICE_EXPORT OutputSurface {
// `features::IsDelegatedCompositingEnabled()`.
bool IsDelegatedCompositingSupportedAndEnabled(
OutputSurface::DCSupportLevel support_level);
+
+bool IsBufferQueueSupportedAndEnabled(
+ OutputSurface::DCSupportLevel support_level);
#endif
} // namespace viz
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index f94826fe8d7c3f407695fa3161f6ae9cd949f99f..610490f50b2e355382f2610c556b8a0683023b06 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -120,12 +120,6 @@ namespace {
BASE_FEATURE(kDumpWithoutCrashingOnMissingRenderPassBacking,
base::FEATURE_ENABLED_BY_DEFAULT);
-#if BUILDFLAG(IS_WIN)
-// Use BufferQueue for the primary plane instead of a DXGI swap chain or DComp
-// surface.
-BASE_FEATURE(kBufferQueue, base::FEATURE_DISABLED_BY_DEFAULT);
-#endif
-
// Smallest unit that impacts anti-aliasing output. We use this to determine
// when an exterior edge (with AA) has been clipped (no AA). The specific value
// was chosen to match that used by gl_renderer.
@@ -991,10 +985,8 @@ SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
// It's possible to use BufferQueue with DComp textures, so we can optionally
// enable it behind a feature flag.
- const bool want_buffer_queue =
- output_surface_->capabilities().dc_support_level >=
- OutputSurface::DCSupportLevel::kDCompDynamicTexture &&
- base::FeatureList::IsEnabled(kBufferQueue);
+ const bool want_buffer_queue = IsBufferQueueSupportedAndEnabled(
+ output_surface_->capabilities().dc_support_level);
#else
const bool want_buffer_queue = true;
#endif

View File

@@ -0,0 +1,122 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Niklas Wenzel <dev@nikwen.de>
Date: Tue, 2 Dec 2025 17:55:29 -0800
Subject: viz: Fix visual artifacts while resizing window with DComp
Manual backport of crrev.com/c/7115438
Bug: 457463689
Change-Id: I9c684effe15e0b112ae533faa243e5a035e9c875
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7115438
Commit-Queue: David Sanders <dsanders11@ucsbalum.com>
Reviewed-by: Michael Tang <tangm@microsoft.com>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1553192}
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 6a28b4e3d9b8d1988e96c6ae9fd931d20af5a876..19e71c4aee88824458cd1b1557e9cd770f025704 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -227,6 +227,31 @@ void DirectRenderer::DrawFrame(
current_frame()->device_viewport_size = device_viewport_size;
current_frame()->display_color_spaces = display_color_spaces;
+ gfx::Size surface_resource_size =
+ CalculateSizeForOutputSurface(device_viewport_size);
+
+#if BUILDFLAG(IS_WIN)
+ if (output_surface_->capabilities().clear_drawn_areas_outside_viewport &&
+ device_viewport_size != surface_resource_size) {
+ // On Windows with DirectComposition, we cannot synchronize the swap chain
+ // |Present| and the DComp |Commit| calls to take effect at the same time.
+ // (Both take effect asynchronously.) Hence, presenting a frame and changing
+ // the DComp layer clip rect can happen at different times. This can lead to
+ // ugly visual artifacts while resizing the window because it can reveal
+ // areas of the surface that are outside the viewport (crbug.com/457463689).
+ // To prevent those artifacts, we clear areas outside of the viewport with a
+ // transparent color. Transparency is expensive, so we use it only while
+ // resizing.
+ // This line gives us a transparent image format and triggers the background
+ // to be cleared in |SkiaRenderer::ClearFramebuffer|.
+ root_render_pass->has_transparent_background = true;
+ // Redraw and swap the whole surface.
+ root_render_pass->output_rect = gfx::Rect(surface_resource_size);
+ current_frame()->root_damage_rect = gfx::Rect(surface_resource_size);
+ current_frame()->device_viewport_size = surface_resource_size;
+ }
+#endif
+
output_surface_->SetNeedsMeasureNextDrawLatency();
BeginDrawingFrame();
@@ -256,8 +281,6 @@ void DirectRenderer::DrawFrame(
current_frame()->display_color_spaces.GetOutputFormat(
current_frame()->root_render_pass->content_color_usage,
current_frame()->root_render_pass->has_transparent_background);
- gfx::Size surface_resource_size =
- CalculateSizeForOutputSurface(device_viewport_size);
#if BUILDFLAG(IS_WIN)
bool has_primary_plane = false;
#endif
@@ -401,8 +424,10 @@ void DirectRenderer::DrawFrame(
// If we need to redraw the frame, the whole output should be considered
// damaged.
- if (needs_full_frame_redraw)
- current_frame()->root_damage_rect = gfx::Rect(device_viewport_size);
+ if (needs_full_frame_redraw) {
+ current_frame()->root_damage_rect =
+ gfx::Rect(current_frame()->device_viewport_size);
+ }
if (!skip_drawing_root_render_pass) {
DrawRenderPassAndExecuteCopyRequests(root_render_pass);
diff --git a/components/viz/service/display/output_surface.h b/components/viz/service/display/output_surface.h
index 589f4c10dad9c807c9e3ce7baba63795b629435b..641bbfc732c88141ddd929a4c334360462259ee4 100644
--- a/components/viz/service/display/output_surface.h
+++ b/components/viz/service/display/output_surface.h
@@ -106,6 +106,11 @@ class VIZ_SERVICE_EXPORT OutputSurface {
#if BUILDFLAG(IS_WIN)
// Whether this OutputSurface supports direct composition layers.
DCSupportLevel dc_support_level = DCSupportLevel::kNone;
+ // Whether to 1) clear all drawn areas outside the viewport with a
+ // transparent background color when drawing a frame and 2) swap them. This
+ // is necessary if the surface clip rect can get out of sync with the
+ // viewport size (e.g., due to a race condition).
+ bool clear_drawn_areas_outside_viewport = false;
#endif
// Whether this OutputSurface should skip DrawAndSwap(). This is true for
// the unified display on Chrome OS. All drawing is handled by the physical
diff --git a/components/viz/service/display_embedder/skia_output_device_dcomp.cc b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
index 7ac9ea1cdc4416a7af8dc2a75404cbc15be6cfad..8e027d1382c5d639c1e114b8e25b6cea3af3445d 100644
--- a/components/viz/service/display_embedder/skia_output_device_dcomp.cc
+++ b/components/viz/service/display_embedder/skia_output_device_dcomp.cc
@@ -39,10 +39,8 @@
namespace viz {
namespace {
-// With DirectComposition, resize surface based on root render pass size to
-// avoid gutter which shows stale pixels.
-BASE_FEATURE(kDirectCompositionResizeBasedOnRootSurface,
- base::FEATURE_ENABLED_BY_DEFAULT);
+// Apply fixes for crbug.com/457463689.
+BASE_FEATURE(kDirectCompositionResizeFixes, base::FEATURE_ENABLED_BY_DEFAULT);
base::TimeTicks g_last_reshape_failure = base::TimeTicks();
@@ -161,7 +159,14 @@ SkiaOutputDeviceDComp::SkiaOutputDeviceDComp(
capabilities_.supports_viewporter = presenter_->SupportsViewporter();
capabilities_.supports_non_backed_solid_color_overlays = true;
capabilities_.resize_based_on_root_surface =
- base::FeatureList::IsEnabled(kDirectCompositionResizeBasedOnRootSurface);
+ base::FeatureList::IsEnabled(kDirectCompositionResizeFixes);
+ // With delegated compositing or a buffer queue, |Present| and |Commit| are
+ // synchronized and the clear is not needed.
+ capabilities_.clear_drawn_areas_outside_viewport =
+ base::FeatureList::IsEnabled(kDirectCompositionResizeFixes) &&
+ !IsDelegatedCompositingSupportedAndEnabled(
+ capabilities_.dc_support_level) &&
+ !IsBufferQueueSupportedAndEnabled(capabilities_.dc_support_level);
DCHECK(context_state_);
DCHECK(context_state_->gr_context() ||