Bug 1435094 - wire up GlyphRasterSpace to nsDisplayTransform. r?mstange,kats draft
authorAlexis Beingessner <a.beingessner@gmail.com>
Thu, 03 May 2018 20:38:37 -0400
changeset 791341 846021b7b358c1ebd9acd90df13958044e295c9e
parent 791312 ce7072c02389db590c487ca5863b7f00ce22336a
push id108785
push userbmo:a.beingessner@gmail.com
push dateFri, 04 May 2018 01:45:30 +0000
reviewersmstange, kats
bugs1435094
milestone61.0a1
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
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/StackingContextHelper.cpp
gfx/layers/wr/StackingContextHelper.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/cbindgen.toml
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
layout/painting/nsDisplayList.cpp
--- 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
 {