Add GrDrawTarget::drawIndexedInstance, use in default text context.

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



git-svn-id: http://skia.googlecode.com/svn/trunk@3444 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrDefaultTextContext.cpp b/src/gpu/GrDefaultTextContext.cpp
index a0747b7..d2c5d92 100644
--- a/src/gpu/GrDefaultTextContext.cpp
+++ b/src/gpu/GrDefaultTextContext.cpp
@@ -33,7 +33,6 @@
             GrSamplerState::kRepeat_WrapMode,filter);
 
         GrAssert(GrIsALIGN4(fCurrVertex));
-        int nIndices = fCurrVertex + (fCurrVertex >> 1);
         GrAssert(fCurrTexture);
         drawState->setTexture(kGlyphMaskStage, fCurrTexture);
 
@@ -56,9 +55,10 @@
         }
 
         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
-
-        fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
-                                 0, 0, fCurrVertex, nIndices);
+        int nGlyphs = fCurrVertex / 4;
+        fDrawTarget->drawIndexedInstances(kTriangles_PrimitiveType,
+                                          nGlyphs,
+                                          4, 6);
         fVertices = NULL;
         this->INHERITED::reset();
     }
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index e9b7e49..6d52c2a 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -1011,6 +1011,34 @@
                     this->getBlendOpts());
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawTarget::drawIndexedInstances(GrPrimitiveType type,
+                                        int instanceCount,
+                                        int verticesPerInstance,
+                                        int indicesPerInstance) {
+    if (!verticesPerInstance || !indicesPerInstance) {
+        return;
+    }
+
+    int instancesPerDraw = this->indexCountInCurrentSource() /
+                           indicesPerInstance;
+    if (!instancesPerDraw) {
+        return;
+    }
+
+    instancesPerDraw = GrMin(instanceCount, instancesPerDraw);
+    int startVertex = 0;
+    while (instanceCount) {
+        this->drawIndexed(type,
+                          startVertex,
+                          0,
+                          verticesPerInstance * instancesPerDraw,
+                          indicesPerInstance * instancesPerDraw);
+        startVertex += verticesPerInstance;
+        instanceCount -= instancesPerDraw;
+    }
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 629963b..3a85767 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -14,6 +14,7 @@
 #include "GrClip.h"
 #include "GrColor.h"
 #include "GrDrawState.h"
+#include "GrIndexBuffer.h"
 #include "GrMatrix.h"
 #include "GrRefCnt.h"
 #include "GrSamplerState.h"
@@ -263,9 +264,8 @@
      * There are three types of "sources" of geometry (vertices and indices) for
      * draw calls made on the target. When performing an indexed draw, the
      * indices and vertices can use different source types. Once a source is
-     * specified it can be used for multiple drawIndexed and drawNonIndexed
-     * calls. However, the time at which the geometry data is no longer editable
-     * depends on the source type.
+     * specified it can be used for multiple draws. However, the time at which
+     * the geometry data is no longer editable depends on the source type.
      *
      * Sometimes it is necessary to perform a draw while upstack code has
      * already specified geometry that it isn't finished with. So there are push
@@ -291,8 +291,8 @@
      *    data. The target provides ptrs to hold the vertex and/or index data.
      *
      *    The data is writable up until the next drawIndexed, drawNonIndexed, 
-     *    or pushGeometrySource. At this point the data is frozen and the ptrs
-     *    are no longer valid.
+     *    drawIndexedInstances, or pushGeometrySource. At this point the data is
+     *    frozen and the ptrs are no longer valid.
      *
      *    Where the space is allocated and how it is uploaded to the GPU is
      *    subclass-dependent.
@@ -319,9 +319,9 @@
      * source is reset and likewise for indexCount.
      *
      * The pointers to the space allocated for vertices and indices remain valid
-     * until a drawIndexed, drawNonIndexed, or push/popGeomtrySource is called.
-     * At that point logically a snapshot of the data is made and the pointers
-     * are invalid.
+     * until a drawIndexed, drawNonIndexed, drawIndexedInstances, or push/
+     * popGeomtrySource is called. At that point logically a snapshot of the
+     * data is made and the pointers are invalid.
      *
      * @param vertexLayout the format of vertices (ignored if vertexCount == 0).
      * @param vertexCount  the number of vertices to reserve space for. Can be
@@ -387,7 +387,7 @@
 
     /**
      * Sets source of vertex data for the next draw. Data does not have to be
-     * in the buffer until drawIndexed or drawNonIndexed.
+     * in the buffer until drawIndexed, drawNonIndexed, or drawIndexedInstances.
      *
      * @param buffer        vertex buffer containing vertex data. Must be
      *                      unlocked before draw call.
@@ -398,7 +398,7 @@
 
     /**
      * Sets source of index data for the next indexed draw. Data does not have
-     * to be in the buffer until drawIndexed or drawNonIndexed.
+     * to be in the buffer until drawIndexed.
      *
      * @param buffer index buffer containing indices. Must be unlocked
      *               before indexed draw call.
@@ -503,6 +503,39 @@
                           const GrMatrix* srcMatrices[]);
 
     /**
+     * This call is used to draw multiple instances of some geometry with a
+     * given number of vertices (V) and indices (I) per-instance. The indices in
+     * the index source must have the form i[k+I] == i[k] + V. Also, all indices
+     * i[kI] ... i[(k+1)I-1] must be elements of the range kV ... (k+1)V-1. As a
+     * concrete example, the following index buffer for drawing a series of
+     * quads each as two triangles each satisfies these conditions with V=4 and
+     * I=6:
+     *      (0,1,2,0,2,3, 4,5,6,4,6,7, 8,9,10,8,10,11, ...)
+     *
+     * The call assumes that the pattern of indices fills the entire index
+     * source. The size of the index buffer limits the number of instances that
+     * can be drawn by the GPU in a single draw. However, the caller may specify
+     * any (positive) number for instanceCount and if necessary multiple GPU
+     * draws will be issued. Morever, when drawIndexedInstances is called
+     * multiple times it may be possible for GrDrawTarget to group them into a
+     * single GPU draw.
+     *
+     * @param type          the type of primitives to draw
+     * @param instanceCount the number of instances to draw. Each instance
+     *                      consists of verticesPerInstance vertices indexed by
+     *                      indicesPerInstance indices drawn as the primitive
+     *                      type specified by type.
+     * @param verticesPerInstance   The number of vertices in each instance (V
+     *                              in the above description).
+     * @param indicesPerInstance    The number of indices in each instance (I
+     *                              in the above description).
+     */
+    virtual void drawIndexedInstances(GrPrimitiveType type,
+                                      int instanceCount,
+                                      int verticesPerInstance,
+                                      int indicesPerInstance);
+
+    /**
      * Helper for drawRect when the caller doesn't need separate src rects or
      * matrices.
      */
@@ -904,7 +937,22 @@
         
         GrVertexLayout          fVertexLayout;
     };
