diff options
author | 2017-11-27 15:34:26 -0700 | |
---|---|---|
committer | 2017-11-27 23:17:57 +0000 | |
commit | c9c97b7fd5f9ca8b95223d99dac063d4a2510a4d (patch) | |
tree | 5a3b8719ea5737eda6fdedae7acf62bc1600c608 /src/gpu/ccpr/GrCCPRCoverageOp.cpp | |
parent | 7b093b77427ab52dc7a43deef3ebbeae378e7b2c (diff) |
CCPR: Transform path points before parsing
Transforms a path's points into a local buffer up front, rather than
transforming as we parse. This hopefully gets better vector
performance as well as allowing us to skip the transformation step
for paths that are known to be in device space already.
Introduces a test for parsing empty paths and does general cleanup.
Bug: skia:7190
Change-Id: Ib86d2ffcdef6fa3ec66f6d9ad4b10c0b6d44c0dc
Reviewed-on: https://skia-review.googlesource.com/74621
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Diffstat (limited to 'src/gpu/ccpr/GrCCPRCoverageOp.cpp')
-rw-r--r-- | src/gpu/ccpr/GrCCPRCoverageOp.cpp | 126 |
1 files changed, 58 insertions, 68 deletions
diff --git a/src/gpu/ccpr/GrCCPRCoverageOp.cpp b/src/gpu/ccpr/GrCCPRCoverageOp.cpp index 5e0538f525..3ed8a76c9b 100644 --- a/src/gpu/ccpr/GrCCPRCoverageOp.cpp +++ b/src/gpu/ccpr/GrCCPRCoverageOp.cpp @@ -20,71 +20,67 @@ using TriangleInstance = GrCCPRCoverageProcessor::TriangleInstance; using CurveInstance = GrCCPRCoverageProcessor::CurveInstance; -/** - * This is a view matrix that accumulates two bounding boxes as it maps points: device-space bounds - * and "45 degree" device-space bounds (| 1 -1 | * devCoords). - * | 1 1 | - */ -class AccumulatingViewMatrix { -public: - AccumulatingViewMatrix(const SkMatrix& m, const SkPoint& initialPoint); - - SkPoint transform(const SkPoint& pt); - void getAccumulatedBounds(SkRect* devBounds, SkRect* devBounds45) const; - -private: - Sk4f fX; - Sk4f fY; - Sk4f fT; - - Sk4f fTopLeft; - Sk4f fBottomRight; -}; - -inline AccumulatingViewMatrix::AccumulatingViewMatrix(const SkMatrix& m, - const SkPoint& initialPoint) { - // m45 transforms into 45 degree space in order to find the octagon's diagonals. We could - // use SK_ScalarRoot2Over2 if we wanted an orthonormal transform, but this is irrelevant as - // long as the shader uses the correct inverse when coming back to device space. +void GrCCPRCoverageOpsBuilder::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds, + SkRect* devBounds45) { + const SkPoint* pts = SkPathPriv::PointData(path); + int numPts = path.countPoints(); + SkASSERT(numPts + 1 <= fLocalDevPtsBuffer.count()); + + if (!numPts) { + devBounds->setEmpty(); + devBounds45->setEmpty(); + this->parsePath(path, nullptr); + return; + } + + // m45 transforms path points into "45 degree" device space. A bounding box in this space gives + // the circumscribing octagon's diagonals. We could use SK_ScalarRoot2Over2, but an orthonormal + // transform is not necessary as long as the shader uses the correct inverse. SkMatrix m45; m45.setSinCos(1, 1); m45.preConcat(m); - fX = Sk4f(m.getScaleX(), m.getSkewY(), m45.getScaleX(), m45.getSkewY()); - fY = Sk4f(m.getSkewX(), m.getScaleY(), m45.getSkewX(), m45.getScaleY()); - fT = Sk4f(m.getTranslateX(), m.getTranslateY(), m45.getTranslateX(), m45.getTranslateY()); - - Sk4f transformed = SkNx_fma(fY, Sk4f(initialPoint.y()), fT); - transformed = SkNx_fma(fX, Sk4f(initialPoint.x()), transformed); - fTopLeft = fBottomRight = transformed; -} - -inline SkPoint AccumulatingViewMatrix::transform(const SkPoint& pt) { - Sk4f transformed = SkNx_fma(fY, Sk4f(pt.y()), fT); - transformed = SkNx_fma(fX, Sk4f(pt.x()), transformed); - - fTopLeft = Sk4f::Min(fTopLeft, transformed); - fBottomRight = Sk4f::Max(fBottomRight, transformed); + // X,Y,T are two parallel view matrices that accumulate two bounding boxes as they map points: + // device-space bounds and "45 degree" device-space bounds (| 1 -1 | * devCoords). + // | 1 1 | + Sk4f X = Sk4f(m.getScaleX(), m.getSkewY(), m45.getScaleX(), m45.getSkewY()); + Sk4f Y = Sk4f(m.getSkewX(), m.getScaleY(), m45.getSkewX(), m45.getScaleY()); + Sk4f T = Sk4f(m.getTranslateX(), m.getTranslateY(), m45.getTranslateX(), m45.getTranslateY()); + + // Map the path's points to device space and accumulate bounding boxes. + Sk4f devPt = SkNx_fma(Y, Sk4f(pts[0].y()), T); + devPt = SkNx_fma(X, Sk4f(pts[0].x()), devPt); + Sk4f topLeft = devPt; + Sk4f bottomRight = devPt; + + // Store all 4 values [dev.x, dev.y, dev45.x, dev45.y]. We are only interested in the first two, + // and will overwrite [dev45.x, dev45.y] with the next point. This is why the dst buffer must + // be at least one larger than the number of points. + devPt.store(&fLocalDevPtsBuffer[0]); + + for (int i = 1; i < numPts; ++i) { + devPt = SkNx_fma(Y, Sk4f(pts[i].y()), T); + devPt = SkNx_fma(X, Sk4f(pts[i].x()), devPt); + topLeft = Sk4f::Min(topLeft, devPt); + bottomRight = Sk4f::Max(bottomRight, devPt); + devPt.store(&fLocalDevPtsBuffer[i]); + } - // TODO: vst1_lane_f32? (Sk4f::storeLane?) - float data[4]; - transformed.store(data); - return SkPoint::Make(data[0], data[1]); -} + SkPoint topLeftPts[2], bottomRightPts[2]; + topLeft.store(topLeftPts); + bottomRight.store(bottomRightPts); + devBounds->setLTRB(topLeftPts[0].x(), topLeftPts[0].y(), + bottomRightPts[0].x(), bottomRightPts[0].y()); + devBounds45->setLTRB(topLeftPts[1].x(), topLeftPts[1].y(), + bottomRightPts[1].x(), bottomRightPts[1].y()); -inline void AccumulatingViewMatrix::getAccumulatedBounds(SkRect* devBounds, - SkRect* devBounds45) const { - float topLeft[4], bottomRight[4]; - fTopLeft.store(topLeft); - fBottomRight.store(bottomRight); - devBounds->setLTRB(topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]); - devBounds45->setLTRB(topLeft[2], topLeft[3], bottomRight[2], bottomRight[3]); + this->parsePath(path, fLocalDevPtsBuffer.get()); } -void GrCCPRCoverageOpsBuilder::parsePath(const SkMatrix& viewMatrix, const SkPath& path, - SkRect* devBounds, SkRect* devBounds45) { +void GrCCPRCoverageOpsBuilder::parsePath(const SkPath& path, const SkPoint* deviceSpacePts) { SkASSERT(!fParsingPath); SkDEBUGCODE(fParsingPath = true); + SkASSERT(path.isEmpty() || deviceSpacePts); fCurrPathPointsIdx = fGeometry.points().count(); fCurrPathVerbsIdx = fGeometry.verbs().count(); @@ -93,22 +89,18 @@ void GrCCPRCoverageOpsBuilder::parsePath(const SkMatrix& viewMatrix, const SkPat fGeometry.beginPath(); if (path.isEmpty()) { - devBounds->setEmpty(); - devBounds45->setEmpty(); return; } - const SkPoint* const pts = SkPathPriv::PointData(path); int ptsIdx = 0; bool insideContour = false; - AccumulatingViewMatrix m(viewMatrix, pts[0]); - for (SkPath::Verb verb : SkPathPriv::Verbs(path)) { switch (verb) { case SkPath::kMove_Verb: this->endContourIfNeeded(insideContour); - fGeometry.beginContour(m.transform(pts[ptsIdx++])); + fGeometry.beginContour(deviceSpacePts[ptsIdx]); + ++ptsIdx; insideContour = true; continue; case SkPath::kClose_Verb: @@ -116,17 +108,16 @@ void GrCCPRCoverageOpsBuilder::parsePath(const SkMatrix& viewMatrix, const SkPat insideContour = false; continue; case SkPath::kLine_Verb: - fGeometry.lineTo(m.transform(pts[ptsIdx++])); + fGeometry.lineTo(deviceSpacePts[ptsIdx]); + ++ptsIdx; continue; case SkPath::kQuad_Verb: - SkASSERT(ptsIdx >= 1); // SkPath should have inserted an implicit moveTo if needed. - fGeometry.quadraticTo(m.transform(pts[ptsIdx]), m.transform(pts[ptsIdx + 1])); + fGeometry.quadraticTo(deviceSpacePts[ptsIdx], deviceSpacePts[ptsIdx + 1]); ptsIdx += 2; continue; case SkPath::kCubic_Verb: - SkASSERT(ptsIdx >= 1); // SkPath should have inserted an implicit moveTo if needed. - fGeometry.cubicTo(m.transform(pts[ptsIdx]), m.transform(pts[ptsIdx + 1]), - m.transform(pts[ptsIdx + 2])); + fGeometry.cubicTo(deviceSpacePts[ptsIdx], deviceSpacePts[ptsIdx + 1], + deviceSpacePts[ptsIdx + 2]); ptsIdx += 3; continue; case SkPath::kConic_Verb: @@ -137,7 +128,6 @@ void GrCCPRCoverageOpsBuilder::parsePath(const SkMatrix& viewMatrix, const SkPat } this->endContourIfNeeded(insideContour); - m.getAccumulatedBounds(devBounds, devBounds45); } void GrCCPRCoverageOpsBuilder::endContourIfNeeded(bool insideContour) { |