Bug 992486 - Part 1: Add API to RotatedContentBuffer for drawing in multiple passes. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 09 Apr 2014 21:15:17 +1200
changeset 177814 eb2bea26398aa13703da2cf663a9054ef85f0927
parent 177813 fda54da252d0b42d9652cc59db4ae7049a2a2b43
child 177815 ca3c03bfcf89da224cb43377cce0d3143ee1107c
push id26566
push useremorley@mozilla.com
push dateThu, 10 Apr 2014 16:55:48 +0000
treeherdermozilla-central@83ae54e18689 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs992486
milestone31.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 992486 - Part 1: Add API to RotatedContentBuffer for drawing in multiple passes. r=roc
gfx/layers/RotatedBuffer.cpp
gfx/layers/RotatedBuffer.h
gfx/layers/basic/BasicThebesLayer.cpp
gfx/layers/client/ClientThebesLayer.cpp
gfx/layers/client/ContentClient.cpp
gfx/layers/client/ContentClient.h
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -526,22 +526,24 @@ RotatedContentBuffer::BeginPaint(ThebesL
       nsIntPoint newRotation = mBufferRotation +
         (destBufferRect.TopLeft() - mBufferRect.TopLeft());
       WrapRotationAxis(&newRotation.x, mBufferRect.width);
       WrapRotationAxis(&newRotation.y, mBufferRect.height);
       NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
                    "newRotation out of bounds");
       int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
       int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
-      if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
-          (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
+      bool drawWrapsBuffer = (drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
+                             (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost());
+      if ((drawWrapsBuffer && !(aFlags & PAINT_CAN_DRAW_ROTATED)) ||
           (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
         // The stuff we need to redraw will wrap around an edge of the
-        // buffer, so move the pixels we can keep into a position that
-        // lets us redraw in just one quadrant.
+        // buffer (and the caller doesn't know how to support that), so
+        // move the pixels we can keep into a position that lets us
+        // redraw in just one quadrant.
         if (mBufferRotation == nsIntPoint(0,0)) {
           nsIntRect srcRect(nsIntPoint(0, 0), mBufferRect.Size());
           nsIntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
           MOZ_ASSERT(mDTBuffer);
           mDTBuffer->CopyRect(IntRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height),
                               IntPoint(dest.x, dest.y));
           if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
             if (!EnsureBufferOnWhite()) {
@@ -669,39 +671,64 @@ RotatedContentBuffer::BeginPaint(ThebesL
   result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
   result.mClip = DrawRegionClip::DRAW_SNAPPED;
   result.mMode = mode;
 
   return result;
 }
 
 DrawTarget*
-RotatedContentBuffer::BorrowDrawTargetForPainting(ThebesLayer* aLayer,
-                                                  const PaintState& aPaintState)
+RotatedContentBuffer::BorrowDrawTargetForPainting(const PaintState& aPaintState,
+                                                  DrawIterator* aIter /* = nullptr */)
 {
   if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
     return nullptr;
   }
 
-  DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
+  const nsIntRegion* drawPtr;
+  if (aIter) {
+    // If an iterator was provided, then BeginPaint must have been run with
+    // PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.
+    // Iterate over each of them, and return an appropriate buffer each time we find
+    // one that intersects the draw region. The iterator mCount value tracks which
+    // quadrants we have considered across multiple calls to this function.
+    aIter->mDrawRegion.SetEmpty();
+    while (aIter->mCount < 4) {
+      nsIntRect quadrant = GetQuadrantRectangle((aIter->mCount & 1) ? LEFT : RIGHT,
+                                                (aIter->mCount & 2) ? TOP : BOTTOM);
+      aIter->mDrawRegion.And(aPaintState.mRegionToDraw, quadrant);
+      aIter->mCount++;
+      if (!aIter->mDrawRegion.IsEmpty()) {
+        break;
+      }
+    }
+    if (aIter->mDrawRegion.IsEmpty()) {
+      return nullptr;
+    }
+    drawPtr = &aIter->mDrawRegion;
+  } else {
+    drawPtr = &aPaintState.mRegionToDraw;
+  }
+
+  DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(drawPtr->GetBounds(),
                                                          BUFFER_BOTH);
 
   if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite);
-    nsIntRegionRectIterator iter(aPaintState.mRegionToDraw);
+    nsIntRegionRectIterator iter(*drawPtr);
     const nsIntRect *iterRect;
     while ((iterRect = iter.Next())) {
       mDTBuffer->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
                           ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
       mDTBufferOnWhite->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
                                  ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
     }
   } else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) {
     // HaveBuffer() => we have an existing buffer that we must clear
-    nsIntRegionRectIterator iter(aPaintState.mRegionToDraw);
+    nsIntRegionRectIterator iter(*drawPtr);
     const nsIntRect *iterRect;
     while ((iterRect = iter.Next())) {
       result->ClearRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height));
     }
   }
 
   return result;
 }
