Bug 1435094 - wire up GlyphRasterSpace to nsDisplayTransform. r?mstange,kats
When a transform thinks it's animated we should abandon screen rasterization
and instead favour local rasterization. This produces a more visually
pleasant rendering, as pixel-snapping "wobbles" the text between
frames.
The float scale of GlyphRasterSpace::Local is currently unused, but this
PR tries its best to set it to a reasonable value, based on discussion
with glennw about the intended semantics. We agreed it should specify
the scale *relative* to the parent stacking context, which means it's
just whatever scaling the stacking context's transform applies. It's
possible we'll need to clamp this value or make it properly 2-dimensional
later on.
Some book-keeping is added to StackingContextHelper to ensure that
GlyphRasterSpace::Screen is never requested by a descendent
of a stacking context using GlyphRasterSpace::Local.
nsDisplayMask is changed to use a StackingContextHelper to ensure
rasterSpace is properly propagated.
In addition, this is the first commit making use of cbindgen's new support
for bridging Rust enums natively into C++! This bumps our minimum cbindgen
to 6.0.0 (just released).
MozReview-Commit-ID: 9AlsB6nUheB
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -322,17 +322,19 @@ AsyncImagePipelineManager::ApplyAsyncIma
nullptr,
nullptr,
&opacity,
pipeline->mScTransform.IsIdentity() ? nullptr : &pipeline->mScTransform,
wr::TransformStyle::Flat,
nullptr,
pipeline->mMixBlendMode,
nsTArray<wr::WrFilterOp>(),
- true);
+ true,
+ // This is fine to do unconditionally because we only push images here.
+ wr::GlyphRasterSpace::Screen());
LayoutDeviceRect rect(0, 0, pipeline->mCurrentTexture->GetSize().width, pipeline->mCurrentTexture->GetSize().height);
if (pipeline->mScaleToSize.isSome()) {
rect = LayoutDeviceRect(0, 0, pipeline->mScaleToSize.value().width, pipeline->mScaleToSize.value().height);
}
if (pipeline->mUseExternalImage) {
MOZ_ASSERT(pipeline->mCurrentTexture->AsWebRenderTextureHost());
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -12,61 +12,70 @@
namespace mozilla {
namespace layers {
StackingContextHelper::StackingContextHelper()
: mBuilder(nullptr)
, mScale(1.0f, 1.0f)
, mAffectsClipPositioning(false)
, mIsPreserve3D(false)
+ , mRasterizeLocally(false)
{
// mOrigin remains at 0,0
}
StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParentSC,
wr::DisplayListBuilder& aBuilder,
const nsTArray<wr::WrFilterOp>& aFilters,
const LayoutDeviceRect& aBounds,
const gfx::Matrix4x4* aBoundTransform,
const wr::WrAnimationProperty* aAnimation,
float* aOpacityPtr,
gfx::Matrix4x4* aTransformPtr,
gfx::Matrix4x4* aPerspectivePtr,
const gfx::CompositionOp& aMixBlendMode,
bool aBackfaceVisible,
bool aIsPreserve3D,
- const Maybe<gfx::Matrix4x4>& aTransformForScrollData)
+ const Maybe<gfx::Matrix4x4>& aTransformForScrollData,
+ const wr::WrClipId* aClipNodeId,
+ bool aRasterizeLocally)
: mBuilder(&aBuilder)
, mScale(1.0f, 1.0f)
, mTransformForScrollData(aTransformForScrollData)
, mIsPreserve3D(aIsPreserve3D)
+ , mRasterizeLocally(aRasterizeLocally || aParentSC.mRasterizeLocally)
{
// Compute scale for fallback rendering. We don't try to guess a scale for 3d
// transformed items
gfx::Matrix transform2d;
if (aBoundTransform && aBoundTransform->CanDraw2D(&transform2d)
&& !aPerspectivePtr
&& !aParentSC.mIsPreserve3D) {
mInheritedTransform = transform2d * aParentSC.mInheritedTransform;
mScale = mInheritedTransform.ScaleFactors(true);
} else {
mInheritedTransform = aParentSC.mInheritedTransform;
mScale = aParentSC.mScale;
}
+ auto rasterSpace = mRasterizeLocally
+ ? wr::GlyphRasterSpace::Local(std::max(mScale.width, mScale.height))
+ : wr::GlyphRasterSpace::Screen();
+
mBuilder->PushStackingContext(wr::ToLayoutRect(aBounds),
- nullptr,
+ aClipNodeId,
aAnimation,
aOpacityPtr,
aTransformPtr,
aIsPreserve3D ? wr::TransformStyle::Preserve3D : wr::TransformStyle::Flat,
aPerspectivePtr,
wr::ToMixBlendMode(aMixBlendMode),
aFilters,
- aBackfaceVisible);
+ aBackfaceVisible,
+ rasterSpace);
mAffectsClipPositioning =
(aTransformPtr && !aTransformPtr->IsIdentity()) ||
(aBounds.TopLeft() != LayoutDevicePoint());
}
StackingContextHelper::~StackingContextHelper()
{
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -34,17 +34,19 @@ public:
const gfx::Matrix4x4* aBoundTransform = nullptr,
const wr::WrAnimationProperty* aAnimation = nullptr,
float* aOpacityPtr = nullptr,
gfx::Matrix4x4* aTransformPtr = nullptr,
gfx::Matrix4x4* aPerspectivePtr = nullptr,
const gfx::CompositionOp& aMixBlendMode = gfx::CompositionOp::OP_OVER,
bool aBackfaceVisible = true,
bool aIsPreserve3D = false,
- const Maybe<gfx::Matrix4x4>& aTransformForScrollData = Nothing());
+ const Maybe<gfx::Matrix4x4>& aTransformForScrollData = Nothing(),
+ const wr::WrClipId* aClipNodeId = nullptr,
+ bool aRasterizeLocally = false);
// This version of the constructor should only be used at the root level
// of the tree, so that we have a StackingContextHelper to pass down into
// the RenderLayer traversal, but don't actually want it to push a stacking
// context on the display list builder.
StackingContextHelper();
// Pops the stacking context, if one was pushed during the constructor.
~StackingContextHelper();
@@ -63,14 +65,15 @@ public:
private:
wr::DisplayListBuilder* mBuilder;
gfx::Size mScale;
gfx::Matrix mInheritedTransform;
bool mAffectsClipPositioning;
Maybe<gfx::Matrix4x4> mTransformForScrollData;
bool mIsPreserve3D;
+ bool mRasterizeLocally;
};
} // namespace layers
} // namespace mozilla
#endif /* GFX_STACKINGCONTEXTHELPER_H */
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -785,34 +785,38 @@ DisplayListBuilder::PushStackingContext(
const wr::WrClipId* aClipNodeId,
const WrAnimationProperty* aAnimation,
const float* aOpacity,
const gfx::Matrix4x4* aTransform,
wr::TransformStyle aTransformStyle,
const gfx::Matrix4x4* aPerspective,
const wr::MixBlendMode& aMixBlendMode,
const nsTArray<wr::WrFilterOp>& aFilters,
- bool aIsBackfaceVisible)
+ bool aIsBackfaceVisible,
+ const wr::GlyphRasterSpace& aRasterSpace)
{
wr::LayoutTransform matrix;
if (aTransform) {
matrix = ToLayoutTransform(*aTransform);
}
const wr::LayoutTransform* maybeTransform = aTransform ? &matrix : nullptr;
wr::LayoutTransform perspective;
if (aPerspective) {
perspective = ToLayoutTransform(*aPerspective);
}
+
const wr::LayoutTransform* maybePerspective = aPerspective ? &perspective : nullptr;
const size_t* maybeClipNodeId = aClipNodeId ? &aClipNodeId->id : nullptr;
WRDL_LOG("PushStackingContext b=%s t=%s\n", mWrState, Stringify(aBounds).c_str(),
aTransform ? Stringify(*aTransform).c_str() : "none");
- wr_dp_push_stacking_context(mWrState, aBounds, maybeClipNodeId, aAnimation, aOpacity,
- maybeTransform, aTransformStyle, maybePerspective,
- aMixBlendMode, aFilters.Elements(), aFilters.Length(), aIsBackfaceVisible);
+ wr_dp_push_stacking_context(mWrState, aBounds, maybeClipNodeId, aAnimation,
+ aOpacity, maybeTransform, aTransformStyle,
+ maybePerspective, aMixBlendMode,
+ aFilters.Elements(), aFilters.Length(),
+ aIsBackfaceVisible, aRasterSpace);
}
void
DisplayListBuilder::PopStackingContext()
{
WRDL_LOG("PopStackingContext\n", mWrState);
wr_dp_pop_stacking_context(mWrState);
}
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -265,17 +265,18 @@ public:
const wr::WrClipId* aClipNodeId,
const wr::WrAnimationProperty* aAnimation,
const float* aOpacity,
const gfx::Matrix4x4* aTransform,
wr::TransformStyle aTransformStyle,
const gfx::Matrix4x4* aPerspective,
const wr::MixBlendMode& aMixBlendMode,
const nsTArray<wr::WrFilterOp>& aFilters,
- bool aIsBackfaceVisible);
+ bool aIsBackfaceVisible,
+ const wr::GlyphRasterSpace& aRasterSpace);
void PopStackingContext();
wr::WrClipId DefineClip(const Maybe<wr::WrScrollId>& aAncestorScrollId,
const Maybe<wr::WrClipId>& aAncestorClipId,
const wr::LayoutRect& aClipRect,
const nsTArray<wr::ComplexClipRegion>* aComplex = nullptr,
const wr::WrImageMask* aMask = nullptr);
void PushClip(const wr::WrClipId& aClipId, const DisplayItemClipChain* aParent = nullptr);
--- a/gfx/webrender_bindings/cbindgen.toml
+++ b/gfx/webrender_bindings/cbindgen.toml
@@ -25,13 +25,14 @@ postfix = "WR_FUNC"
args = "Vertical"
rename_args = "GeckoCase"
[struct]
derive_eq = true
[enum]
add_sentinel = true
+derive_helper_methods = true
[defines]
"target_os = windows" = "XP_WIN"
"target_os = macos" = "XP_MACOSX"
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1576,17 +1576,18 @@ pub extern "C" fn wr_dp_push_stacking_co
animation: *const WrAnimationProperty,
opacity: *const f32,
transform: *const LayoutTransform,
transform_style: TransformStyle,
perspective: *const LayoutTransform,
mix_blend_mode: MixBlendMode,
filters: *const WrFilterOp,
filter_count: usize,
- is_backface_visible: bool) {
+ is_backface_visible: bool,
+ glyph_raster_space: GlyphRasterSpace) {
debug_assert!(unsafe { !is_in_render_thread() });
let c_filters = make_slice(filters, filter_count);
let mut filters : Vec<FilterOp> = c_filters.iter().map(|c_filter| {
match c_filter.filter_type {
WrFilterOpType::Blur => FilterOp::Blur(c_filter.argument),
WrFilterOpType::Brightness => FilterOp::Brightness(c_filter.argument),
WrFilterOpType::Contrast => FilterOp::Contrast(c_filter.argument),
@@ -1646,17 +1647,17 @@ pub extern "C" fn wr_dp_push_stacking_co
.push_stacking_context(&prim_info,
clip_node_id,
ScrollPolicy::Scrollable,
transform_binding,
transform_style,
perspective,
mix_blend_mode,
filters,
- GlyphRasterSpace::Screen);
+ glyph_raster_space);
}
#[no_mangle]
pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState) {
debug_assert!(unsafe { !is_in_render_thread() });
state.frame_builder.dl_builder.pop_stacking_context();
}
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -701,16 +701,61 @@ using LayoutTransform = TypedTransform3D
struct WrFilterOp {
WrFilterOpType filter_type;
float argument;
LayoutVector2D offset;
ColorF color;
float matrix[20];
};
+union GlyphRasterSpace {
+ enum class Tag : uint32_t {
+ Local,
+ Screen,
+
+ Sentinel /* this must be last for serialization purposes. */
+ };
+
+ struct Local_Body {
+ Tag tag;
+ float _0;
+
+ bool operator==(const Local_Body& aOther) const {
+ return tag == aOther.tag &&
+ _0 == aOther._0;
+ }
+ };
+
+ struct {
+ Tag tag;
+ };
+ Local_Body local;
+
+ static GlyphRasterSpace Local(float const& a0) {
+ GlyphRasterSpace result;
+ result.local._0 = a0;
+ result.tag = Tag::Local;
+ return result;
+ }
+
+ static GlyphRasterSpace Screen() {
+ GlyphRasterSpace result;
+ result.tag = Tag::Screen;
+ return result;
+ }
+
+ bool IsLocal() const {
+ return tag == Tag::Local;
+ }
+
+ bool IsScreen() const {
+ return tag == Tag::Screen;
+ }
+};
+
struct FontInstanceKey {
IdNamespace mNamespace;
uint32_t mHandle;
bool operator==(const FontInstanceKey& aOther) const {
return mNamespace == aOther.mNamespace &&
mHandle == aOther.mHandle;
}
@@ -1286,17 +1331,18 @@ void wr_dp_push_stacking_context(WrState
const WrAnimationProperty *aAnimation,
const float *aOpacity,
const LayoutTransform *aTransform,
TransformStyle aTransformStyle,
const LayoutTransform *aPerspective,
MixBlendMode aMixBlendMode,
const WrFilterOp *aFilters,
uintptr_t aFilterCount,
- bool aIsBackfaceVisible)
+ bool aIsBackfaceVisible,
+ GlyphRasterSpace aGlyphRasterSpace)
WR_FUNC;
WR_INLINE
void wr_dp_push_text(WrState *aState,
LayoutRect aBounds,
LayoutRect aClip,
bool aIsBackfaceVisible,
ColorF aColor,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8573,29 +8573,37 @@ nsDisplayTransform::CreateWebRenderComma
if (!mFrame->HasPerspective()) {
// If it has perspective, we create a new scroll data via the
// UpdateScrollData call because that scenario is more complex. Otherwise
// we can just stash the transform on the StackingContextHelper and
// apply it to any scroll data that are created inside this
// nsDisplayTransform.
transformForScrollData = Some(GetTransform().GetMatrix());
}
+
+ // If it looks like we're animated, we should rasterize in local space
+ // (disabling subpixel-aa and global pixel snapping)
+ bool rasterizeLocally = ActiveLayerTracker::IsStyleMaybeAnimated(
+ Frame(), eCSSProperty_transform);
+
StackingContextHelper sc(aSc,
aBuilder,
filters,
LayoutDeviceRect(position, LayoutDeviceSize()),
&newTransformMatrix,
animationsId ? &prop : nullptr,
nullptr,
transformForSC,
nullptr,
gfx::CompositionOp::OP_OVER,
!BackfaceIsHidden(),
mFrame->Extend3DContext() && !mNoExtendContext,
- transformForScrollData);
+ transformForScrollData,
+ nullptr,
+ rasterizeLocally);
return mStoredList.CreateWebRenderCommands(aBuilder, aResources, sc,
aManager, aDisplayListBuilder);
}
bool
nsDisplayTransform::UpdateScrollData(mozilla::layers::WebRenderScrollData* aData,
mozilla::layers::WebRenderLayerScrollData* aLayerData)
@@ -9687,45 +9695,47 @@ nsDisplayMask::CreateWebRenderCommands(m
bool snap;
float appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
nsRect displayBound = GetBounds(aDisplayListBuilder, &snap);
LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(displayBound, appUnitsPerDevPixel);
Maybe<wr::WrImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(this, aBuilder, aResources,
aSc, aDisplayListBuilder,
bounds);
+ Maybe<StackingContextHelper> layer;
+ const StackingContextHelper* sc = &aSc;
if (mask) {
auto layoutBounds = wr::ToRoundedLayoutRect(bounds);
wr::WrClipId clipId = aBuilder.DefineClip(Nothing(), Nothing(),
layoutBounds, nullptr, mask.ptr());
// Create a new stacking context to attach the mask to, ensuring the mask is
// applied to the aggregate, and not the individual elements.
// The stacking context shouldn't have any offset.
- layoutBounds.origin.x = 0;
- layoutBounds.origin.y = 0;
-
- aBuilder.PushStackingContext(/*aBounds: */ layoutBounds,
- /*aClipNodeId: */ &clipId,
- /*aAnimation: */ nullptr,
- /*aOpacity: */ nullptr,
- /*aTransform: */ nullptr,
- /*aTransformStyle: */ wr::TransformStyle::Flat,
- /*aPerspective: */ nullptr,
- /*aMixBlendMode: */ wr::MixBlendMode::Normal,
- /*aFilters: */ nsTArray<wr::WrFilterOp>(),
- /*aBackfaceVisible: */ true);
- }
-
- nsDisplaySVGEffects::CreateWebRenderCommands(aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
-
- if (mask) {
- aBuilder.PopStackingContext();
- }
+ bounds.MoveTo(0, 0);
+
+ layer.emplace(aSc,
+ aBuilder,
+ /*aFilters: */ nsTArray<wr::WrFilterOp>(),
+ /*aBounds: */ bounds,
+ /*aBoundTransform: */ nullptr,
+ /*aAnimation: */ nullptr,
+ /*aOpacity: */ nullptr,
+ /*aTransform: */ nullptr,
+ /*aPerspective: */ nullptr,
+ /*aMixBlendMode: */ gfx::CompositionOp::OP_OVER,
+ /*aBackfaceVisible: */ true,
+ /*aIsPreserve3D: */ false,
+ /*aTransformForScrollData: */ Nothing(),
+ /*aClipNodeId: */ &clipId);
+ sc = layer.ptr();
+ }
+
+ nsDisplaySVGEffects::CreateWebRenderCommands(aBuilder, aResources, *sc, aManager, aDisplayListBuilder);
return true;
}
Maybe<nsRect>
nsDisplayMask::GetClipWithRespectToASR(nsDisplayListBuilder* aBuilder,
const ActiveScrolledRoot* aASR) const
{