Bug 539356 - Part 9d - Make SVG support the new invalidation model. r=jwatt
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 29 Aug 2012 17:39:33 +1200
changeset 108409 9c8f66d8eee4ba1587e1a7fac9585ed1aea5c83b
parent 108408 e5651c513f3f89e2b8cb277fb375d0e0554a990b
child 108410 04df652e584727de9301b60ae52b52f46bc2f778
push id15517
push usermwoodrow@mozilla.com
push dateFri, 28 Sep 2012 11:21:47 +0000
treeherdermozilla-inbound@6b58397ad735 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs539356
milestone18.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 539356 - Part 9d - Make SVG support the new invalidation model. r=jwatt
layout/svg/nsSVGForeignObjectFrame.cpp
layout/svg/nsSVGForeignObjectFrame.h
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGIntegrationUtils.h
layout/svg/nsSVGOuterSVGFrame.cpp
layout/svg/nsSVGOuterSVGFrame.h
layout/svg/nsSVGUtils.cpp
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -608,8 +608,23 @@ nsSVGForeignObjectFrame::DoReflow()
   NS_ASSERTION(mRect.width == desiredSize.width &&
                mRect.height == desiredSize.height, "unexpected size");
   FinishReflowChild(kid, presContext, &reflowState, desiredSize, 0, 0,
                     NS_FRAME_NO_MOVE_FRAME);
   
   mInReflow = false;
 }
 
+nsRect
+nsSVGForeignObjectFrame::GetInvalidRegion()
+{
+  nsIFrame* kid = GetFirstPrincipalChild();
+  if (kid->HasInvalidFrameInSubtree()) {
+    gfxRect r(mRect.x, mRect.y, mRect.width, mRect.height);
+    r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
+    nsRect rect = ToCanvasBounds(r, GetCanvasTM(FOR_PAINTING), PresContext());
+    rect = nsSVGUtils::GetPostFilterVisualOverflowRect(this, rect);
+    return rect;
+  }
+  return nsRect();
+}
+
+
--- a/layout/svg/nsSVGForeignObjectFrame.h
+++ b/layout/svg/nsSVGForeignObjectFrame.h
@@ -85,16 +85,18 @@ public:
   virtual void ReflowSVG() MOZ_OVERRIDE;
   virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE;
   virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                       uint32_t aFlags) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsDisplayContainer() MOZ_OVERRIDE { return true; }
 
   gfxMatrix GetCanvasTM(uint32_t aFor);
 
+  nsRect GetInvalidRegion();
+
 protected:
   // implementation helpers:
   void DoReflow();
   void RequestReflow(nsIPresShell::IntrinsicDirty aType);
 
   // If width or height is less than or equal to zero we must disable rendering
   bool IsDisabled() const { return mRect.width <= 0 || mRect.height <= 0; }
 
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -275,50 +275,54 @@ nsRect
 
   nsRect overflowRect =
     filterFrame->GetPostFilterBounds(firstFrame, &overrideBBox);
 
   // Return overflowRect relative to aFrame, rather than "user space":
   return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToUserSpace);
 }
 
-nsRect
+nsIntRect
 nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
