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;