Batch consecutive draw rects.

Review URL: http://codereview.appspot.com/4178057/

git-svn-id: http://skia.googlecode.com/svn/trunk@800 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrInOrderDrawBuffer.cpp b/gpu/src/GrInOrderDrawBuffer.cpp
index 49b8901..25e74e3 100644
--- a/gpu/src/GrInOrderDrawBuffer.cpp
+++ b/gpu/src/GrInOrderDrawBuffer.cpp
@@ -18,6 +18,8 @@
 #include "GrInOrderDrawBuffer.h"
 #include "GrTexture.h"
 #include "GrBufferAllocPool.h"
+#include "GrIndexBuffer.h"
+#include "GrVertexBuffer.h"
 #include "GrGpu.h"
 
 GrInOrderDrawBuffer::GrInOrderDrawBuffer(GrVertexBufferAllocPool* vertexPool,
@@ -25,7 +27,13 @@
         fDraws(DRAWS_BLOCK_SIZE, fDrawsStorage),
         fStates(STATES_BLOCK_SIZE, fStatesStorage),
         fClips(CLIPS_BLOCK_SIZE, fClipsStorage),
-        fClipChanged(true),
+        fClipSet(true),
+
+        fLastRectVertexLayout(0),
+        fQuadIndexBuffer(NULL),
+        fMaxQuads(0),
+        fCurrQuad(0),
+
         fVertexPool(*vertexPool),
         fCurrPoolVertexBuffer(NULL),
         fCurrPoolStartVertex(0),
@@ -41,7 +49,8 @@
 }
 
 GrInOrderDrawBuffer::~GrInOrderDrawBuffer() {
-    reset();
+    this->reset();
+    GrSafeUnref(fQuadIndexBuffer);
 }
 
 void GrInOrderDrawBuffer::initializeDrawStateAndClip(const GrDrawTarget& target) {
@@ -49,6 +58,121 @@
     this->setClip(target.getClip());
 }
 
