Bug 1420628 - Avoid allocations for each text display item. r=jrmuizel
authorMatt Woodrow <mwoodrow@mozilla.com>
Sun, 25 Nov 2018 22:57:04 +0000
changeset 504378 f5c8fe43b6ee1be0f100ff3777c8d99c4ce8af40
parent 504377 995042d1cd8cdc7bd7ea8724017afde36cc69b7c
child 504379 d6509279d52e5c1a9b2951b88f23a989470ff195
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1420628
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1420628 - Avoid allocations for each text display item. r=jrmuizel MozReview-Commit-ID: HoRHQVEdJeT Differential Revision: https://phabricator.services.mozilla.com/D12720
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxContext.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
layout/generic/TextDrawTarget.h
layout/generic/TextOverflow.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsTextFrame.cpp
layout/xul/nsTextBoxFrame.cpp
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -1016,16 +1016,22 @@ gfxContext::GetAzureDeviceSpaceClipBound
 }
 
 Point
 gfxContext::GetDeviceOffset() const
 {
   return CurrentState().deviceOffset;
 }
 
+void
+gfxContext::SetDeviceOffset(const Point& aOffset)
+{
+  CurrentState().deviceOffset = aOffset;
+}
+
 Matrix
 gfxContext::GetDeviceTransform() const
 {
   return Matrix::Translation(-CurrentState().deviceOffset.x,
                              -CurrentState().deviceOffset.y);
 }
 
 Matrix
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -420,16 +420,17 @@ public:
      */
     void PushGroupAndCopyBackground(gfxContentType content = gfxContentType::COLOR,
                                     mozilla::gfx::Float aOpacity = 1.0f,
                                     mozilla::gfx::SourceSurface* aMask = nullptr,
                                     const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
     void PopGroupAndBlend();
 
     mozilla::gfx::Point GetDeviceOffset() const;
+    void SetDeviceOffset(const mozilla::gfx::Point& aOffset);
 
 #ifdef MOZ_DUMP_PAINTING
     /**
      * Debug functions to encode the current surface as a PNG and export it.
      */
 
     /**
      * Writes a binary PNG file.
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -9,16 +9,17 @@
 #include "gfxPrefs.h"
 #include "LayersLogging.h"
 #include "mozilla/webrender/RendererOGL.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/webrender/RenderCompositor.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "mozilla/layers/SynchronousTask.h"
+#include "TextDrawTarget.h"
 
 #define WRDL_LOG(...)
 //#define WRDL_LOG(...) printf_stderr("WRDL(%p): " __VA_ARGS__)
 //#define WRDL_LOG(...) if (XRE_IsContentProcess()) printf_stderr("WRDL(%p): " __VA_ARGS__)
 
 namespace mozilla {
 namespace wr {
 
@@ -1401,16 +1402,37 @@ DisplayListBuilder::FixedPosScrollTarget
 }
 
 Maybe<layers::ScrollableLayerGuid::ViewID>
 DisplayListBuilder::FixedPosScrollTargetTracker::GetScrollTargetForASR(const ActiveScrolledRoot* aAsr)
 {
   return aAsr == mAsr ? Some(mScrollId) : Nothing();
 }
 
+already_AddRefed<gfxContext>
+DisplayListBuilder::GetTextContext(wr::IpcResourceUpdateQueue& aResources,
+                                   const layers::StackingContextHelper& aSc,
+                                   layers::WebRenderLayerManager* aManager,
+                                   nsDisplayItem* aItem,
+                                   nsRect& aBounds,
+                                   const gfx::Point& aDeviceOffset)
+{
+  if (!mCachedTextDT) {
+    mCachedTextDT = new layout::TextDrawTarget(*this, aResources, aSc, aManager, aItem, aBounds);
+    mCachedContext = gfxContext::CreateOrNull(mCachedTextDT, aDeviceOffset);
+  } else {
+    mCachedTextDT->Reinitialize(aResources, aSc, aManager, aItem, aBounds);
+    mCachedContext->SetDeviceOffset(aDeviceOffset);
+    mCachedContext->SetMatrix(Matrix());
+  }
+
+  RefPtr<gfxContext> tmp = mCachedContext;
+  return tmp.forget();
+}
+
 } // namespace wr
 } // namespace mozilla
 
 extern "C" {
 
 void wr_transaction_notification_notified(uintptr_t aHandler, mozilla::wr::Checkpoint aWhen) {
   auto handler = reinterpret_cast<mozilla::wr::NotificationHandler*>(aHandler);
   handler->Notify(aWhen);
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -16,27 +16,34 @@
 #include "mozilla/layers/ScrollableLayerGuid.h"
 #include "mozilla/layers/SyncObject.h"
 #include "mozilla/Range.h"
 #include "mozilla/webrender/webrender_ffi.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "GLTypes.h"
 #include "Units.h"
 
+class nsDisplayItem;
+
 namespace mozilla {
 
 struct ActiveScrolledRoot;
 
 namespace widget {
 class CompositorWidget;
 }
 
 namespace layers {
 class CompositorBridgeParent;
 class WebRenderBridgeParent;
+class WebRenderLayerManager;
+}
+
+namespace layout {
+class TextDrawTarget;
 }
 
 namespace wr {
 
 class DisplayListBuilder;
 class RendererOGL;
 class RendererEvent;
 
@@ -530,16 +537,23 @@ public:
 
   // Set the hit-test info to be used for all display items until the next call
   // to SetHitTestInfo or ClearHitTestInfo.
   void SetHitTestInfo(const layers::ScrollableLayerGuid::ViewID& aScrollId,
                       gfx::CompositorHitTestInfo aHitInfo);
   // Clears the hit-test info so that subsequent display items will not have it.
   void ClearHitTestInfo();
 
+  already_AddRefed<gfxContext> GetTextContext(wr::IpcResourceUpdateQueue& aResources,
+                                              const layers::StackingContextHelper& aSc,
+                                              layers::WebRenderLayerManager* aManager,
+                                              nsDisplayItem* aItem,
+                                              nsRect& aBounds,
+                                              const gfx::Point& aDeviceOffset);
+
   // Try to avoid using this when possible.
   wr::WrState* Raw() { return mWrState; }
 
   // A chain of RAII objects, each holding a (ASR, ViewID) tuple of data. The
   // topmost object is pointed to by the mActiveFixedPosTracker pointer in
   // the wr::DisplayListBuilder.
   class MOZ_RAII FixedPosScrollTargetTracker {
   public:
@@ -572,16 +586,19 @@ protected:
   // as that results in undefined behaviour in WR.
   std::unordered_map<layers::ScrollableLayerGuid::ViewID, wr::WrClipId> mScrollIds;
 
   // Contains the current leaf of the clip chain to be merged with the
   // display item's clip rect when pushing an item. May be set to Nothing() if
   // there is no clip rect to merge with.
   Maybe<wr::LayoutRect> mClipChainLeaf;
 
+  RefPtr<layout::TextDrawTarget> mCachedTextDT;
+  RefPtr<gfxContext> mCachedContext;
+
   FixedPosScrollTargetTracker* mActiveFixedPosTracker;
 
   friend class WebRenderAPI;
 };
 
 Maybe<wr::ImageFormat>
 SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat);
 
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -51,54 +51,74 @@ class TextDrawTarget : public DrawTarget
 {
 public:
   explicit TextDrawTarget(wr::DisplayListBuilder& aBuilder,
                           wr::IpcResourceUpdateQueue& aResources,
                           const layers::StackingContextHelper& aSc,
                           layers::WebRenderLayerManager* aManager,
                           nsDisplayItem* aItem,
                           nsRect& aBounds)
-    : mBuilder(aBuilder), mResources(aResources), mSc(aSc), mManager(aManager)
+    : mBuilder(aBuilder)
+  {
+    Reinitialize(aResources, aSc, aManager, aItem, aBounds);
+  }
+
+  // Prevent this from being copied
+  TextDrawTarget(const TextDrawTarget& src) = delete;
+  TextDrawTarget& operator=(const TextDrawTarget&) = delete;
+
+  ~TextDrawTarget()
   {
+  }
+
+  void Reinitialize(wr::IpcResourceUpdateQueue& aResources,
+                    const layers::StackingContextHelper& aSc,
+                    layers::WebRenderLayerManager* aManager,
+                    nsDisplayItem* aItem,
+                    nsRect& aBounds)
+  {
+    mResources = &aResources;
+    mSc = &aSc;
+    mManager = aManager;
+    mHasUnsupportedFeatures = false;
+    mHasShadows = false;
+
     SetPermitSubpixelAA(!aItem->IsSubpixelAADisabled());
 
     // Compute clip/bounds
     auto appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
     LayoutDeviceRect layoutBoundsRect = LayoutDeviceRect::FromAppUnits(
         aBounds, appUnitsPerDevPixel);
     LayoutDeviceRect layoutClipRect = layoutBoundsRect;
     mBoundsRect = wr::ToRoundedLayoutRect(layoutBoundsRect);
 
     // Add 1 pixel of dirty area around clip rect to allow us to paint
     // antialiased pixels beyond the measured text extents.
     layoutClipRect.Inflate(1);
     mSize = IntSize::Ceil(layoutClipRect.Width(), layoutClipRect.Height());
+    mClipStack.ClearAndRetainStorage();
     mClipStack.AppendElement(layoutClipRect);
 
     mBackfaceVisible = !aItem->BackfaceIsHidden();
 
     mBuilder.Save();
   }
 
-  // Prevent this from being copied
-  TextDrawTarget(const TextDrawTarget& src) = delete;
-  TextDrawTarget& operator=(const TextDrawTarget&) = delete;
 
-  ~TextDrawTarget()
-  {
+  void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; }
+
+  bool Finish() {
     if (mHasUnsupportedFeatures) {
       mBuilder.Restore();
-    } else {
-      mBuilder.ClearSave();
+      return false;
     }
+    mBuilder.ClearSave();
+    return true;
   }
 
-  void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; }
-  bool HasUnsupportedFeatures() { return mHasUnsupportedFeatures; }
-
   wr::FontInstanceFlags GetWRGlyphFlags() const { return mWRGlyphFlags; }
   void SetWRGlyphFlags(wr::FontInstanceFlags aFlags) { mWRGlyphFlags = aFlags; }
 
   class AutoRestoreWRGlyphFlags
   {
   public:
     ~AutoRestoreWRGlyphFlags()
     {
@@ -169,17 +189,17 @@ public:
                   && sizeof(aBuffer.mGlyphs[0]) == sizeof(glyphs[0])
                   && sizeof(aBuffer.mGlyphs[0].mPosition) == sizeof(glyphs[0].point)
                   , "glyph buf types don't match");
 
     wr::GlyphOptions glyphOptions;
     glyphOptions.render_mode = wr::ToFontRenderMode(aOptions.mAntialiasMode, GetPermitSubpixelAA());
     glyphOptions.flags = mWRGlyphFlags;
 
-    mManager->WrBridge()->PushGlyphs(mBuilder, glyphs, aFont, color, mSc,
+    mManager->WrBridge()->PushGlyphs(mBuilder, glyphs, aFont, color, *mSc,
                                      mBoundsRect, ClipRect(), mBackfaceVisible,
                                      &glyphOptions);
   }
 
   void
   PushClipRect(const Rect &aRect) override {
     LayoutDeviceRect rect = LayoutDeviceRect::FromUnknownRect(aRect);
     rect = rect.Intersect(mClipStack.LastElement());
@@ -311,17 +331,17 @@ public:
   DefineImage(const IntSize& aSize,
               uint32_t aStride,
               SurfaceFormat aFormat,
               const uint8_t* aData)
   {
     wr::ImageKey key = mManager->WrBridge()->GetNextImageKey();
     wr::ImageDescriptor desc(aSize, aStride, aFormat);
     Range<uint8_t> bytes(const_cast<uint8_t*>(aData), aStride * aSize.height);
-    if (mResources.AddImage(key, desc, bytes)) {
+    if (mResources->AddImage(key, desc, bytes)) {
         return Some(key);
     }
     return Nothing();
   }
 
   void PushImage(wr::ImageKey aKey,
                  const wr::LayoutRect& aBounds,
                  const wr::LayoutRect& aClip,
@@ -347,24 +367,24 @@ private:
   // * Text stroke
   bool mHasUnsupportedFeatures = false;
 
   // Whether PopAllShadows needs to be called
   bool mHasShadows = false;
 
   // Things used to push to webrender
   wr::DisplayListBuilder& mBuilder;
-  wr::IpcResourceUpdateQueue& mResources;
-  const layers::StackingContextHelper& mSc;
+  wr::IpcResourceUpdateQueue* mResources;
+  const layers::StackingContextHelper* mSc;
   layers::WebRenderLayerManager* mManager;
 
   // Computed facts
   IntSize mSize;
   wr::LayoutRect mBoundsRect;
-  nsTArray<LayoutDeviceRect> mClipStack;
+  AutoTArray<LayoutDeviceRect, 3> mClipStack;
   bool mBackfaceVisible;
 
   wr::FontInstanceFlags mWRGlyphFlags = {0};
 
   // The rest of this is dummy implementations of DrawTarget's API
 public:
   DrawTargetType GetType() const override {
     return DrawTargetType::SOFTWARE_RASTER;
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -304,17 +304,17 @@ nsDisplayTextOverflowMarker::CreateWebRe
   }
 
   // Run the rendering algorithm to capture the glyphs and shadows
   RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aBuilder, aResources, aSc, aManager, this, bounds);
   RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
   Paint(aDisplayListBuilder, captureCtx);
   textDrawer->TerminateShadows();
 
-  return !textDrawer->HasUnsupportedFeatures();
+  return textDrawer->Finish();
 }
 
 
 TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder,
                            nsIFrame* aBlockFrame)
   : mContentArea(aBlockFrame->GetWritingMode(),
                  aBlockFrame->GetContentRectRelativeToSelf(),
                  aBlockFrame->GetSize())
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -574,17 +574,17 @@ BulletRenderer::CreateWebRenderCommandsF
     return true;
   }
 
   RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aBuilder, aResources, aSc, aManager, aItem, bounds);
   RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
   PaintTextToContext(aItem->Frame(), captureCtx, aItem->IsSubpixelAADisabled());
   textDrawer->TerminateShadows();
 
-  return !textDrawer->HasUnsupportedFeatures();
+  return textDrawer->Finish();
 }
 
 class nsDisplayBullet final : public nsDisplayItem {
 public:
   nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
     : nsDisplayItem(aBuilder, aFrame)
   {
     MOZ_COUNT_CTOR(nsDisplayBullet);
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5124,22 +5124,21 @@ nsDisplayText::CreateWebRenderCommands(m
     return true;
   }
 
 
   auto appUnitsPerDevPixel = Frame()->PresContext()->AppUnitsPerDevPixel();
   gfx::Point deviceOffset = LayoutDevicePoint::FromAppUnits(
       mBounds.TopLeft(), appUnitsPerDevPixel).ToUnknownPoint();
 
-  RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aBuilder, aResources, aSc, aManager, this, mBounds);
-  RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer, deviceOffset);
-
-  RenderToContext(captureCtx, aDisplayListBuilder, true);
-
-  return !textDrawer->HasUnsupportedFeatures();
+  RefPtr<gfxContext> textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager, this, mBounds, deviceOffset);
+
+  RenderToContext(textDrawer, aDisplayListBuilder, true);
+
+  return textDrawer->GetTextDrawer()->Finish();
 }
 
 void
 nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording)
 {
   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
 
   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -369,17 +369,17 @@ nsDisplayXULTextBox::CreateWebRenderComm
 
   RefPtr<mozilla::layout::TextDrawTarget> textDrawer =
       new mozilla::layout::TextDrawTarget(aBuilder, aResources, aSc, aManager, this, bounds);
   RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer, deviceOffset);
 
   Paint(aDisplayListBuilder, captureCtx);
   textDrawer->TerminateShadows();
 
-  return !textDrawer->HasUnsupportedFeatures();
+  return textDrawer->Finish();
 }
 
 nsRect
 nsDisplayXULTextBox::GetBounds(nsDisplayListBuilder* aBuilder,
                                bool* aSnap) const
 {
   *aSnap = false;
   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();