#include "GrDefaultPathRenderer.h"
+#include "GrBatch.h"
+#include "GrBatchTarget.h"
+#include "GrBufferAllocPool.h"
#include "GrContext.h"
#include "GrDefaultGeoProcFactory.h"
#include "GrPathUtils.h"
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],
}
}
-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,
}
}
- 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
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;
}
}
+ SkScalar tol = SK_Scalar1;
+ SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
+
SkRect devBounds;
GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds);
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;