+void GrInOrderDrawBuffer::setQuadIndexBuffer(const GrIndexBuffer* indexBuffer) {
+    bool newIdxBuffer = fQuadIndexBuffer != indexBuffer;
+    if (newIdxBuffer) {
+        GrSafeUnref(fQuadIndexBuffer);
+        fQuadIndexBuffer = indexBuffer;
+        GrSafeRef(fQuadIndexBuffer);
+        fCurrQuad = 0;
+        fMaxQuads = (NULL == indexBuffer) ? 0 : indexBuffer->maxQuads();
+    } else {
+        GrAssert((NULL == indexBuffer && 0 == fMaxQuads) || 
+                 (indexBuffer->maxQuads() == fMaxQuads));
+    }
+}
+
+void GrInOrderDrawBuffer::drawRect(const GrRect& rect, 
+                                   const GrMatrix* matrix,
+                                   int stageEnableMask,
+                                   const GrRect* srcRects[],
+                                   const GrMatrix* srcMatrices[]) {
+    
+    GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad));
+    GrAssert(!(fDraws.empty() && fCurrQuad));
+    GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer));
+
+    // if we have a quad IB then either append to the previous run of
+    // rects or start a new run
+    if (fMaxQuads) {
+        
+        bool appendToPreviousDraw = false;
+        GrVertexLayout layout = GetRectVertexLayout(stageEnableMask, srcRects);
+        AutoReleaseGeometry geo(this, layout, 4, 0);
+        AutoViewMatrixRestore avmr(this);
+        GrMatrix combinedMatrix = this->getViewMatrix();
+        this->setViewMatrix(GrMatrix::I());
+        if (NULL != matrix) {
+            combinedMatrix.preConcat(*matrix);
+        }
+
+        SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices, layout, geo.vertices());
+
+        // we don't want to miss an opportunity to batch rects together
+        // simply because the clip has changed if the clip doesn't affect
+        // the rect.
+        bool disabledClip = false;
+        if (this->isClipState() && fClip.isRect()) {
+            GrRect clipRect = GrRect(*fClip.getRects());
+            // If the clip rect touches the edge of the viewport, extended it
+            // out (close) to infinity to avoid bogus intersections.
+            // We might consider a more exact clip to viewport if this 
+            // conservative test fails.
+            const GrRenderTarget* target = this->getRenderTarget();
+            if (0 >= clipRect.fLeft) {
+                clipRect.fLeft = GR_ScalarMin;
+            }
+            if (target->width() <= clipRect.fRight) {
+                clipRect.fRight = GR_ScalarMax;
+            }
+            if (0 >= clipRect.top()) {
+                clipRect.fTop = GR_ScalarMin;
+            }
+            if (target->height() <= clipRect.fBottom) {
+                clipRect.fBottom = GR_ScalarMax;
+            }
+            int stride = VertexSize(layout);
+            bool insideClip = true;
+            for (int v = 0; v < 4; ++v) {
+                const GrPoint& p = *GetVertexPoint(geo.vertices(), v, stride);
+                if (!clipRect.contains(p)) {
+                    insideClip = false;
+                    break;
+                }
+            }
+            if (insideClip) {
+                this->disableState(kClip_StateBit);
+                disabledClip = true;
+            }
+        }
+        if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 && 
+            fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) {
+
+            int vsize = VertexSize(layout);
+        
+            Draw& lastDraw = fDraws.back();
+
+            GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer);
+            GrAssert(kTriangles_PrimitiveType == lastDraw.fPrimitiveType);
+            GrAssert(0 == lastDraw.fVertexCount % 4);
+            GrAssert(0 == lastDraw.fIndexCount % 6);
+            GrAssert(0 == lastDraw.fStartIndex);
+
+            appendToPreviousDraw = lastDraw.fVertexBuffer == fCurrPoolVertexBuffer &&
+                                   (fCurrQuad * 4 + lastDraw.fStartVertex) == fCurrPoolStartVertex;
+            if (appendToPreviousDraw) {
+                lastDraw.fVertexCount += 4;
+                lastDraw.fIndexCount += 6;
+                fCurrQuad += 1;
+                GrAssert(0 == fUsedReservedVertexBytes);
+                fUsedReservedVertexBytes = 4 * vsize;
+            }
+        }
+        if (!appendToPreviousDraw) {
+            this->setIndexSourceToBuffer(fQuadIndexBuffer);
+            drawIndexed(kTriangles_PrimitiveType, 0, 0, 4, 6);
+            fCurrQuad = 1;
+            fLastRectVertexLayout = layout;
+        }
+        if (disabledClip) {
+            this->enableState(kClip_StateBit);
+        }
+this->enableState(kClip_StateBit);
+    } else {
+        INHERITED::drawRect(rect, matrix, stageEnableMask, srcRects, srcMatrices);
+    }
+}
+
 void GrInOrderDrawBuffer::drawIndexed(PrimitiveType primitiveType,
                                       int startVertex,
                                       int startIndex,
@@ -59,14 +183,24 @@
         return;
     }
 
+    fCurrQuad = 0;
+
     Draw& draw = fDraws.push_back();
     draw.fPrimitiveType = primitiveType;
     draw.fStartVertex   = startVertex;
     draw.fStartIndex    = startIndex;
     draw.fVertexCount   = vertexCount;
     draw.fIndexCount    = indexCount;
-    draw.fClipChanged   = grabClip();
-    draw.fStateChanged  = grabState();
+
+    draw.fClipChanged = this->needsNewClip();
+    if (draw.fClipChanged) {
+       this->pushClip();
+    }
+
+    draw.fStateChanged = this->needsNewState();
+    if (draw.fStateChanged) {
+        this->pushState();
+    }
 
     draw.fVertexLayout = fGeometrySrc.fVertexLayout;
     switch (fGeometrySrc.fVertexSrc) {
@@ -76,8 +210,7 @@
     case kReserved_GeometrySrcType: {
         size_t vertexBytes = (vertexCount + startVertex) *
         VertexSize(fGeometrySrc.fVertexLayout);
-        fUsedReservedVertexBytes = GrMax(fUsedReservedVertexBytes,
-                                         vertexBytes);
+        fUsedReservedVertexBytes = GrMax(fUsedReservedVertexBytes, vertexBytes);
     } // fallthrough
     case kArray_GeometrySrcType:
         draw.fVertexBuffer = fCurrPoolVertexBuffer;
@@ -86,6 +219,7 @@
     default:
         GrCrash("unknown geom src type");
     }
+    draw.fVertexBuffer->ref();
 
     switch (fGeometrySrc.fIndexSrc) {
     case kBuffer_GeometrySrcType:
@@ -102,6 +236,7 @@
     default:
         GrCrash("unknown geom src type");
     }
+    draw.fIndexBuffer->ref();
 }
 
 void GrInOrderDrawBuffer::drawNonIndexed(PrimitiveType primitiveType,
@@ -111,6 +246,8 @@
         return;
     }
 
