aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu')
-rw-r--r--src/gpu/GrPathUtils.cpp13
-rw-r--r--src/gpu/GrPathUtils.h3
-rw-r--r--src/gpu/ops/GrDefaultPathRenderer.cpp475
3 files changed, 278 insertions, 213 deletions
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
index 91a48f7b6b..918493982a 100644
--- a/src/gpu/GrPathUtils.cpp
+++ b/src/gpu/GrPathUtils.cpp
@@ -11,7 +11,6 @@
#include "SkGeometry.h"
#include "SkMathPriv.h"
-static const int MAX_POINTS_PER_CURVE = 1 << 10;
static const SkScalar gMinCurveTol = 0.0001f;
SkScalar GrPathUtils::scaleToleranceToSrc(SkScalar devTol,
@@ -45,7 +44,7 @@ uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol)
SkScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
if (!SkScalarIsFinite(d)) {
- return MAX_POINTS_PER_CURVE;
+ return kMaxPointsPerCurve;
} else if (d <= tol) {
return 1;
} else {
@@ -55,7 +54,7 @@ uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol)
// 2^(log4(x)) = sqrt(x);
SkScalar divSqrt = SkScalarSqrt(d / tol);
if (((SkScalar)SK_MaxS32) <= divSqrt) {
- return MAX_POINTS_PER_CURVE;
+ return kMaxPointsPerCurve;
} else {
int temp = SkScalarCeilToInt(divSqrt);
int pow2 = GrNextPow2(temp);
@@ -65,7 +64,7 @@ uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol)
if (pow2 < 1) {
pow2 = 1;
}
- return SkTMin(pow2, MAX_POINTS_PER_CURVE);
+ return SkTMin(pow2, kMaxPointsPerCurve);
}
}
}
@@ -105,13 +104,13 @@ uint32_t GrPathUtils::cubicPointCount(const SkPoint points[],
points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
d = SkScalarSqrt(d);
if (!SkScalarIsFinite(d)) {
- return MAX_POINTS_PER_CURVE;
+ return kMaxPointsPerCurve;
} else if (d <= tol) {
return 1;
} else {
SkScalar divSqrt = SkScalarSqrt(d / tol);
if (((SkScalar)SK_MaxS32) <= divSqrt) {
- return MAX_POINTS_PER_CURVE;
+ return kMaxPointsPerCurve;
} else {
int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol));
int pow2 = GrNextPow2(temp);
@@ -121,7 +120,7 @@ uint32_t GrPathUtils::cubicPointCount(const SkPoint points[],
if (pow2 < 1) {
pow2 = 1;
}
- return SkTMin(pow2, MAX_POINTS_PER_CURVE);
+ return SkTMin(pow2, kMaxPointsPerCurve);
}
}
}
diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h
index fdfd375427..421fca129b 100644
--- a/src/gpu/GrPathUtils.h
+++ b/src/gpu/GrPathUtils.h
@@ -165,5 +165,8 @@ namespace GrPathUtils {
// This value was chosen to approximate the supersampling accuracy of the raster path (16
// samples, or one quarter pixel).
static const SkScalar kDefaultTolerance = SkDoubleToScalar(0.25);
+
+ // We guarantee that no quad or cubic will ever produce more than this many points
+ static const int kMaxPointsPerCurve = 1 << 10;
};
#endif
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index c282036971..22e24e6544 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -59,37 +59,269 @@ GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
}
}
-static inline void append_countour_edge_indices(bool hairLine,
- uint16_t fanCenterIdx,
- uint16_t edgeV0Idx,
- uint16_t** indices) {
- // when drawing lines we're appending line segments along
- // the contour. When applying the other fill rules we're
- // drawing triangle fans around fanCenterIdx.
- if (!hairLine) {
- *((*indices)++) = fanCenterIdx;
- }
- *((*indices)++) = edgeV0Idx;
- *((*indices)++) = edgeV0Idx + 1;
-}
+// Needs to be large enough to handle quads/cubics, which have a worst-case of 1k points
+static const int kVerticesPerChunk = 16384;
+
+class PathGeoBuilder {
+public:
+ PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
+ GrGeometryProcessor* geometryProcessor, const GrPipeline* pipeline)
+ : fMesh(primitiveType)
+ , fTarget(target)
+ , fVertexStride(sizeof(SkPoint))
+ , fGeometryProcessor(geometryProcessor)
+ , fPipeline(pipeline)
+ , fIndexBuffer(nullptr)
+ , fFirstIndex(0)
+ , fIndices(nullptr) {
+ this->allocNewBuffers();
+ }
+
+ ~PathGeoBuilder() {
+ this->emitMesh();
+ this->putBackReserve();
+ }
+
+ // Called before we start each path
+ void beginInstance() {
+ fSubpathIndexStart = fVertexOffset;
+ fCurIdx = fIndices + fIndexOffset;
+ fCurVert = fVertices + fVertexOffset;
+ }
-static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
- SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
- 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 + offset;
- uint16_t numPts = (uint16_t)
- GrPathUtils::generateQuadraticPoints(
- pts[0], pts[1], pts[2],
- srcSpaceTolSqd, vert,
- GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
- if (indexed) {
- for (uint16_t i = 0; i < numPts; ++i) {
- append_countour_edge_indices(isHairline, subpathIdxStart,
- firstQPtIdx + i, idx);
+ // Called after we end each path
+ void endInstance() {
+ fVertexOffset = fCurVert - fVertices;
+ fIndexOffset = fCurIdx - fIndices;
+ SkASSERT(fVertexOffset <= kVerticesPerChunk);
+ SkASSERT(fIndexOffset <= this->maxIndices());
+ }
+
+ /**
+ * Path verbs
+ */
+ void moveTo(const SkPoint& p) {
+ needSpace(1);
+
+ fSubpathIndexStart = this->currentIndex();
+ *(fCurVert++) = p;
+ }
+
+ void addLine(const SkPoint& p) {
+ needSpace(1, this->indexScale());
+
+ if (this->isIndexed()) {
+ uint16_t prevIdx = this->currentIndex() - 1;
+ appendCountourEdgeIndices(prevIdx);
}
+ *(fCurVert++) = p;
+ }
+
+ void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
+ this->needSpace(GrPathUtils::kMaxPointsPerCurve,
+ GrPathUtils::kMaxPointsPerCurve * this->indexScale());
+
+ // First pt of quad is the pt we ended on in previous step
+ uint16_t firstQPtIdx = this->currentIndex() - 1;
+ uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
+ pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
+ GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
+ if (this->isIndexed()) {
+ for (uint16_t i = 0; i < numPts; ++i) {
+ appendCountourEdgeIndices(firstQPtIdx + i);
+ }
+ }
+ }
+
+ void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
+ SkScalar srcSpaceTol) {
+ SkAutoConicToQuads converter;
+ const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
+ for (int i = 0; i < converter.countQuads(); ++i) {
+ this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
+ }
+ }
+
+ void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
+ this->needSpace(GrPathUtils::kMaxPointsPerCurve,
+ GrPathUtils::kMaxPointsPerCurve * this->indexScale());
+
+ // First pt of cubic is the pt we ended on in previous step
+ uint16_t firstCPtIdx = this->currentIndex() - 1;
+ uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
+ pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
+ GrPathUtils::cubicPointCount(pts, srcSpaceTol));
+ if (this->isIndexed()) {
+ for (uint16_t i = 0; i < numPts; ++i) {
+ appendCountourEdgeIndices(firstCPtIdx + i);
+ }
+ }
+ }
+
+ void addPath(const SkPath& path, SkScalar srcSpaceTol) {
+ SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
+
+ SkPath::Iter iter(path, false);
+ SkPoint pts[4];
+
+ bool done = false;
+ while (!done) {
+ SkPath::Verb verb = iter.next(pts);
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ this->moveTo(pts[0]);
+ break;
+ case SkPath::kLine_Verb:
+ this->addLine(pts[1]);
+ break;
+ case SkPath::kConic_Verb:
+ this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
+ break;
+ case SkPath::kQuad_Verb:
+ this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
+ break;
+ case SkPath::kCubic_Verb:
+ this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
+ break;
+ case SkPath::kClose_Verb:
+ break;
+ case SkPath::kDone_Verb:
+ done = true;
+ }
+ }
+ }
+
+ static bool PathHasMultipleSubpaths(const SkPath& path) {
+ bool first = true;
+
+ SkPath::Iter iter(path, false);
+ SkPath::Verb verb;
+
+ SkPoint pts[4];
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+ if (SkPath::kMove_Verb == verb && !first) {
+ return true;
+ }
+ first = false;
+ }
+ return false;
+ }
+
+private:
+ /**
+ * Derived properties
+ * TODO: Cache some of these for better performance, rather than re-computing?
+ */
+ bool isIndexed() const {
+ return kLines_GrPrimitiveType == fMesh.primitiveType() ||
+ kTriangles_GrPrimitiveType == fMesh.primitiveType();
+ }
+ bool isHairline() const {
+ return kLines_GrPrimitiveType == fMesh.primitiveType() ||
+ kLineStrip_GrPrimitiveType == fMesh.primitiveType();
+ }
+ int indexScale() const {
+ switch (fMesh.primitiveType()) {
+ case kLines_GrPrimitiveType:
+ return 2;
+ case kTriangles_GrPrimitiveType:
+ return 3;
+ default:
+ return 0;
+ }
+ }
+ int maxIndices() const { return kVerticesPerChunk * this->indexScale(); }
+
+ uint16_t currentIndex() const { return fCurVert - fVertices; }
+
+ void putBackReserve() {
+ fTarget->putBackIndices((size_t)(this->maxIndices() - fIndexOffset));
+ fTarget->putBackVertices((size_t)(kVerticesPerChunk - fVertexOffset), fVertexStride);
+ }
+
+ // Allocate vertex and (possibly) index buffers
+ void allocNewBuffers() {
+ fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpace(fVertexStride, kVerticesPerChunk,
+ &fVertexBuffer, &fFirstVertex));
+ if (this->isIndexed()) {
+ fIndices = fTarget->makeIndexSpace(this->maxIndices(), &fIndexBuffer, &fFirstIndex);
+ }
+ fVertexOffset = 0;
+ fIndexOffset = 0;
+ }
+
+ void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
+ // When drawing lines we're appending line segments along the countour. When applying the
+ // other fill rules we're drawing triangle fans around the start of the current (sub)path.
+ if (!this->isHairline()) {
+ *(fCurIdx++) = fSubpathIndexStart;
+ }
+ *(fCurIdx++) = edgeV0Idx;
+ *(fCurIdx++) = edgeV0Idx + 1;
}
-}
+
+ // Emits a single draw with all accumulated vertex/index data
+ void emitMesh() {
+ if (!this->isIndexed()) {
+ fMesh.setNonIndexedNonInstanced(fVertexOffset);
+ } else {
+ fMesh.setIndexed(fIndexBuffer, fIndexOffset, fFirstIndex, 0, fVertexOffset - 1);
+ }
+ fMesh.setVertexData(fVertexBuffer, fFirstVertex);
+ fTarget->draw(fGeometryProcessor, fPipeline, fMesh);
+ }
+
+ void needSpace(int vertsNeeded, int indicesNeeded = 0) {
+ if (fCurVert + vertsNeeded > fVertices + kVerticesPerChunk ||
+ fCurIdx + indicesNeeded > fIndices + this->maxIndices()) {
+ // We are about to run out of space (possibly)
+
+ // To maintain continuity, we need to remember one or two points from the current mesh.
+ // Lines only need the last point, fills need the first point from the current contour.
+ // We always grab both here, and append the ones we need at the end of this process.
+ SkPoint lastPt = *(fCurVert - 1);
+ SkPoint subpathStartPt = fVertices[fSubpathIndexStart];
+
+ // Pretend that we've reached the end of an entire path, so our offsets are correct
+ this->endInstance();
+
+ // Draw the mesh we've accumulated
+ this->emitMesh();
+
+ // Put back any unused space, get new buffers
+ this->putBackReserve();
+ this->allocNewBuffers();
+
+ // Start a "new" path, which is really just a continuation of the in-progress one
+ this->beginInstance();
+
+ // Append copies of the points we saved so the two meshes will weld properly
+ if (!this->isHairline()) {
+ *(fCurVert++) = subpathStartPt;
+ }
+ *(fCurVert++) = lastPt;
+ }
+ }
+
+ GrMesh fMesh;
+ GrMeshDrawOp::Target* fTarget;
+ size_t fVertexStride;
+ GrGeometryProcessor* fGeometryProcessor;
+ const GrPipeline* fPipeline;
+
+ const GrBuffer* fVertexBuffer;
+ int fFirstVertex;
+ SkPoint* fVertices;
+ SkPoint* fCurVert;
+ int fVertexOffset;
+
+ const GrBuffer* fIndexBuffer;
+ int fFirstIndex;
+ uint16_t* fIndices;
+ uint16_t* fCurIdx;
+ int fIndexOffset;
+ uint16_t fSubpathIndexStart;
+};
class DefaultPathOp final : public GrLegacyMeshDrawOp {
public:
@@ -153,112 +385,35 @@ private:
gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
}
- size_t vertexStride = gp->getVertexStride();
- SkASSERT(vertexStride == sizeof(SkPoint));
+ SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
int instanceCount = fPaths.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++) {
+ for (int i = 0; !isIndexed && i < instanceCount; i++) {
const PathData& args = fPaths[i];
-
- int contourCount;
- maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
- args.fTolerance);
-
- isIndexed = isIndexed || contourCount > 1;
- }
-
- if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
- //SkDebugf("Cannot render path (%d)\n", maxVertices);
- return;
+ isIndexed = isIndexed || PathGeoBuilder::PathHasMultipleSubpaths(args.fPath);
}
// determine primitiveType
- int maxIndices = 0;
GrPrimitiveType primitiveType;
if (this->isHairline()) {
- if (isIndexed) {
- maxIndices = 2 * maxVertices;
- primitiveType = kLines_GrPrimitiveType;
- } else {
- primitiveType = kLineStrip_GrPrimitiveType;
- }
+ primitiveType = isIndexed ? kLines_GrPrimitiveType : kLineStrip_GrPrimitiveType;
} else {
- if (isIndexed) {
- maxIndices = 3 * maxVertices;
- primitiveType = kTriangles_GrPrimitiveType;
- } else {
- primitiveType = kTriangleFan_GrPrimitiveType;
- }
- }
-
- // allocate vertex / index buffers
- const GrBuffer* vertexBuffer;
- int firstVertex;
-
- void* verts = target->makeVertexSpace(vertexStride, maxVertices,
- &vertexBuffer, &firstVertex);
-
- if (!verts) {
- SkDebugf("Could not allocate vertices\n");
- return;
+ primitiveType = isIndexed ? kTriangles_GrPrimitiveType : kTriangleFan_GrPrimitiveType;
}
- const GrBuffer* indexBuffer = nullptr;
- int firstIndex = 0;
-
- void* indices = nullptr;
- if (isIndexed) {
- indices = target->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex);
-
- if (!indices) {
- SkDebugf("Could not allocate indices\n");
- return;
- }
- }
+ PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(), this->pipeline());
// fill buffers
- int vertexOffset = 0;
- int indexOffset = 0;
for (int i = 0; i < instanceCount; i++) {
const PathData& args = fPaths[i];
- int vertexCnt = 0;
- int indexCnt = 0;
- if (!this->createGeom(verts,
- vertexOffset,
- indices,
- indexOffset,
- &vertexCnt,
- &indexCnt,
- args.fPath,
- args.fTolerance,
- isIndexed)) {
- return;
- }
-
- vertexOffset += vertexCnt;
- indexOffset += indexCnt;
- SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
+ pathGeoBuilder.beginInstance();
+ pathGeoBuilder.addPath(args.fPath, args.fTolerance);
+ pathGeoBuilder.endInstance();
}
-
- GrMesh mesh(primitiveType);
- if (!isIndexed) {
- mesh.setNonIndexedNonInstanced(vertexOffset);
- } else {
- mesh.setIndexed(indexBuffer, indexOffset, firstIndex, 0, vertexOffset - 1);
- }
- mesh.setVertexData(vertexBuffer, firstVertex);
- target->draw(gp.get(), this->pipeline(), mesh);
-
- // put back reserves
- target->putBackIndices((size_t)(maxIndices - indexOffset));
- target->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
}
bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
@@ -289,98 +444,6 @@ private:
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) const {
- SkScalar srcSpaceTolSqd = 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;
- const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
- 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;
- }
- first = false;
- }
-
- *vertexCnt = static_cast<int>(vert - base);
- *indexCnt = static_cast<int>(idx - idxBase);
- return true;
- }
-
GrColor color() const { return fColor; }
uint8_t coverage() const { return fCoverage; }
bool usesLocalCoords() const { return fUsesLocalCoords; }