--- a/gfx/layers/RotatedBuffer.h
+++ b/gfx/layers/RotatedBuffer.h
@@ -218,45 +218,72 @@ public:
     SurfaceMode mMode;
     DrawRegionClip mClip;
     ContentType mContentType;
     bool mDidSelfCopy;
   };
 
   enum {
     PAINT_WILL_RESAMPLE = 0x01,
-    PAINT_NO_ROTATION = 0x02
+    PAINT_NO_ROTATION = 0x02,
+    PAINT_CAN_DRAW_ROTATED = 0x04
   };
   /**
    * Start a drawing operation. This returns a PaintState describing what
    * needs to be drawn to bring the buffer up to date in the visible region.
    * This queries aLayer to get the currently valid and visible regions.
    * The returned mTarget may be null if mRegionToDraw is empty.
    * Otherwise it must not be null.
    * mRegionToInvalidate will contain mRegionToDraw.
    * @param aFlags when PAINT_WILL_RESAMPLE is passed, this indicates that
    * buffer will be resampled when rendering (i.e the effective transform
    * combined with the scale for the resolution is not just an integer
    * translation). This will disable buffer rotation (since we don't want
    * to resample across the rotation boundary) and will ensure that we
    * make the entire buffer contents valid (since we don't want to sample
    * invalid pixels outside the visible region, if the visible region doesn't
    * fill the buffer bounds).
+   * PAINT_CAN_DRAW_ROTATED can be passed if the caller supports drawing
+   * rotated content that crosses the physical buffer boundary. The caller
+   * will need to call BorrowDrawTargetForPainting multiple times to achieve
+   * this.
    */
   PaintState BeginPaint(ThebesLayer* aLayer,
                         uint32_t aFlags);
 
+  struct DrawIterator {
+    friend class RotatedContentBuffer;
+    friend class ContentClientIncremental;
+    DrawIterator()
+      : mCount(0)
+    {}
+
+    nsIntRegion mDrawRegion;
+
+  private:
+    uint32_t mCount;
+  };
+
   /**
    * Fetch a DrawTarget for rendering. The DrawTarget remains owned by
    * this. See notes on BorrowDrawTargetForQuadrantUpdate.
    * May return null. If the return value is non-null, it must be
    * 'un-borrowed' using ReturnDrawTarget.
+   *
+   * If PAINT_CAN_DRAW_ROTATED was specified for BeginPaint, then the caller
+   * must call this function repeatedly (with an iterator) until it returns
+   * nullptr. The caller should draw the mDrawRegion of the iterator instead
+   * of mRegionToDraw in the PaintState.
+   *
+   * @param aPaintState Paint state data returned by a call to BeginPaint
+   * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED
+   * was specified to BeginPaint.
    */