-    
+
+    int indexCountInCurrentSource() const {
+        const GeometrySrcState& src = this->getGeomSrc();
+        switch (src.fIndexSrc) {
+            case kNone_GeometrySrcType:
+                return 0;
+            case kReserved_GeometrySrcType:
+            case kArray_GeometrySrcType:
+                return src.fIndexCount;
+            case kBuffer_GeometrySrcType:
+                return src.fIndexBuffer->sizeInBytes() / sizeof(uint16_t);
+            default:
+                GrCrash("Unexpected Index Source.");
+                return 0;
+        }
+    }
     // given a vertex layout and a draw state, will a stage be used?
     static bool StageWillBeUsed(int stage, GrVertexLayout layout, 
                                 const GrDrawState& state) {
diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp
index b6581c0..cab4bdd 100644
--- a/src/gpu/GrInOrderDrawBuffer.cpp
+++ b/src/gpu/GrInOrderDrawBuffer.cpp
@@ -42,6 +42,7 @@
     poolState.fPoolIndexBuffer = (GrIndexBuffer*)~0;
     poolState.fPoolStartIndex = ~0;
 #endif
+    fInstancedDrawTracker.reset();
 }
 
 GrInOrderDrawBuffer::~GrInOrderDrawBuffer() {
@@ -71,6 +72,13 @@
     }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+void GrInOrderDrawBuffer::resetDrawTracking() {
+    fCurrQuad = 0;
+    fInstancedDrawTracker.reset();
+}
+
 void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
                                    const GrMatrix* matrix,
                                    StageMask stageMask,
@@ -188,11 +196,135 @@
         if (disabledClip) {
             drawState->enableState(GrDrawState::kClip_StateBit);
         }
+        fInstancedDrawTracker.reset();
     } else {
         INHERITED::drawRect(rect, matrix, stageMask, srcRects, srcMatrices);
     }
 }
 