-                                                      const nsRect& aInvalidRect)
+                                                      const nsIntRect& aInvalidRect)
 {
   // Don't bother calling GetEffectProperties; the filter property should
   // already have been set up during reflow/ComputeFrameEffectsRect
   nsIFrame* firstFrame =
     nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
   if (!effectProperties.mFilter)
     return aInvalidRect;
 
   nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
   if (!prop || !prop->IsInObserverList()) {
     return aInvalidRect;
   }
 
+  int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+
   nsSVGFilterFrame* filterFrame = prop->GetFilterFrame();
   if (!filterFrame) {
     // The frame is either not there or not currently available,
     // perhaps because we're in the middle of tearing stuff down.
     // Be conservative.
-    return aFrame->GetVisualOverflowRect();
+    nsRect overflow = aFrame->GetVisualOverflowRect();
+    return overflow.ToOutsidePixels(appUnitsPerDevPixel);
   }
 
   // Convert aInvalidRect into "user space" in app units:
   nsPoint toUserSpace =
     aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
-  nsRect preEffectsRect = aInvalidRect + toUserSpace;
+  nsRect preEffectsRect = aInvalidRect.ToAppUnits(appUnitsPerDevPixel) + toUserSpace;
 
   // Return ther result, relative to aFrame, not in user space:
-  return filterFrame->GetPostFilterDirtyArea(firstFrame, preEffectsRect) -
+  nsRect result = filterFrame->GetPostFilterDirtyArea(firstFrame, preEffectsRect) -
            toUserSpace;
+  return result.ToOutsidePixels(appUnitsPerDevPixel);
 }
 
 nsRect
 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
                                                        const nsRect& aDirtyRect)
 {
   // Don't bother calling GetEffectProperties; the filter property should
   // already have been set up during reflow/ComputeFrameEffectsRect
--- a/layout/svg/nsSVGIntegrationUtils.h
+++ b/layout/svg/nsSVGIntegrationUtils.h
@@ -111,18 +111,18 @@ public:
   static nsRect
   ComputePostEffectsVisualOverflowRect(nsIFrame* aFrame,
                                        const nsRect& aPreEffectsOverflowRect);
 
   /**
    * Used to adjust the area of a frame that needs to be invalidated to take
    * account of SVG effects.
    */
-  static nsRect
-  AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame, const nsRect& aInvalidRect);
+  static nsIntRect
+  AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame, const nsIntRect& aInvalidRect);
 
   /**
    * Figure out which area of the source is needed given an area to
    * repaint
    */
   static nsRect
   GetRequiredSourceForInvalidArea(nsIFrame* aFrame, const nsRect& aDamageRect);
 
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -17,16 +17,17 @@
 #include "nsIObjectLoadingContent.h"
 #include "nsRenderingContext.h"
 #include "nsStubMutationObserver.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGSVGElement.h"
 #include "nsSVGTextFrame.h"
 #include "nsSVGViewElement.h"
+#include "nsSubDocumentFrame.h"
 
 namespace dom = mozilla::dom;
 
 class nsSVGMutationObserver : public nsStubMutationObserver
 {
 public:
   // nsIMutationObserver interface
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
@@ -500,16 +501,21 @@ public:
     MOZ_COUNT_DTOR(nsDisplayOuterSVG);
   }
 #endif
 
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx);
+
+  virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+                                         const nsDisplayItemGeometry* aGeometry,
+                                         nsRegion* aInvalidRegion);
+
   NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG)
 };
 
 void
 nsDisplayOuterSVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                            HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
   nsSVGOuterSVGFrame *outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
@@ -565,16 +571,49 @@ nsDisplayOuterSVG::Paint(nsDisplayListBu
   NS_ASSERTION(!aContext->ThebesContext()->HasError(), "Cairo in error state");
 
 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
   PRTime end = PR_Now();
   printf("SVG Paint Timing: %f ms\n", (end-start)/1000.0);
 #endif
 }
 