+    fCurrQuad = 0;
+
     Draw& draw = fDraws.push_back();
     draw.fPrimitiveType = primitiveType;
     draw.fStartVertex   = startVertex;
@@ -118,8 +255,15 @@
     draw.fVertexCount   = vertexCount;
     draw.fIndexCount    = 0;
 
-    draw.fClipChanged   = grabClip();
-    draw.fStateChanged  = grabState();
+    draw.fClipChanged = this->needsNewClip();
+    if (draw.fClipChanged) {
+        this->pushClip();
+    }
+
+    draw.fStateChanged = this->needsNewState();
+    if (draw.fStateChanged) {
+        this->pushState();
+    }
 
     draw.fVertexLayout = fGeometrySrc.fVertexLayout;
     switch (fGeometrySrc.fVertexSrc) {
@@ -139,6 +283,8 @@
     default:
         GrCrash("unknown geom src type");
     }
+    draw.fVertexBuffer->ref();
+    draw.fIndexBuffer = NULL;
 }
 
 void GrInOrderDrawBuffer::reset() {
@@ -146,12 +292,19 @@
     uint32_t numStates = fStates.count();
     for (uint32_t i = 0; i < numStates; ++i) {
         for (int s = 0; s < kNumStages; ++s) {
-            GrTexture* tex = accessSavedDrawState(fStates[i]).fTextures[s];
+            GrTexture* tex = this->accessSavedDrawState(fStates[i]).fTextures[s];
             if (NULL != tex) {
                 tex->unref();
             }
         }
     }
+    int numDraws = fDraws.count();
+    for (int d = 0; d < numDraws; ++d) {
+        // we always have a VB, but not always an IB
+        GrAssert(NULL != fDraws[d].fVertexBuffer);
+        fDraws[d].fVertexBuffer->unref();
+        GrSafeUnref(fDraws[d].fIndexBuffer);
+    }
     fDraws.reset();
     fStates.reset();
 
@@ -159,6 +312,8 @@
     fIndexPool.reset();
 
     fClips.reset();
+
+    fCurrQuad = 0;
 }
 
 void GrInOrderDrawBuffer::playback(GrDrawTarget* target) {
@@ -331,37 +486,36 @@
     GR_DEBUGASSERT(success);
 }
 
-bool GrInOrderDrawBuffer::grabState() {
-    bool newState;
-    if (fStates.empty()) {
-        newState = true;
-    } else {
-        const DrState& old = accessSavedDrawState(fStates.back());
-        newState = old != fCurrDrawState;
-    }
-    if (newState) {
-        for (int s = 0; s < kNumStages; ++s) {
-            if (NULL != fCurrDrawState.fTextures[s]) {
-                fCurrDrawState.fTextures[s]->ref();
-            }
-        }
-        saveCurrentDrawState(&fStates.push_back());
-    }
-    return newState;
+bool GrInOrderDrawBuffer::needsNewState() const {
+     if (fStates.empty()) {
+        return true;
+     } else {
+         const DrState& old = this->accessSavedDrawState(fStates.back());
+        return old != fCurrDrawState;
+     }
 }
 
-bool GrInOrderDrawBuffer::grabClip() {
-    if ((fCurrDrawState.fFlagBits & kClip_StateBit) &&
-        (fClipChanged || fClips.empty())) {
-
-        fClips.push_back() = fClip;
-        fClipChanged = false;
-        return true;
+void GrInOrderDrawBuffer::pushState() {
+    for (int s = 0; s < kNumStages; ++s) {
+        GrSafeRef(fCurrDrawState.fTextures[s]);
+    }
+    this->saveCurrentDrawState(&fStates.push_back());
+ }
+ 
+bool GrInOrderDrawBuffer::needsNewClip() const {
+   if (fCurrDrawState.fFlagBits & kClip_StateBit) {
+       if (fClips.empty() || (fClipSet && fClips.back() != fClip)) {
+           return true;
+       }
     }
     return false;
 }
-
-void GrInOrderDrawBuffer::clipWillChange(const GrClip& clip)  {
-    fClipChanged = true;
+ 
+void GrInOrderDrawBuffer::pushClip() {
+    fClips.push_back() = fClip;
+    fClipSet = false;
 }
-
+ 
+void GrInOrderDrawBuffer::clipWillBeSet(const GrClip& newClip)  {
+    fClipSet = true;
+}