| Index: src/gpu/GrAAConvexPathRenderer.cpp
|
| diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
|
| index b3010767e3bf4d9d433000572b62ef087a90876c..f4a6266bc17b7bb48bf77df73b1e439c98359e1c 100644
|
| --- a/src/gpu/GrAAConvexPathRenderer.cpp
|
| +++ b/src/gpu/GrAAConvexPathRenderer.cpp
|
| @@ -8,6 +8,9 @@
|
|
|
| #include "GrAAConvexPathRenderer.h"
|
|
|
| +#include "GrBatch.h"
|
| +#include "GrBatchTarget.h"
|
| +#include "GrBufferAllocPool.h"
|
| #include "GrContext.h"
|
| #include "GrDrawTargetCaps.h"
|
| #include "GrGeometryProcessor.h"
|
| @@ -222,46 +225,34 @@ static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::
|
| }
|
|
|
| static inline void add_line_to_segment(const SkPoint& pt,
|
| - SegmentArray* segments,
|
| - SkRect* devBounds) {
|
| + SegmentArray* segments) {
|
| segments->push_back();
|
| segments->back().fType = Segment::kLine;
|
| segments->back().fPts[0] = pt;
|
| - devBounds->growToInclude(pt.fX, pt.fY);
|
| }
|
|
|
| -#ifdef SK_DEBUG
|
| -static inline bool contains_inclusive(const SkRect& rect, const SkPoint& p) {
|
| - return p.fX >= rect.fLeft && p.fX <= rect.fRight && p.fY >= rect.fTop && p.fY <= rect.fBottom;
|
| -}
|
| -#endif
|
| -
|
| static inline void add_quad_segment(const SkPoint pts[3],
|
| - SegmentArray* segments,
|
| - SkRect* devBounds) {
|
| + SegmentArray* segments) {
|
| if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
|
| if (pts[0] != pts[2]) {
|
| - add_line_to_segment(pts[2], segments, devBounds);
|
| + add_line_to_segment(pts[2], segments);
|
| }
|
| } else {
|
| segments->push_back();
|
| segments->back().fType = Segment::kQuad;
|
| segments->back().fPts[0] = pts[1];
|
| segments->back().fPts[1] = pts[2];
|
| - SkASSERT(contains_inclusive(*devBounds, pts[0]));
|
| - devBounds->growToInclude(pts + 1, 2);
|
| }
|
| }
|
|
|
| static inline void add_cubic_segments(const SkPoint pts[4],
|
| SkPath::Direction dir,
|
| - SegmentArray* segments,
|
| - SkRect* devBounds) {
|
| + SegmentArray* segments) {
|
| SkSTArray<15, SkPoint, true> quads;
|
| GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
|
| int count = quads.count();
|
| for (int q = 0; q < count; q += 3) {
|
| - add_quad_segment(&quads[q], segments, devBounds);
|
| + add_quad_segment(&quads[q], segments);
|
| }
|
| }
|
|
|
| @@ -270,8 +261,7 @@ static bool get_segments(const SkPath& path,
|
| SegmentArray* segments,
|
| SkPoint* fanPt,
|
| int* vCount,
|
| - int* iCount,
|
| - SkRect* devBounds) {
|
| + int* iCount) {
|
| SkPath::Iter iter(path, true);
|
| // This renderer over-emphasizes very thin path regions. We use the distance
|
| // to the path from the sample to compute coverage. Every pixel intersected
|
| @@ -294,19 +284,18 @@ static bool get_segments(const SkPath& path,
|
| case SkPath::kMove_Verb:
|
| m.mapPoints(pts, 1);
|
| update_degenerate_test(°enerateData, pts[0]);
|
| - devBounds->set(pts->fX, pts->fY, pts->fX, pts->fY);
|
| break;
|
| case SkPath::kLine_Verb: {
|
| m.mapPoints(&pts[1], 1);
|
| update_degenerate_test(°enerateData, pts[1]);
|
| - add_line_to_segment(pts[1], segments, devBounds);
|
| + add_line_to_segment(pts[1], segments);
|
| break;
|
| }
|
| case SkPath::kQuad_Verb:
|
| m.mapPoints(pts, 3);
|
| update_degenerate_test(°enerateData, pts[1]);
|
| update_degenerate_test(°enerateData, pts[2]);
|
| - add_quad_segment(pts, segments, devBounds);
|
| + add_quad_segment(pts, segments);
|
| break;
|
| case SkPath::kConic_Verb: {
|
| m.mapPoints(pts, 3);
|
| @@ -316,7 +305,7 @@ static bool get_segments(const SkPath& path,
|
| for (int i = 0; i < converter.countQuads(); ++i) {
|
| update_degenerate_test(°enerateData, quadPts[2*i + 1]);
|
| update_degenerate_test(°enerateData, quadPts[2*i + 2]);
|
| - add_quad_segment(quadPts + 2*i, segments, devBounds);
|
| + add_quad_segment(quadPts + 2*i, segments);
|
| }
|
| break;
|
| }
|
| @@ -325,7 +314,7 @@ static bool get_segments(const SkPath& path,
|
| update_degenerate_test(°enerateData, pts[1]);
|
| update_degenerate_test(°enerateData, pts[2]);
|
| update_degenerate_test(°enerateData, pts[3]);
|
| - add_cubic_segments(pts, dir, segments, devBounds);
|
| + add_cubic_segments(pts, dir, segments);
|
| break;
|
| };
|
| case SkPath::kDone_Verb:
|
| @@ -703,95 +692,214 @@ bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target,
|
| stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
|
| }
|
|
|
| -bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target,
|
| - GrPipelineBuilder* pipelineBuilder,
|
| - GrColor color,
|
| - const SkMatrix& vm,
|
| - const SkPath& origPath,
|
| - const SkStrokeRec&,
|
| - bool antiAlias) {
|
| +class AAConvexPathBatch : public GrBatch {
|
| +public:
|
| + struct Geometry {
|
| + GrColor fColor;
|
| + SkMatrix fViewMatrix;
|
| + SkPath fPath;
|
| + SkDEBUGCODE(SkRect fDevBounds;)
|
| + };
|
|
|
| - const SkPath* path = &origPath;
|
| - if (path->isEmpty()) {
|
| - return true;
|
| + static GrBatch* Create(const Geometry& geometry) {
|
| + return SkNEW_ARGS(AAConvexPathBatch, (geometry));
|
| }
|
|
|
| - SkMatrix viewMatrix = vm;
|
| - SkMatrix invert;
|
| - if (!viewMatrix.invert(&invert)) {
|
| - return false;
|
| - }
|
| + const char* name() const SK_OVERRIDE { return "AAConvexBatch"; }
|
|
|
| - // We use the fact that SkPath::transform path does subdivision based on
|
| - // perspective. Otherwise, we apply the view matrix when copying to the
|
| - // segment representation.
|
| - SkPath tmpPath;
|
| - if (viewMatrix.hasPerspective()) {
|
| - origPath.transform(viewMatrix, &tmpPath);
|
| - path = &tmpPath;
|
| - viewMatrix = SkMatrix::I();
|
| + 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);
|
| + }
|
| + void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
|
| + out->setUnknownSingleComponent();
|
| }
|
|
|
| - QuadVertex *verts;
|
| - uint16_t* idxs;
|
| + void initBatchOpt(const GrBatchOpt& batchOpt) {
|
| + fBatchOpt = batchOpt;
|
| + }
|
|
|
| - int vCount;
|
| - int iCount;
|
| - enum {
|
| - kPreallocSegmentCnt = 512 / sizeof(Segment),
|
| - kPreallocDrawCnt = 4,
|
| - };
|
| - SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
|
| - SkPoint fanPt;
|
| + 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;
|
| + }
|
|
|
| - // We can't simply use the path bounds because we may degenerate cubics to quads which produces
|
| - // new control points outside the original convex hull.
|
| - SkRect devBounds;
|
| - if (!get_segments(*path, viewMatrix, &segments, &fanPt, &vCount, &iCount, &devBounds)) {
|
| - return false;
|
| + // setup batch properties
|
| + fBatch.fColorIgnored = init.fColorIgnored;
|
| + fBatch.fColor = fGeoData[0].fColor;
|
| + fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
|
| + fBatch.fCoverageIgnored = init.fCoverageIgnored;
|
| }
|
|
|
| - // Our computed verts should all be within one pixel of the segment control points.
|
| - devBounds.outset(SK_Scalar1, SK_Scalar1);
|
| + void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
|
| + int instanceCount = fGeoData.count();
|
|
|
| - SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(color, invert));
|
| + SkMatrix invert;
|
| + if (!this->viewMatrix().invert(&invert)) {
|
| + SkDebugf("Could not invert viewmatrix\n");
|
| + return;
|
| + }
|
|
|
| - GrDrawTarget::AutoReleaseGeometry arg(target, vCount, quadProcessor->getVertexStride(), iCount);
|
| - SkASSERT(quadProcessor->getVertexStride() == sizeof(QuadVertex));
|
| - if (!arg.succeeded()) {
|
| - return false;
|
| - }
|
| - verts = reinterpret_cast<QuadVertex*>(arg.vertices());
|
| - idxs = reinterpret_cast<uint16_t*>(arg.indices());
|
| + // Setup GrGeometryProcessor
|
| + SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(this->color(),
|
| + invert));
|
| +
|
| + batchTarget->initDraw(quadProcessor, pipeline);
|
| +
|
| + // TODO remove this when batch is everywhere
|
| + GrPipelineInfo init;
|
| + init.fColorIgnored = fBatch.fColorIgnored;
|
| + init.fOverrideColor = GrColor_ILLEGAL;
|
| + init.fCoverageIgnored = fBatch.fCoverageIgnored;
|
| + init.fUsesLocalCoords = this->usesLocalCoords();
|
| + quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
|
| +
|
| + // TODO generate all segments for all paths and use one vertex buffer
|
| + for (int i = 0; i < instanceCount; i++) {
|
| + Geometry& args = fGeoData[i];
|
| +
|
| + // We use the fact that SkPath::transform path does subdivision based on
|
| + // perspective. Otherwise, we apply the view matrix when copying to the
|
| + // segment representation.
|
| + const SkMatrix* viewMatrix = &args.fViewMatrix;
|
| + if (viewMatrix->hasPerspective()) {
|
| + args.fPath.transform(*viewMatrix);
|
| + viewMatrix = &SkMatrix::I();
|
| + }
|
| +
|
| + int vertexCount;
|
| + int indexCount;
|
| + enum {
|
| + kPreallocSegmentCnt = 512 / sizeof(Segment),
|
| + kPreallocDrawCnt = 4,
|
| + };
|
| + SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
|
| + SkPoint fanPt;
|
| +
|
| + if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &vertexCount,
|
| + &indexCount)) {
|
| + continue;
|
| + }
|
| +
|
| + const GrVertexBuffer* vertexBuffer;
|
| + int firstVertex;
|
| +
|
| + size_t vertexStride = quadProcessor->getVertexStride();
|
| + void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
|
| + vertexCount,
|
| + &vertexBuffer,
|
| + &firstVertex);
|
|
|
| - SkSTArray<kPreallocDrawCnt, Draw, true> draws;
|
| - create_vertices(segments, fanPt, &draws, verts, idxs);
|
| + const GrIndexBuffer* indexBuffer;
|
| + int firstIndex;
|
| +
|
| + void *indices = batchTarget->indexPool()->makeSpace(indexCount,
|
| + &indexBuffer,
|
| + &firstIndex);
|
| +
|
| + QuadVertex* verts = reinterpret_cast<QuadVertex*>(vertices);
|
| + uint16_t* idxs = reinterpret_cast<uint16_t*>(indices);
|
| +
|
| + SkSTArray<kPreallocDrawCnt, Draw, true> draws;
|
| + create_vertices(segments, fanPt, &draws, verts, idxs);
|
|
|
| - // Check devBounds
|
| #ifdef SK_DEBUG
|
| - SkRect tolDevBounds = devBounds;
|
| - tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000);
|
| - SkRect actualBounds;
|
| - actualBounds.set(verts[0].fPos, verts[1].fPos);
|
| - for (int i = 2; i < vCount; ++i) {
|
| - actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
|
| - }
|
| - SkASSERT(tolDevBounds.contains(actualBounds));
|
| + // Check devBounds
|
| + SkRect actualBounds;
|
| + actualBounds.set(verts[0].fPos, verts[1].fPos);
|
| + for (int i = 2; i < vertexCount; ++i) {
|
| + actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
|
| + }
|
| + SkASSERT(args.fDevBounds.contains(actualBounds));
|
| #endif
|
|
|
| - int vOffset = 0;
|
| - for (int i = 0; i < draws.count(); ++i) {
|
| - const Draw& draw = draws[i];
|
| - target->drawIndexed(pipelineBuilder,
|
| - quadProcessor,
|
| - kTriangles_GrPrimitiveType,
|
| - vOffset, // start vertex
|
| - 0, // start index
|
| - draw.fVertexCnt,
|
| - draw.fIndexCnt,
|
| - &devBounds);
|
| - vOffset += draw.fVertexCnt;
|
| + GrDrawTarget::DrawInfo info;
|
| + info.setVertexBuffer(vertexBuffer);
|
| + info.setIndexBuffer(indexBuffer);
|
| + info.setPrimitiveType(kTriangles_GrPrimitiveType);
|
| + info.setStartIndex(firstIndex);
|
| +
|
| + int vOffset = 0;
|
| + for (int i = 0; i < draws.count(); ++i) {
|
| + const Draw& draw = draws[i];
|
| + info.setStartVertex(vOffset + firstVertex);
|
| + info.setVertexCount(draw.fVertexCnt);
|
| + info.setIndexCount(draw.fIndexCnt);
|
| + batchTarget->draw(info);
|
| + vOffset += draw.fVertexCnt;
|
| + }
|
| + }
|
| + }
|
| +
|
| + SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
|
| +
|
| +private:
|
| + AAConvexPathBatch(const Geometry& geometry) {
|
| + this->initClassID<AAConvexPathBatch>();
|
| + fGeoData.push_back(geometry);
|
| + }
|
| +
|
| + bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
|
| + AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
|
| +
|
| + if (this->color() != that->color()) {
|
| + return false;
|
| + }
|
| +
|
| + SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
|
| + if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
|
| + return false;
|
| + }
|
| +
|
| + fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
|
| + return true;
|
| + }
|
| +
|
| + GrColor color() const { return fBatch.fColor; }
|
| + bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
|
| + const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
|
| +
|
| + struct BatchTracker {
|
| + GrColor fColor;
|
| + bool fUsesLocalCoords;
|
| + bool fColorIgnored;
|
| + bool fCoverageIgnored;
|
| + };
|
| +
|
| + GrBatchOpt fBatchOpt;
|
| + BatchTracker fBatch;
|
| + SkSTArray<1, Geometry, true> fGeoData;
|
| +};
|
| +
|
| +bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target,
|
| + GrPipelineBuilder* pipelineBuilder,
|
| + GrColor color,
|
| + const SkMatrix& vm,
|
| + const SkPath& path,
|
| + const SkStrokeRec&,
|
| + bool antiAlias) {
|
| + if (path.isEmpty()) {
|
| + return true;
|
| }
|
|
|
| + // We outset our vertices one pixel and add one more pixel for precision.
|
| + // TODO create tighter bounds when we start reordering.
|
| + SkRect devRect = path.getBounds();
|
| + vm.mapRect(&devRect);
|
| + devRect.outset(2, 2);
|
| +
|
| + AAConvexPathBatch::Geometry geometry;
|
| + geometry.fColor = color;
|
| + geometry.fViewMatrix = vm;
|
| + geometry.fPath = path;
|
| + SkDEBUGCODE(geometry.fDevBounds = devRect;)
|
| +
|
| + SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry));
|
| + target->drawBatch(pipelineBuilder, batch, &devRect);
|
| +
|
| return true;
|
| +
|
| }
|
|
|