+void GrInOrderDrawBuffer::drawIndexedInstances(GrPrimitiveType type,
+                                               int instanceCount,
+                                               int verticesPerInstance,
+                                               int indicesPerInstance) {
+    if (!verticesPerInstance || !indicesPerInstance) {
+        return;
+    }
+
+    const GeometrySrcState& geomSrc = this->getGeomSrc();
+
+    // we only attempt to concat the case when reserved verts are used with
+    // an index buffer.
+    if (kReserved_GeometrySrcType == geomSrc.fVertexSrc &&
+        kBuffer_GeometrySrcType == geomSrc.fIndexSrc) {
+
+        Draw* draw = NULL;
+        // if the last draw used the same indices/vertices per shape then we
+        // may be able to append to it.
+        if (verticesPerInstance == fInstancedDrawTracker.fVerticesPerInstance &&
+            indicesPerInstance == fInstancedDrawTracker.fIndicesPerInstance) {
+            GrAssert(fDraws.count());
+            draw = &fDraws.back();
+        }
+
+        bool clipChanged = this->needsNewClip();
+        bool stateChanged = this->needsNewState();
+        if (clipChanged) {
+            this->pushClip();
+        }
+        if (stateChanged) {
+            this->pushState();
+        }
+
+        GeometryPoolState& poolState = fGeoPoolStateStack.back();
+        const GrVertexBuffer* vertexBuffer = poolState.fPoolVertexBuffer;
+
+        // Check whether the draw is compatible with this draw in order to
+        // append
+        if (NULL == draw ||
+            clipChanged ||
+            stateChanged ||
+            draw->fIndexBuffer != geomSrc.fIndexBuffer ||
+            draw->fPrimitiveType != type ||
+            draw->fVertexBuffer != vertexBuffer) {
+
+            draw = &fDraws.push_back();
+            draw->fClipChanged = clipChanged;
+            draw->fStateChanged = stateChanged;
+            draw->fIndexBuffer = geomSrc.fIndexBuffer;
+            geomSrc.fIndexBuffer->ref();
+            draw->fVertexBuffer = vertexBuffer;
+            vertexBuffer->ref();
+            draw->fPrimitiveType = type;
+            draw->fStartIndex = 0;
+            draw->fIndexCount = 0;
+            draw->fStartVertex = poolState.fPoolStartVertex;
+            draw->fVertexCount = 0;
+            draw->fVertexLayout = geomSrc.fVertexLayout;
+        } else {
+            GrAssert(!(draw->fIndexCount % indicesPerInstance));
+            GrAssert(!(draw->fVertexCount % verticesPerInstance));
+            GrAssert(poolState.fPoolStartVertex == draw->fStartVertex +
+                                                   draw->fVertexCount);
+        }
+
+        // how many instances can be in a single draw
+        int maxInstancesPerDraw = this->indexCountInCurrentSource() /
+                                  indicesPerInstance;
+        if (!maxInstancesPerDraw) {
+            return;
+        }
+        // how many instances should be concat'ed onto draw
+        int instancesToConcat = maxInstancesPerDraw - draw->fVertexCount /
+                                                      verticesPerInstance;
+        if (maxInstancesPerDraw > instanceCount) {
+            maxInstancesPerDraw = instanceCount;
+            if (instancesToConcat > instanceCount) {
+                instancesToConcat = instanceCount;
+            }
+        }
+
+        // update the amount of reserved data actually referenced in draws
+        size_t vertexBytes = instanceCount * verticesPerInstance *
+                             VertexSize(draw->fVertexLayout);
+        poolState.fUsedPoolVertexBytes =
+                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
+
+        while (instanceCount) {
+            if (!instancesToConcat) {
+                int startVertex = draw->fStartVertex + draw->fVertexCount;
+                draw = &fDraws.push_back();
+                draw->fClipChanged = false;
+                draw->fStateChanged = false;
+                draw->fIndexBuffer = geomSrc.fIndexBuffer;
+                geomSrc.fIndexBuffer->ref();
+                draw->fVertexBuffer = vertexBuffer;
+                vertexBuffer->ref();
+                draw->fPrimitiveType = type;
+                draw->fStartIndex = 0;
+                draw->fStartVertex = startVertex;
+                draw->fVertexCount = 0;
+                draw->fVertexLayout = geomSrc.fVertexLayout;
+                instancesToConcat = maxInstancesPerDraw;
+            }
+            draw->fVertexCount += instancesToConcat * verticesPerInstance;
+            draw->fIndexCount += instancesToConcat * indicesPerInstance;
+            instanceCount -= instancesToConcat;
+            instancesToConcat = 0;
+        }
+
+        // update draw tracking for next draw
+        fCurrQuad = 0;
+        fInstancedDrawTracker.fVerticesPerInstance = verticesPerInstance;
+        fInstancedDrawTracker.fIndicesPerInstance = indicesPerInstance;
+    } else {
+        this->INHERITED::drawIndexedInstances(type,
+                                              instanceCount,
+                                              verticesPerInstance,
+                                              indicesPerInstance);
+    }
+
+}
+
 void GrInOrderDrawBuffer::onDrawIndexed(GrPrimitiveType primitiveType,
                                         int startVertex,
                                         int startIndex,
@@ -203,7 +335,7 @@
         return;
     }
 
