commit c35192456a26bb94d66cd75be9f3c32d5db4ddf0
parent fa4f5d7269ff10842981cf5a45434338ff18dd3d
Author: Glenn Watson <git@chillybin.org>
Date: Thu, 27 Nov 2025 19:41:15 +0000
Bug 2001524 - Disable ShouldSnapToGrid in more cases. r=tnikkel
Move snapping of reference frame origins to WR, where we can remove
the (potentially fractional) external scroll offset reliably. The
old non-WR render paths need to keep snapping enabled here.
Mark one animated transform test fuzzy on windows, which appears to
be caused by a separate bug in the animation / snapping code, to be
fixed as a follow up.
Differential Revision: https://phabricator.services.mozilla.com/D273529
Diffstat:
7 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/gfx/wr/webrender/src/spatial_node.rs b/gfx/wr/webrender/src/spatial_node.rs
@@ -446,8 +446,34 @@ impl SpatialNode {
ReferenceFrameKind::Transform { .. } => source_transform,
};
+ // Previously, the origin of a stacking context transform was snapped
+ // in Gecko. However, this causes jittering issues during scrolling in
+ // some cases when fractional scrolling is enabled. The origin used in
+ // Gecko doesn't have the external scroll offset from the content process
+ // removed, so if that content-side scroll amount is fractional, it can
+ // cause inconsistent snapping during scene building. Instead, we need
+ // to apply the device-pixel snap _after_ the external scroll offset
+ // has been removed. To further complicate matters, we _don't_ want to
+ // snap this if this spatial node has a snapping transform, as we rely
+ // on the fractional intermediate nodes in order to arrive at a correct
+ // final snapping result. If we don't have a snapping offset, we've
+ // reached a spatial node where snapping will no longer apply (e.g. a
+ // complex transform) and then we need to snap the device pixel position
+ // of that transform.
+ let parent_origin = match self.snapping_transform {
+ Some(..) => {
+ info.origin_in_parent_reference_frame
+ }
+ None => {
+ snap_offset(
+ info.origin_in_parent_reference_frame,
+ state.coordinate_system_relative_scale_offset.scale,
+ )
+ }
+ };
+
let resolved_transform =
- LayoutFastTransform::with_vector(info.origin_in_parent_reference_frame)
+ LayoutFastTransform::with_vector(parent_origin)
.pre_transform(&source_transform);
// The transformation for this viewport in world coordinates is the transformation for
diff --git a/gfx/wr/wrench/reftests/filters/blend-clipped.png b/gfx/wr/wrench/reftests/filters/blend-clipped.png
Binary files differ.
diff --git a/gfx/wr/wrench/reftests/tiles/tile-cache-raster-root.png b/gfx/wr/wrench/reftests/tiles/tile-cache-raster-root.png
Binary files differ.
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
@@ -1925,6 +1925,12 @@ void nsLayoutUtils::PostTranslate(Matrix4x4& aTransform, const nsPoint& aOrigin,
}
bool nsLayoutUtils::ShouldSnapToGrid(const nsIFrame* aFrame) {
+ // TODO: Remove this function when this pref is being removed.
+ if (StaticPrefs::layout_disable_pixel_alignment() &&
+ nsDisplayListBuilder::IsPaintingForWebRender()) {
+ return aFrame && aFrame->IsSVGOuterSVGAnonChildFrame();
+ }
+
return !aFrame || !aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
aFrame->IsSVGOuterSVGAnonChildFrame();
}
@@ -3304,6 +3310,7 @@ void nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
}
}
+ builder->SetPaintingForWebRender(false);
--paintFrameDepth;
#if 0
if (XRE_IsParentProcess()) {
diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp
@@ -781,6 +781,7 @@ void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
}
uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
+bool nsDisplayListBuilder::sIsPaintingForWebRender(false);
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
nsDisplayListBuilderMode aMode,
@@ -819,7 +820,6 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mIsPaintingToWindow(false),
mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
mUseHighQualityScaling(false),
- mIsPaintingForWebRender(false),
mAncestorHasApzAwareEventHandler(false),
mHaveScrollableDisplayPort(false),
mWindowDraggingAllowed(false),
diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h
@@ -616,10 +616,10 @@ class nsDisplayListBuilder {
/**
* Call this if we're doing painting for WebRender
*/
- void SetPaintingForWebRender(bool aForWebRender) {
- mIsPaintingForWebRender = true;
+ static void SetPaintingForWebRender(bool aForWebRender) {
+ sIsPaintingForWebRender = aForWebRender;
}
- bool IsPaintingForWebRender() const { return mIsPaintingForWebRender; }
+ static bool IsPaintingForWebRender() { return sIsPaintingForWebRender; }
/**
* Call this to prevent descending into subdocuments.
*/
@@ -2024,7 +2024,10 @@ class nsDisplayListBuilder {
bool mIsPaintingToWindow;
bool mAsyncPanZoomEnabled;
bool mUseHighQualityScaling;
- bool mIsPaintingForWebRender;
+ // This is static because it's used by nsLayoutUtils::ShouldSnapToGrid which
+ // is a static method that is very unwieldy to provide a builder instance
+ // object to.
+ static bool sIsPaintingForWebRender;
bool mAncestorHasApzAwareEventHandler;
// True when the first async-scrollable scroll frame for which we build a
// display list has a display port. An async-scrollable scroll frame is one
diff --git a/testing/web-platform/meta/css/css-transforms/individual-transform/animation/individual-transform-combine.html.ini b/testing/web-platform/meta/css/css-transforms/individual-transform/animation/individual-transform-combine.html.ini
@@ -0,0 +1,3 @@
+[individual-transform-combine.html]
+ fuzzy:
+ if (os == "win") and not swgl: maxDifference=255-255;totalPixels=596-596