Chromium Code Reviews| Index: src/gpu/GrDefaultPathRenderer.cpp |
| diff --git a/src/gpu/GrDefaultPathRenderer.cpp b/src/gpu/GrDefaultPathRenderer.cpp |
| index 7b9817484f576ec908d1388314dc0aa48f5bd2e5..fc0d7b759f89c908e9563532bfb22ecbcb604174 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,313 @@ 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; |
| + } |
| + |
| + if (this->coverage() != that->coverage()) { |
| + return false; |
| + } |
| - SkPoint pts[4]; |
| + if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { |
| + return false; |
| + } |
| - bool first = true; |
| - int subpath = 0; |
| + if (this->isHairline() != that->isHairline()) { |
| + return false; |
| + } |
| - SkPath::Iter iter(path, false); |
| + fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); |
| + return true; |
| + } |
| - 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); |
| + 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* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffset; |
| + uint16_t* idx = idxBase; |
| + uint16_t subpathIdxStart = vertexOffset; |
| + |
| + 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) + vertexOffset; |
| + subpathIdxStart = currIdx; |
| + ++subpath; |
| + } |
| + *vert = pts[0]; |
| + vert++; |
| + break; |
| + case SkPath::kLine_Verb: |
| + if (isIndexed) { |
| + uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffset; |
| + 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, vertexOffset, &idx); |
|
egdaniel
2015/02/18 15:16:57
100 chars
|
| + } |
| + break; |
| + } |
| + case SkPath::kQuad_Verb: |
| + add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed, |
| + this->isHairline(), subpathIdxStart, 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 + vertexOffset; |
| + 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: |
| + // uint16_t currIdx = (uint16_t) (vert - base); |
|
egdaniel
2015/02/18 15:16:57
is the commented line needed for any reason
|
| + 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); |
|
egdaniel
2015/02/18 15:16:57
was there a reason to remove the asserts in that w
joshualitt
2015/02/20 14:31:46
I put an assert at the end of the whole thing(line
|
| - *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 +537,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 +546,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 +636,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 +677,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; |