-    fCurrQuad = 0;
+    this->resetDrawTracking();
 
     GeometryPoolState& poolState = fGeoPoolStateStack.back();
 
@@ -270,7 +402,7 @@
         return;
     }
 
-    fCurrQuad = 0;
+    this->resetDrawTracking();
 
     GeometryPoolState& poolState = fGeoPoolStateStack.back();
 
@@ -359,7 +491,7 @@
 
     fClips.reset();
 
-    fCurrQuad = 0;
+    this->resetDrawTracking();
 }
 
 void GrInOrderDrawBuffer::playback(GrDrawTarget* target) {
@@ -608,6 +740,7 @@
     GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
     poolState.fUsedPoolVertexBytes = 0;
     poolState.fUsedPoolIndexBytes = 0;
+    this->resetDrawTracking();
 #if GR_DEBUG
     poolState.fPoolVertexBuffer = (GrVertexBuffer*)~0;
     poolState.fPoolStartVertex = ~0;
@@ -635,6 +768,7 @@
         poolState.fUsedPoolIndexBytes = sizeof(uint16_t) * 
                                          restoredState.fIndexCount;
     }
+    this->resetDrawTracking();
 }
 
 bool GrInOrderDrawBuffer::needsNewState() const {
diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h
index b802f96..a0880d2 100644
--- a/src/gpu/GrInOrderDrawBuffer.h
+++ b/src/gpu/GrInOrderDrawBuffer.h
@@ -109,6 +109,12 @@
                           const GrRect* srcRects[] = NULL,
                           const GrMatrix* srcMatrices[] = NULL) SK_OVERRIDE;
 
+    virtual void drawIndexedInstances(GrPrimitiveType type,
+                                      int instanceCount,
+                                      int verticesPerInstance,
+                                      int indicesPerInstance)
+                                      SK_OVERRIDE;
+
     virtual bool geometryHints(GrVertexLayout vertexLayout,
                                int* vertexCount,
                                int* indexCount) const SK_OVERRIDE;
@@ -116,13 +122,10 @@
     virtual void clear(const GrIRect* rect, GrColor color) SK_OVERRIDE;
 
 protected:
-
     virtual void willReserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
                                                 int vertexCount,
                                                 int indexCount) SK_OVERRIDE;
-    
 private:
-
     struct Draw {
         GrPrimitiveType         fPrimitiveType;
         int                     fStartVertex;
@@ -172,7 +175,11 @@
 
     void pushState();
     void pushClip();
-    
+
+    // call this to invalidate the tracking data that is used to concatenate 
+    // multiple draws into a single draw.
+    void resetDrawTracking();
+
     enum {
         kDrawPreallocCnt         = 8,
         kStatePreallocCnt        = 8,
@@ -190,14 +197,25 @@
 
     bool                            fClipSet;
 
+    GrVertexBufferAllocPool&        fVertexPool;
+
+    GrIndexBufferAllocPool&         fIndexPool;
+
+    // these are used to attempt to concatenate drawRect calls
     GrVertexLayout                  fLastRectVertexLayout;
     const GrIndexBuffer*            fQuadIndexBuffer;
     int                             fMaxQuads;
     int                             fCurrQuad;
 
-    GrVertexBufferAllocPool&        fVertexPool;
-
-    GrIndexBufferAllocPool&         fIndexPool;
+    // bookkeeping to attempt to concantenate drawIndexedInstances calls
+    struct {
+        int            fVerticesPerInstance;
+        int            fIndicesPerInstance;
+        void reset() {
+            fVerticesPerInstance = 0;
+            fIndicesPerInstance = 0;
+        }
+    } fInstancedDrawTracker;
 
     struct GeometryPoolState {
         const GrVertexBuffer*           fPoolVertexBuffer;