-  gfx::DrawTarget* BorrowDrawTargetForPainting(ThebesLayer* aLayer,
-                                               const PaintState& aPaintState);
+  gfx::DrawTarget* BorrowDrawTargetForPainting(const PaintState& aPaintState,
+                                               DrawIterator* aIter = nullptr);
 
   enum {
     ALLOW_REPEAT = 0x01,
     BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
                                   // component alpha.
   };
   /**
    * Return a new surface of |aSize| and |aType|.
--- a/gfx/layers/basic/BasicThebesLayer.cpp
+++ b/gfx/layers/basic/BasicThebesLayer.cpp
@@ -181,17 +181,17 @@ BasicThebesLayer::Validate(LayerManager:
 #endif
   if (mDrawAtomically) {
     flags |= RotatedContentBuffer::PAINT_NO_ROTATION;
   }
   PaintState state =
     mContentClient->BeginPaintBuffer(this, flags);
   mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
 
-  if (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(this, state)) {
+  if (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state)) {
     // The area that became invalid and is visible needs to be repainted
     // (this could be the whole visible area if our buffer switched
     // from RGB to RGBA, because we might need to repaint with
     // subpixel AA)
     state.mRegionToInvalidate.And(state.mRegionToInvalidate,
                                   GetEffectiveVisibleRegion());
     SetAntialiasingFlags(this, target);
 
--- a/gfx/layers/client/ClientThebesLayer.cpp
+++ b/gfx/layers/client/ClientThebesLayer.cpp
@@ -51,17 +51,17 @@ ClientThebesLayer::PaintThebes()
         flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
       }
     }
 #endif
     PaintState state =
       mContentClient->BeginPaintBuffer(this, flags);
     mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
 
-    if (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(this, state)) {
+    if (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state)) {
       // The area that became invalid and is visible needs to be repainted
       // (this could be the whole visible area if our buffer switched
       // from RGB to RGBA, because we might need to repaint with
       // subpixel AA)
       state.mRegionToInvalidate.And(state.mRegionToInvalidate,
                                     GetEffectiveVisibleRegion());
       SetAntialiasingFlags(this, target);
 
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -780,23 +780,30 @@ ContentClientIncremental::BeginPaintBuff
   // clip and draw regions.
   result.mClip = DrawRegionClip::DRAW;
   result.mMode = mode;
 
   return result;
 }
 
 DrawTarget*
-ContentClientIncremental::BorrowDrawTargetForPainting(ThebesLayer* aLayer,
-                                                      const PaintState& aPaintState)
+ContentClientIncremental::BorrowDrawTargetForPainting(const PaintState& aPaintState,
+                                                      RotatedContentBuffer::DrawIterator* aIter)
 {
   if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
     return nullptr;
   }
 
+  if (aIter) {
+    if (aIter->mCount++ > 0) {
+      return nullptr;
+    }
+    aIter->mDrawRegion = aPaintState.mRegionToDraw;
+  }
+
   DrawTarget* result = nullptr;
 
   nsIntRect drawBounds = aPaintState.mRegionToDraw.GetBounds();
   MOZ_ASSERT(!mLoanedDrawTarget);
 
   // BeginUpdate is allowed to modify the given region,
   // if it wants more to be repainted than we request.
   if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -90,18 +90,18 @@ public:
   {}
   virtual ~ContentClient()
   {}
 
 
   virtual void Clear() = 0;
   virtual RotatedContentBuffer::PaintState BeginPaintBuffer(ThebesLayer* aLayer,
                                                             uint32_t aFlags) = 0;
-  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(ThebesLayer* aLayer,
-                                                       const RotatedContentBuffer::PaintState& aPaintState) = 0;
+  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(const RotatedContentBuffer::PaintState& aPaintState,
+                                                       RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
   virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0;
 
   virtual void PrepareFrame() {}
 
   // Called as part of the layers transation reply. Conveys data about our
   // buffer(s) from the compositor. If appropriate we should swap references
   // to our buffers.
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {}
@@ -138,20 +138,20 @@ public:
   typedef RotatedContentBuffer::ContentType ContentType;
 
   virtual void Clear() { RotatedContentBuffer::Clear(); }
   virtual PaintState BeginPaintBuffer(ThebesLayer* aLayer,
                                       uint32_t aFlags) MOZ_OVERRIDE
   {
     return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
   }
-  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(ThebesLayer* aLayer,
-                                                       const PaintState& aPaintState) MOZ_OVERRIDE
+  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(const PaintState& aPaintState,
+                                                       RotatedContentBuffer::DrawIterator* aIter = nullptr) MOZ_OVERRIDE
   {
-    return RotatedContentBuffer::BorrowDrawTargetForPainting(aLayer, aPaintState);
+    return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
   }
   virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) MOZ_OVERRIDE
   {
     BorrowDrawTarget::ReturnDrawTarget(aReturned);
   }
 
   void DrawTo(ThebesLayer* aLayer,
               gfx::DrawTarget* aTarget,
@@ -213,20 +213,20 @@ public:
     mTextureClientOnWhite = nullptr;
   }
 
   virtual PaintState BeginPaintBuffer(ThebesLayer* aLayer,
                                       uint32_t aFlags) MOZ_OVERRIDE
   {
     return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
   }
-  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(ThebesLayer* aLayer,
-                                                       const PaintState& aPaintState) MOZ_OVERRIDE
+  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(const PaintState& aPaintState,
+                                                       RotatedContentBuffer::DrawIterator* aIter = nullptr) MOZ_OVERRIDE
   {
-    return RotatedContentBuffer::BorrowDrawTargetForPainting(aLayer, aPaintState);
+    return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
   }
   virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) MOZ_OVERRIDE
   {
     BorrowDrawTarget::ReturnDrawTarget(aReturned);
   }
 
   /**
    * Begin/End Paint map a gfxASurface from the texture client
@@ -415,18 +415,18 @@ public:
   {
     mBufferRect.SetEmpty();
     mHasBuffer = false;
     mHasBufferOnWhite = false;
   }
 
   virtual PaintState BeginPaintBuffer(ThebesLayer* aLayer,
                                       uint32_t aFlags) MOZ_OVERRIDE;
-  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(ThebesLayer* aLayer,
-                                                       const PaintState& aPaintState) MOZ_OVERRIDE;
+  virtual gfx::DrawTarget* BorrowDrawTargetForPainting(const PaintState& aPaintState,
+                                                       RotatedContentBuffer::DrawIterator* aIter = nullptr) MOZ_OVERRIDE;
   virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) MOZ_OVERRIDE
   {
     BorrowDrawTarget::ReturnDrawTarget(aReturned);
   }
 
   virtual void Updated(const nsIntRegion& aRegionToDraw,
                        const nsIntRegion& aVisibleRegion,
                        bool aDidSelfCopy);