Index: src/gpu/GrDefaultPathRenderer.cpp |
diff --git a/src/gpu/GrDefaultPathRenderer.cpp b/src/gpu/GrDefaultPathRenderer.cpp |
index 7b9817484f576ec908d1388314dc0aa48f5bd2e5..792b76c8f73ab1c6130012dd40c3bfc67a4fea86 100644 |
--- a/src/gpu/GrDefaultPathRenderer.cpp |
+++ b/src/gpu/GrDefaultPathRenderer.cpp |
@@ -7,6 +7,9 @@ |
#include "GrDefaultPathRenderer.h" |
+#include "GrBatch.h" |
+#include "GrBatchTarget.h" |
+#include "GrBufferAllocPool.h" |
#include "GrContext.h" |
#include "GrDefaultGeoProcFactory.h" |
#include "GrPathUtils.h" |
@@ -191,9 +194,9 @@ static inline void append_countour_edge_indices(bool hairLine, |
static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[], |
SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed, |
- bool isHairline, uint16_t subpathIdxStart, uint16_t** idx) { |
+ bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) { |
// first pt of quad is the pt we ended on in previous step |
- uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1; |
+ uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset; |
uint16_t numPts = (uint16_t) |
GrPathUtils::generateQuadraticPoints( |
pts[0], pts[1], pts[2], |
@@ -207,139 +210,316 @@ static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint p |
} |
} |
-bool GrDefaultPathRenderer::createGeom(GrDrawTarget* target, |
- GrPipelineBuilder* pipelineBuilder, |
- GrPrimitiveType* primType, |
- int* vertexCnt, |
- int* indexCnt, |
- GrDrawTarget::AutoReleaseGeometry* arg, |
- const SkPath& path, |
- const SkStrokeRec& stroke, |
- SkScalar srcSpaceTol) { |
- { |
- SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol); |
- int contourCnt; |
- int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt, |
- srcSpaceTol); |
- |
- if (maxPts <= 0) { |
- return false; |
+class DefaultPathBatch : public GrBatch { |
+public: |
+ struct Geometry { |
+ GrColor fColor; |
+ SkPath fPath; |
+ SkScalar fTolerance; |
+ SkDEBUGCODE(SkRect fDevBounds;) |
+ }; |
+ |
+ static GrBatch* Create(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix, |
+ bool isHairline) { |
+ return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline)); |
+ } |
+ |
+ const char* name() const SK_OVERRIDE { return "DefaultPathBatch"; } |
+ |
+ void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { |
+ // When this is called on a batch, there is only one geometry bundle |
+ out->setKnownFourComponents(fGeoData[0].fColor); |
} |
- if (maxPts > ((int)SK_MaxU16 + 1)) { |
- SkDebugf("Path not rendered, too many verts (%d)\n", maxPts); |
- return false; |
+ void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE { |
+ out->setUnknownSingleComponent(); |
} |
- bool indexed = contourCnt > 1; |
+ void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { |
+ // Handle any color overrides |
+ if (init.fColorIgnored) { |
+ fGeoData[0].fColor = GrColor_ILLEGAL; |
+ } else if (GrColor_ILLEGAL != init.fOverrideColor) { |
+ fGeoData[0].fColor = init.fOverrideColor; |
+ } |
- const bool isHairline = stroke.isHairlineStyle(); |
+ // setup batch properties |
+ fBatch.fColorIgnored = init.fColorIgnored; |
+ fBatch.fColor = fGeoData[0].fColor; |
+ fBatch.fUsesLocalCoords = init.fUsesLocalCoords; |
+ fBatch.fCoverageIgnored = init.fCoverageIgnored; |
+ } |
- int maxIdxs = 0; |
- if (isHairline) { |
- if (indexed) { |
- maxIdxs = 2 * maxPts; |
- *primType = kLines_GrPrimitiveType; |
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE { |
+ SkAutoTUnref<const GrGeometryProcessor> gp( |
+ GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, |
+ this->color(), |
+ this->viewMatrix(), |
+ SkMatrix::I(), |
+ false, |
+ this->coverage())); |
+ |
+ size_t vertexStride = gp->getVertexStride(); |
+ SkASSERT(vertexStride == sizeof(SkPoint)); |
+ |
+ batchTarget->initDraw(gp, pipeline); |
+ |
+ // TODO this is hacky, but the only way we have to initialize the GP is to use the |
+ // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch |
+ // everywhere we can remove this nastiness |
+ GrPipelineInfo init; |
+ init.fColorIgnored = fBatch.fColorIgnored; |
+ init.fOverrideColor = GrColor_ILLEGAL; |
+ init.fCoverageIgnored = fBatch.fCoverageIgnored; |
+ init.fUsesLocalCoords = this->usesLocalCoords(); |
+ gp->initBatchTracker(batchTarget->currentBatchTracker(), init); |
+ |
+ int instanceCount = fGeoData.count(); |
+ |
+ // compute number of vertices |
+ int maxVertices = 0; |
+ |
+ // We will use index buffers if we have multiple paths or one path with multiple contours |
+ bool isIndexed = instanceCount > 1; |
+ for (int i = 0; i < instanceCount; i++) { |
+ Geometry& args = fGeoData[i]; |
+ |
+ int contourCount; |
+ maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount, |
+ args.fTolerance); |
+ |
+ isIndexed = isIndexed || contourCount > 1; |
+ } |
+ |
+ // determine primitiveType |
+ int maxIndices = 0; |
+ GrPrimitiveType primitiveType; |
+ if (this->isHairline()) { |
+ if (isIndexed) { |
+ maxIndices = 2 * maxVertices; |
+ primitiveType = kLines_GrPrimitiveType; |
+ } else { |
+ primitiveType = kLineStrip_GrPrimitiveType; |
+ } |
} else { |
- *primType = kLineStrip_GrPrimitiveType; |
+ if (isIndexed) { |
+ maxIndices = 3 * maxVertices; |
+ primitiveType = kTriangles_GrPrimitiveType; |
+ } else { |
+ primitiveType = kTriangleFan_GrPrimitiveType; |
+ } |
} |
- } else { |
- if (indexed) { |
- maxIdxs = 3 * maxPts; |
- *primType = kTriangles_GrPrimitiveType; |
+ |
+ // allocate vertex / index buffers |
+ const GrVertexBuffer* vertexBuffer; |
+ int firstVertex; |
+ |
+ void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, |
+ maxVertices, |
+ &vertexBuffer, |
+ &firstVertex); |
+ |
+ const GrIndexBuffer* indexBuffer; |
+ int firstIndex; |
+ |
+ void* indices = NULL; |
+ if (isIndexed) { |
+ indices = batchTarget->indexPool()->makeSpace(maxIndices, |
+ &indexBuffer, |
+ &firstIndex); |
+ } |
+ |
+ // fill buffers |
+ int vertexOffset = 0; |
+ int indexOffset = 0; |
+ for (int i = 0; i < instanceCount; i++) { |
+ Geometry& args = fGeoData[i]; |
+ |
+ int vertexCnt = 0; |
+ int indexCnt = 0; |
+ if (!this->createGeom(vertices, |
+ vertexOffset, |
+ indices, |
+ indexOffset, |
+ &vertexCnt, |
+ &indexCnt, |
+ args.fPath, |
+ args.fTolerance, |
+ isIndexed)) { |
+ return; |
+ } |
+ |
+ vertexOffset += vertexCnt; |
+ indexOffset += indexCnt; |
+ SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices); |
+ } |
+ |
+ GrDrawTarget::DrawInfo drawInfo; |
+ drawInfo.setPrimitiveType(primitiveType); |
+ drawInfo.setVertexBuffer(vertexBuffer); |
+ drawInfo.setStartVertex(firstVertex); |
+ drawInfo.setVertexCount(vertexOffset); |
+ if (isIndexed) { |
+ drawInfo.setIndexBuffer(indexBuffer); |
+ drawInfo.setStartIndex(firstIndex); |
+ drawInfo.setIndexCount(indexOffset); |
} else { |
- *primType = kTriangleFan_GrPrimitiveType; |
+ drawInfo.setStartIndex(0); |
+ drawInfo.setIndexCount(0); |
} |
+ batchTarget->draw(drawInfo); |
} |
- if (!arg->set(target, maxPts, GrDefaultGeoProcFactory::DefaultVertexStride(), maxIdxs)) { |
- return false; |
+ SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } |
+ |
+private: |
+ DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix, |
+ bool isHairline) { |
+ this->initClassID<DefaultPathBatch>(); |
+ fBatch.fCoverage = coverage; |
+ fBatch.fIsHairline = isHairline; |
+ fBatch.fViewMatrix = viewMatrix; |
+ fGeoData.push_back(geometry); |
} |
- SkASSERT(GrDefaultGeoProcFactory::DefaultVertexStride() == sizeof(SkPoint)); |
- uint16_t* idxBase = reinterpret_cast<uint16_t*>(arg->indices()); |
- uint16_t* idx = idxBase; |
- uint16_t subpathIdxStart = 0; |
+ bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { |
+ DefaultPathBatch* that = t->cast<DefaultPathBatch>(); |
- SkPoint* base = reinterpret_cast<SkPoint*>(arg->vertices()); |
- SkASSERT(base); |
- SkPoint* vert = base; |
+ if (this->color() != that->color()) { |
+ return false; |
+ } |
- SkPoint pts[4]; |
+ if (this->coverage() != that->coverage()) { |
+ return false; |
+ } |
- bool first = true; |
- int subpath = 0; |
+ if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { |
+ return false; |
+ } |
- SkPath::Iter iter(path, false); |
+ if (this->isHairline() != that->isHairline()) { |
+ return false; |
+ } |
- for (;;) { |
- SkPath::Verb verb = iter.next(pts); |
- switch (verb) { |
- case SkPath::kMove_Verb: |
- if (!first) { |
- uint16_t currIdx = (uint16_t) (vert - base); |
- subpathIdxStart = currIdx; |
- ++subpath; |
- } |
- *vert = pts[0]; |
- vert++; |
- break; |
- case SkPath::kLine_Verb: |
- if (indexed) { |
- uint16_t prevIdx = (uint16_t)(vert - base) - 1; |
- append_countour_edge_indices(isHairline, subpathIdxStart, |
- prevIdx, &idx); |
- } |
- *(vert++) = pts[1]; |
- break; |
- case SkPath::kConic_Verb: { |
- SkScalar weight = iter.conicWeight(); |
- SkAutoConicToQuads converter; |
- // Converting in src-space, hance the finer tolerance (0.25) |
- // TODO: find a way to do this in dev-space so the tolerance means something |
- const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f); |
- for (int i = 0; i < converter.countQuads(); ++i) { |
- add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol, indexed, |
- isHairline, subpathIdxStart, &idx); |
- } |
- break; |
- } |
- case SkPath::kQuad_Verb: |
- add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, indexed, |
- isHairline, subpathIdxStart, &idx); |
- break; |
- case SkPath::kCubic_Verb: { |
- // first pt of cubic is the pt we ended on in previous step |
- uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1; |
- uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( |
- pts[0], pts[1], pts[2], pts[3], |
- srcSpaceTolSqd, &vert, |
- GrPathUtils::cubicPointCount(pts, srcSpaceTol)); |
- if (indexed) { |
- for (uint16_t i = 0; i < numPts; ++i) { |
- append_countour_edge_indices(isHairline, subpathIdxStart, |
- firstCPtIdx + i, &idx); |
+ fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); |
+ return true; |
+ } |
+ |
+ bool createGeom(void* vertices, |
+ size_t vertexOffset, |
+ void* indices, |
+ size_t indexOffset, |
+ int* vertexCnt, |
+ int* indexCnt, |
+ const SkPath& path, |
+ SkScalar srcSpaceTol, |
+ bool isIndexed) { |
+ { |
+ SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol); |
+ |
+ uint16_t indexOffsetU16 = (uint16_t)indexOffset; |
+ uint16_t vertexOffsetU16 = (uint16_t)vertexOffset; |
+ |
+ uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16; |
+ uint16_t* idx = idxBase; |
+ uint16_t subpathIdxStart = vertexOffsetU16; |
+ |
+ SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset; |
+ SkPoint* vert = base; |
+ |
+ SkPoint pts[4]; |
+ |
+ bool first = true; |
+ int subpath = 0; |
+ |
+ SkPath::Iter iter(path, false); |
+ |
+ bool done = false; |
+ while (!done) { |
+ SkPath::Verb verb = iter.next(pts); |
+ switch (verb) { |
+ case SkPath::kMove_Verb: |
+ if (!first) { |
+ uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16; |
+ subpathIdxStart = currIdx; |
+ ++subpath; |
+ } |
+ *vert = pts[0]; |
+ vert++; |
+ break; |
+ case SkPath::kLine_Verb: |
+ if (isIndexed) { |
+ uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16; |
+ append_countour_edge_indices(this->isHairline(), subpathIdxStart, |
+ prevIdx, &idx); |
+ } |
+ *(vert++) = pts[1]; |
+ break; |
+ case SkPath::kConic_Verb: { |
+ SkScalar weight = iter.conicWeight(); |
+ SkAutoConicToQuads converter; |
+ // Converting in src-space, hance the finer tolerance (0.25) |
+ // TODO: find a way to do this in dev-space so the tolerance means something |
+ const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.25f); |
+ for (int i = 0; i < converter.countQuads(); ++i) { |
+ add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol, |
+ isIndexed, this->isHairline(), subpathIdxStart, |
+ (int)vertexOffset, &idx); |
+ } |
+ break; |
+ } |
+ case SkPath::kQuad_Verb: |
+ add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed, |
+ this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx); |
+ break; |
+ case SkPath::kCubic_Verb: { |
+ // first pt of cubic is the pt we ended on in previous step |
+ uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16; |
+ uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( |
+ pts[0], pts[1], pts[2], pts[3], |
+ srcSpaceTolSqd, &vert, |
+ GrPathUtils::cubicPointCount(pts, srcSpaceTol)); |
+ if (isIndexed) { |
+ for (uint16_t i = 0; i < numPts; ++i) { |
+ append_countour_edge_indices(this->isHairline(), subpathIdxStart, |
+ firstCPtIdx + i, &idx); |
+ } |
+ } |
+ break; |
} |
+ case SkPath::kClose_Verb: |
+ break; |
+ case SkPath::kDone_Verb: |
+ done = true; |
} |
- break; |
+ first = false; |
} |
- case SkPath::kClose_Verb: |
- break; |
- case SkPath::kDone_Verb: |
- // uint16_t currIdx = (uint16_t) (vert - base); |
- goto FINISHED; |
- } |
- first = false; |
- } |
-FINISHED: |
- SkASSERT((vert - base) <= maxPts); |
- SkASSERT((idx - idxBase) <= maxIdxs); |
- *vertexCnt = static_cast<int>(vert - base); |
- *indexCnt = static_cast<int>(idx - idxBase); |
+ *vertexCnt = static_cast<int>(vert - base); |
+ *indexCnt = static_cast<int>(idx - idxBase); |
+ } |
+ return true; |
} |
- return true; |
-} |
+ |
+ GrColor color() const { return fBatch.fColor; } |
+ uint8_t coverage() const { return fBatch.fCoverage; } |
+ bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } |
+ const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } |
+ bool isHairline() const { return fBatch.fIsHairline; } |
+ |
+ struct BatchTracker { |
+ GrColor fColor; |
+ uint8_t fCoverage; |
+ SkMatrix fViewMatrix; |
+ bool fUsesLocalCoords; |
+ bool fColorIgnored; |
+ bool fCoverageIgnored; |
+ bool fIsHairline; |
+ }; |
+ |
+ BatchTracker fBatch; |
+ SkSTArray<1, Geometry, true> fGeoData; |
+}; |
bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, |
GrPipelineBuilder* pipelineBuilder, |
@@ -360,24 +540,8 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, |
} |
} |
- SkScalar tol = SK_Scalar1; |
- tol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds()); |
- |
- int vertexCnt; |
- int indexCnt; |
- GrPrimitiveType primType; |
- GrDrawTarget::AutoReleaseGeometry arg; |
- if (!this->createGeom(target, |
- pipelineBuilder, |
- &primType, |
- &vertexCnt, |
- &indexCnt, |
- &arg, |
- path, |
- *stroke, |
- tol)) { |
- return false; |
- } |
+ const bool isHairline = stroke->isHairlineStyle(); |
+ |
// Save the current xp on the draw state so we can reset it if needed |
SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory())); |
// face culling doesn't make sense here |
@@ -385,11 +549,11 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, |
int passCount = 0; |
const GrStencilSettings* passes[3]; |
- GrPipelineBuilder::DrawFace drawFace[3]; |
+ GrPipelineBuilder::DrawFace drawFace[3]; |
bool reverse = false; |
bool lastPassIsBounds; |
- if (stroke->isHairlineStyle()) { |
+ if (isHairline) { |
passCount = 1; |
if (stencilOnly) { |
passes[0] = &gDirectToStencil; |
@@ -475,6 +639,9 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, |
} |
} |
+ SkScalar tol = SK_Scalar1; |
+ SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds()); |
+ |
SkRect devBounds; |
GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds); |
@@ -513,26 +680,17 @@ bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, |
if (passCount > 1) { |
pipelineBuilder->setDisableColorXPFactory(); |
} |
- GrPipelineBuilder::AutoRestoreEffects are(pipelineBuilder); |
- SkAutoTUnref<const GrGeometryProcessor> gp( |
- GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, |
- color, |
- viewMatrix, |
- SkMatrix::I(), |
- false, |
- newCoverage)); |
- if (indexCnt) { |
- target->drawIndexed(pipelineBuilder, |
- gp, |
- primType, |
- 0, |
- 0, |
- vertexCnt, |
- indexCnt, |
- &devBounds); |
- } else { |
- target->drawNonIndexed(pipelineBuilder, gp, primType, 0, vertexCnt, &devBounds); |
- } |
+ |
+ DefaultPathBatch::Geometry geometry; |
+ geometry.fColor = color; |
+ geometry.fPath = path; |
+ geometry.fTolerance = srcSpaceTol; |
+ SkDEBUGCODE(geometry.fDevBounds = devBounds;) |
+ |
+ SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix, |
+ isHairline)); |
+ |
+ target->drawBatch(pipelineBuilder, batch, &devBounds); |
} |
} |
return true; |