diff options
author | Brian Osman <brianosman@google.com> | 2017-06-07 14:43:17 +0000 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-06-07 14:43:29 +0000 |
commit | fd7819c5d8779e7394277abb49e0a0b2b03d1045 (patch) | |
tree | 6fc8e37394d7922c1baf8c09312edce584a6fe5f /src | |
parent | 7785dd235d50e51f80bc4bb82639b0ffb29540b5 (diff) |
Revert "Handle too many (or too large) paths in GrDefaultPathRenderer"
This reverts commit 6383b298489504d7f8f822d7da575b04b14a9737.
Reason for revert: Test failures
Original change's description:
> Handle too many (or too large) paths in GrDefaultPathRenderer
>
> PathGeoBuilder constructs the geometry with the same basic
> technique as before, but allows interrupting the process
> to emit multiple draws.
>
> Original test case was 2000 non-AA stroked circles, which
> created ~66000 vertices. That now renders, as do various
> tests with a single large path (as well as filled paths).
>
> TODO: I think that this could be extracted and re-used for
> MSAA path renderer without too much work? I need to read
> that code more carefully to make sure it lines up.
>
> Bug: skia:6695
> Change-Id: I18983ba3d4f475ae0651946958b4911008aa623f
> Reviewed-on: https://skia-review.googlesource.com/18360
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Commit-Queue: Brian Osman <brianosman@google.com>
TBR=bsalomon@google.com,robertphillips@google.com,brianosman@google.com
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:6695
Change-Id: I78ce9879a2e45e19f53027ca506cc2e8fae664b3
Reviewed-on: https://skia-review.googlesource.com/18981
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/gpu/GrPathUtils.cpp | 13 | ||||
-rw-r--r-- | src/gpu/GrPathUtils.h | 3 | ||||
-rw-r--r-- | src/gpu/ops/GrDefaultPathRenderer.cpp | 475 |
3 files changed, 213 insertions, 278 deletions
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp index 918493982a..91a48f7b6b 100644 --- a/src/gpu/GrPathUtils.cpp +++ b/src/gpu/GrPathUtils.cpp @@ -11,6 +11,7 @@ #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, @@ -44,7 +45,7 @@ uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol) SkScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]); if (!SkScalarIsFinite(d)) { - return kMaxPointsPerCurve; + return MAX_POINTS_PER_CURVE; } else if (d <= tol) { return 1; } else { @@ -54,7 +55,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 kMaxPointsPerCurve; + return MAX_POINTS_PER_CURVE; } else { int temp = SkScalarCeilToInt(divSqrt); int pow2 = GrNextPow2(temp); @@ -64,7 +65,7 @@ uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol) if (pow2 < 1) { pow2 = 1; } - return SkTMin(pow2, kMaxPointsPerCurve); + return SkTMin(pow2, MAX_POINTS_PER_CURVE); } } } @@ -104,13 +105,13 @@ uint32_t GrPathUtils::cubicPointCount(const SkPoint points[], points[2].distanceToLineSegmentBetweenSqd(points[0], points[3])); d = SkScalarSqrt(d); if (!SkScalarIsFinite(d)) { - return kMaxPointsPerCurve; + return MAX_POINTS_PER_CURVE; } else if (d <= tol) { return 1; } else { SkScalar divSqrt = SkScalarSqrt(d / tol); if (((SkScalar)SK_MaxS32) <= divSqrt) { - return kMaxPointsPerCurve; + return MAX_POINTS_PER_CURVE; } else { int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol)); int pow2 = GrNextPow2(temp); @@ -120,7 +121,7 @@ uint32_t GrPathUtils::cubicPointCount(const SkPoint points[], if (pow2 < 1) { pow2 = 1; } - return SkTMin(pow2, kMaxPointsPerCurve); + return SkTMin(pow2, MAX_POINTS_PER_CURVE); } } } diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h index 421fca129b..fdfd375427 100644 --- a/src/gpu/GrPathUtils.h +++ b/src/gpu/GrPathUtils.h @@ -165,8 +165,5 @@ 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 22e24e6544..c282036971 100644 --- a/src/gpu/ops/GrDefaultPathRenderer.cpp +++ b/src/gpu/ops/GrDefaultPathRenderer.cpp @@ -59,269 +59,37 @@ GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const { } } -// 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; - } - - // 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(); +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; +} - // Append copies of the points we saved so the two meshes will weld properly - if (!this->isHairline()) { - *(fCurVert++) = subpathStartPt; - } - *(fCurVert++) = lastPt; +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); } } - - 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: @@ -385,35 +153,112 @@ private: gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix()); } - SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); + size_t vertexStride = gp->getVertexStride(); + SkASSERT(vertexStride == 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; !isIndexed && i < instanceCount; i++) { + for (int i = 0; i < instanceCount; i++) { const PathData& args = fPaths[i]; - isIndexed = isIndexed || PathGeoBuilder::PathHasMultipleSubpaths(args.fPath); + + 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; } // determine primitiveType + int maxIndices = 0; GrPrimitiveType primitiveType; if (this->isHairline()) { - primitiveType = isIndexed ? kLines_GrPrimitiveType : kLineStrip_GrPrimitiveType; + if (isIndexed) { + maxIndices = 2 * maxVertices; + primitiveType = kLines_GrPrimitiveType; + } else { + primitiveType = kLineStrip_GrPrimitiveType; + } } else { - primitiveType = isIndexed ? kTriangles_GrPrimitiveType : kTriangleFan_GrPrimitiveType; + 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; } - PathGeoBuilder pathGeoBuilder(primitiveType, target, gp.get(), this->pipeline()); + 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; + } + } // fill buffers + int vertexOffset = 0; + int indexOffset = 0; for (int i = 0; i < instanceCount; i++) { const PathData& args = fPaths[i]; - pathGeoBuilder.beginInstance(); - pathGeoBuilder.addPath(args.fPath, args.fTolerance); - pathGeoBuilder.endInstance(); + 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); } + + 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 { @@ -444,6 +289,98 @@ 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; } |