+static PLDHashOperator CheckForeignObjectInvalidatedArea(nsPtrHashKey<nsSVGForeignObjectFrame>* aEntry, void* aData)
+{
+  nsRegion* region = static_cast<nsRegion*>(aData);
+  region->Or(*region, aEntry->GetKey()->GetInvalidRegion());
+  return PL_DHASH_NEXT;
+}
+
+nsRegion
+nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame* aFrame)
+{
+  nsRegion result;
+  if (mForeignObjectHash.Count()) {
+    mForeignObjectHash.EnumerateEntries(CheckForeignObjectInvalidatedArea, &result);
+  }
+  return result;
+}
+
+void
+nsDisplayOuterSVG::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+                                             const nsDisplayItemGeometry* aGeometry,
+                                             nsRegion* aInvalidRegion)
+{
+  nsSVGOuterSVGFrame *frame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
+  frame->InvalidateSVG(frame->FindInvalidatedForeignObjectFrameChildren(frame));
+
+  nsRegion result = frame->GetInvalidRegion();
+  result.MoveBy(ToReferenceFrame());
+  frame->ClearInvalidRegion();
+
+  nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
+  aInvalidRegion->Or(*aInvalidRegion, result);
+}
+
 // helper
 static inline bool
 DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame)
 {
   const nsStylePosition *pos = aEmbeddingFrame->GetStylePosition();
   const nsStyleCoord &width = pos->mWidth;
   const nsStyleCoord &height = pos->mHeight;
 
--- a/layout/svg/nsSVGOuterSVGFrame.h
+++ b/layout/svg/nsSVGOuterSVGFrame.h
@@ -139,16 +139,35 @@ public:
    * the height is explicitly set to a percentage value no greater than 100%.
    */
   bool VerticalScrollbarNotNeeded() const;
 
   bool IsCallingReflowSVG() const {
     return mCallingReflowSVG;
   }
 
+  void InvalidateSVG(const nsRegion& aRegion)
+  {
+    if (!aRegion.IsEmpty()) {
+      mInvalidRegion.Or(mInvalidRegion, aRegion);
+      InvalidateFrame();
+    }
+  }
+  
+  void ClearInvalidRegion() { mInvalidRegion.SetEmpty(); }
+
+  const nsRegion& GetInvalidRegion() {
+    if (!IsInvalid()) {
+      mInvalidRegion.SetEmpty();
+    }
+    return mInvalidRegion;
+  }
+
+  nsRegion FindInvalidatedForeignObjectFrameChildren(nsIFrame* aFrame);
+
 protected:
 
   bool mCallingReflowSVG;
 
   /* Returns true if our content is the document element and our document is
    * embedded in an HTML 'object', 'embed' or 'applet' element. Set
    * aEmbeddingFrame to obtain the nsIFrame for the embedding HTML element.
    */
@@ -159,20 +178,22 @@ protected:
    */
   bool IsRootOfImage();
 
   // This is temporary until display list based invalidation is implemented for
   // SVG.
   // A hash-set containing our nsSVGForeignObjectFrame descendants. Note we use
   // a hash-set to avoid the O(N^2) behavior we'd get tearing down an SVG frame
   // subtree if we were to use a list (see bug 381285 comment 20).
-  nsTHashtable<nsVoidPtrHashKey> mForeignObjectHash;
+  nsTHashtable<nsPtrHashKey<nsSVGForeignObjectFrame> > mForeignObjectHash;
 
   nsAutoPtr<gfxMatrix> mCanvasTM;
 
+  nsRegion mInvalidRegion; 
+
   float mFullZoom;
 
   bool mViewportInitialized;
   bool mIsRootContent;
 };
 
 ////////////////////////////////////////////////////////////////////////
 // nsSVGOuterSVGAnonChildFrame class
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -410,16 +410,24 @@ nsSVGUtils::InvalidateBounds(nsIFrame *a
   // XXXjwatt: can this come before InvalidateRenderingObservers?
   if (aFrame->GetStateBits() &
       (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
     // Nothing to do if we're already dirty, or if the outer-<svg>
     // hasn't yet had its initial reflow.
     return;
   }
 
+  aFrame->InvalidateFrameSubtree();
+
+  if ((aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame ||
+      aFrame->GetType() == nsGkAtoms::svgGlyphFrame) &&
+      NS_SVGDisplayListPaintingEnabled()) {
+    return;
+  }
+
   // Okay, so now we pass the area that needs to be invalidated up our parent
   // chain, accounting for filter effects and transforms as we go, until we
   // reach our nsSVGOuterSVGFrame where we can invalidate:
 
   nsRect invalidArea;
   if (aBoundsSubArea) {
     invalidArea = *aBoundsSubArea;
   } else {
@@ -474,18 +482,17 @@ nsSVGUtils::InvalidateBounds(nsIFrame *a
     // We seem to be able to get here, even though SVG frames are never created
     // without an ancestor nsSVGOuterSVGFrame. See bug 767996.
     return;
   }
 
   NS_ASSERTION(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG,
                "SVG frames must always have an nsSVGOuterSVGFrame ancestor!");
 
-  static_cast<nsSVGOuterSVGFrame*>(aFrame)->InvalidateWithFlags(invalidArea,
-                                                                aFlags);
+  static_cast<nsSVGOuterSVGFrame*>(aFrame)->InvalidateSVG(invalidArea);
 }
 
 void
 nsSVGUtils::ScheduleReflowSVG(nsIFrame *aFrame)
 {
   NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
                     "Passed bad frame!");