diff options
author | 2015-02-05 14:39:01 -0800 | |
---|---|---|
committer | 2015-02-05 14:39:01 -0800 | |
commit | 27f398f04dff306418a142c27175eaa35d21a915 (patch) | |
tree | be0e157043e15160af167396965c4674b0942021 /src | |
parent | 21b2c53218ab25f4268e3992e51d916076a2a7ee (diff) |
Convex batch
BUG=skia:
Review URL: https://codereview.chromium.org/880643002
Diffstat (limited to 'src')
-rw-r--r-- | src/gpu/GrAAConvexPathRenderer.cpp | 298 |
1 files changed, 203 insertions, 95 deletions
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp index b3010767e3..f4a6266bc1 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; + } |