| 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;
|
|
|