| Index: src/gpu/batches/GrTessellatingPathRenderer.cpp
|
| diff --git a/src/gpu/batches/GrTessellatingPathRenderer.cpp b/src/gpu/batches/GrTessellatingPathRenderer.cpp
|
| index b2c3138aed438f9e0979826cdacd121dd8bc5da2..541254530880a96b2b8cf4248c7217309f172a43 100644
|
| --- a/src/gpu/batches/GrTessellatingPathRenderer.cpp
|
| +++ b/src/gpu/batches/GrTessellatingPathRenderer.cpp
|
| @@ -12,6 +12,7 @@
|
| #include "GrBatchTest.h"
|
| #include "GrClip.h"
|
| #include "GrDefaultGeoProcFactory.h"
|
| +#include "GrDrawTarget.h"
|
| #include "GrMesh.h"
|
| #include "GrPathUtils.h"
|
| #include "GrPipelineBuilder.h"
|
| @@ -24,10 +25,12 @@
|
|
|
| #include <stdio.h>
|
|
|
| +#define SK_DISABLE_SCREENSPACE_TESS_AA_PATH_RENDERER
|
| +
|
| /*
|
| - * This path renderer tessellates the path into triangles using GrTessellator, uploads the triangles
|
| - * to a vertex buffer, and renders them with a single draw call. It does not currently do
|
| - * antialiasing, so it must be used in conjunction with multisampling.
|
| + * This path renderer tessellates the path into triangles using GrTessellator, uploads the
|
| + * triangles to a vertex buffer, and renders them with a single draw call. It can do screenspace
|
| + * antialiasing with a one-pixel coverage ramp.
|
| */
|
| namespace {
|
|
|
| @@ -64,22 +67,23 @@ bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
|
|
|
| class StaticVertexAllocator : public GrTessellator::VertexAllocator {
|
| public:
|
| - StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB)
|
| - : fResourceProvider(resourceProvider)
|
| + StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
|
| + : VertexAllocator(stride)
|
| + , fResourceProvider(resourceProvider)
|
| , fCanMapVB(canMapVB)
|
| , fVertices(nullptr) {
|
| }
|
| - SkPoint* lock(int vertexCount) override {
|
| - size_t size = vertexCount * sizeof(SkPoint);
|
| + void* lock(int vertexCount) override {
|
| + size_t size = vertexCount * stride();
|
| fVertexBuffer.reset(fResourceProvider->createBuffer(
|
| size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0));
|
| if (!fVertexBuffer.get()) {
|
| return nullptr;
|
| }
|
| if (fCanMapVB) {
|
| - fVertices = static_cast<SkPoint*>(fVertexBuffer->map());
|
| + fVertices = fVertexBuffer->map();
|
| } else {
|
| - fVertices = new SkPoint[vertexCount];
|
| + fVertices = sk_malloc_throw(vertexCount * stride());
|
| }
|
| return fVertices;
|
| }
|
| @@ -87,8 +91,8 @@ public:
|
| if (fCanMapVB) {
|
| fVertexBuffer->unmap();
|
| } else {
|
| - fVertexBuffer->updateData(fVertices, actualCount * sizeof(SkPoint));
|
| - delete[] fVertices;
|
| + fVertexBuffer->updateData(fVertices, actualCount * stride());
|
| + sk_free(fVertices);
|
| }
|
| fVertices = nullptr;
|
| }
|
| @@ -97,7 +101,34 @@ private:
|
| SkAutoTUnref<GrBuffer> fVertexBuffer;
|
| GrResourceProvider* fResourceProvider;
|
| bool fCanMapVB;
|
| - SkPoint* fVertices;
|
| + void* fVertices;
|
| +};
|
| +
|
| +class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
|
| +public:
|
| + DynamicVertexAllocator(size_t stride, GrVertexBatch::Target* target)
|
| + : VertexAllocator(stride)
|
| + , fTarget(target)
|
| + , fVertexBuffer(nullptr)
|
| + , fVertices(nullptr) {
|
| + }
|
| + void* lock(int vertexCount) override {
|
| + fVertexCount = vertexCount;
|
| + fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
|
| + return fVertices;
|
| + }
|
| + void unlock(int actualCount) override {
|
| + fTarget->putBackVertices(fVertexCount - actualCount, stride());
|
| + fVertices = nullptr;
|
| + }
|
| + const GrBuffer* vertexBuffer() const { return fVertexBuffer; }
|
| + int firstVertex() const { return fFirstVertex; }
|
| +private:
|
| + GrVertexBatch::Target* fTarget;
|
| + const GrBuffer* fVertexBuffer;
|
| + int fVertexCount;
|
| + int fFirstVertex;
|
| + void* fVertices;
|
| };
|
|
|
| } // namespace
|
| @@ -106,13 +137,30 @@ GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
|
| }
|
|
|
| bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
|
| - // This path renderer can draw fill styles but does not do antialiasing. It can do convex and
|
| - // concave paths, but we'll leave the convex ones to simpler algorithms. We pass on paths that
|
| - // have styles, though they may come back around after applying the styling information to the
|
| - // geometry to create a filled path. We also skip paths that don't have a key since the real
|
| - // advantage of this path renderer comes from caching the tessellated geometry.
|
| - return !args.fShape->style().applies() && args.fShape->style().isSimpleFill() &&
|
| - !args.fAntiAlias && args.fShape->hasUnstyledKey() && !args.fShape->knownToBeConvex();
|
| + // This path renderer can draw fill styles, and can do screenspace antialiasing via a
|
| + // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex
|
| + // ones to simpler algorithms. We pass on paths that have styles, though they may come back
|
| + // around after applying the styling information to the geometry to create a filled path. In
|
| + // the non-AA case, We skip paths thta don't have a key since the real advantage of this path
|
| + // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we
|
| + // accept paths without keys.
|
| + if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
|
| + return false;
|
| + }
|
| + if (args.fAntiAlias) {
|
| +#ifdef SK_DISABLE_SCREENSPACE_TESS_AA_PATH_RENDERER
|
| + return false;
|
| +#else
|
| + SkPath path;
|
| + args.fShape->asPath(&path);
|
| + if (path.countVerbs() > 10) {
|
| + return false;
|
| + }
|
| +#endif
|
| + } else if (!args.fShape->hasUnstyledKey()) {
|
| + return false;
|
| + }
|
| + return true;
|
| }
|
|
|
| class TessellatingPathBatch : public GrVertexBatch {
|
| @@ -122,8 +170,9 @@ public:
|
| static GrDrawBatch* Create(const GrColor& color,
|
| const GrShape& shape,
|
| const SkMatrix& viewMatrix,
|
| - SkRect clipBounds) {
|
| - return new TessellatingPathBatch(color, shape, viewMatrix, clipBounds);
|
| + SkIRect devClipBounds,
|
| + bool antiAlias) {
|
| + return new TessellatingPathBatch(color, shape, viewMatrix, devClipBounds, antiAlias);
|
| }
|
|
|
| const char* name() const override { return "TessellatingPathBatch"; }
|
| @@ -145,41 +194,53 @@ private:
|
| fPipelineInfo = overrides;
|
| }
|
|
|
| - void draw(Target* target, const GrGeometryProcessor* gp) const {
|
| - GrResourceProvider* rp = target->resourceProvider();
|
| - SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
|
| - SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix,
|
| - fShape.bounds());
|
| -
|
| + SkPath getPath() const {
|
| + SkASSERT(!fShape.style().applies());
|
| SkPath path;
|
| fShape.asPath(&path);
|
| - bool inverseFill = path.isInverseFillType();
|
| + return path;
|
| + }
|
| +
|
| + void draw(Target* target, const GrGeometryProcessor* gp) const {
|
| + SkASSERT(!fAntiAlias);
|
| + GrResourceProvider* rp = target->resourceProvider();
|
| + bool inverseFill = fShape.inverseFilled();
|
| // construct a cache key from the path's genID and the view matrix
|
| static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
| GrUniqueKey key;
|
| - static constexpr int kClipBoundsCnt = sizeof(fClipBounds) / sizeof(uint32_t);
|
| + static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
|
| int shapeKeyDataCnt = fShape.unstyledKeySize();
|
| SkASSERT(shapeKeyDataCnt >= 0);
|
| GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
|
| fShape.writeUnstyledKey(&builder[0]);
|
| // For inverse fills, the tessellation is dependent on clip bounds.
|
| if (inverseFill) {
|
| - memcpy(&builder[shapeKeyDataCnt], &fClipBounds, sizeof(fClipBounds));
|
| + memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds));
|
| } else {
|
| - memset(&builder[shapeKeyDataCnt], 0, sizeof(fClipBounds));
|
| + memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds));
|
| }
|
| builder.finish();
|
| SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
|
| int actualCount;
|
| + SkScalar tol = GrPathUtils::kDefaultTolerance;
|
| + tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
|
| if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
|
| this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
|
| return;
|
| }
|
|
|
| + SkRect clipBounds = SkRect::Make(fDevClipBounds);
|
| +
|
| + SkMatrix vmi;
|
| + if (!fViewMatrix.invert(&vmi)) {
|
| + return;
|
| + }
|
| + vmi.mapRect(&clipBounds);
|
| bool isLinear;
|
| bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
|
| - StaticVertexAllocator allocator(rp, canMapVB);
|
| - int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear);
|
| + StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB);
|
| + int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator,
|
| + false, GrColor(), false, &isLinear);
|
| if (count == 0) {
|
| return;
|
| }
|
| @@ -191,6 +252,27 @@ private:
|
| rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
|
| }
|
|
|
| + void drawAA(Target* target, const GrGeometryProcessor* gp) const {
|
| + SkASSERT(fAntiAlias);
|
| + SkPath path = getPath();
|
| + if (path.isEmpty()) {
|
| + return;
|
| + }
|
| + SkRect clipBounds = SkRect::Make(fDevClipBounds);
|
| + path.transform(fViewMatrix);
|
| + SkScalar tol = GrPathUtils::kDefaultTolerance;
|
| + bool isLinear;
|
| + DynamicVertexAllocator allocator(gp->getVertexStride(), target);
|
| + bool canTweakAlphaForCoverage = fPipelineInfo.canTweakAlphaForCoverage();
|
| + int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator,
|
| + true, fColor, canTweakAlphaForCoverage,
|
| + &isLinear);
|
| + if (count == 0) {
|
| + return;
|
| + }
|
| + drawVertices(target, gp, allocator.vertexBuffer(), allocator.firstVertex(), count);
|
| + }
|
| +
|
| void onPrepareDraws(Target* target) const override {
|
| sk_sp<GrGeometryProcessor> gp;
|
| {
|
| @@ -201,21 +283,35 @@ private:
|
| LocalCoords::kUsePosition_Type :
|
| LocalCoords::kUnused_Type);
|
| Coverage::Type coverageType;
|
| - if (fPipelineInfo.readsCoverage()) {
|
| + if (fAntiAlias) {
|
| + color = Color(Color::kAttribute_Type);
|
| + if (fPipelineInfo.canTweakAlphaForCoverage()) {
|
| + coverageType = Coverage::kSolid_Type;
|
| + } else {
|
| + coverageType = Coverage::kAttribute_Type;
|
| + }
|
| + } else if (fPipelineInfo.readsCoverage()) {
|
| coverageType = Coverage::kSolid_Type;
|
| } else {
|
| coverageType = Coverage::kNone_Type;
|
| }
|
| Coverage coverage(coverageType);
|
| - gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, fViewMatrix);
|
| + if (fAntiAlias) {
|
| + gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverage, localCoords,
|
| + fViewMatrix);
|
| + } else {
|
| + gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, fViewMatrix);
|
| + }
|
| + }
|
| + if (fAntiAlias) {
|
| + this->drawAA(target, gp.get());
|
| + } else {
|
| + this->draw(target, gp.get());
|
| }
|
| - this->draw(target, gp.get());
|
| }
|
|
|
| void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
|
| int firstVertex, int count) const {
|
| - SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
|
| -
|
| GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
|
| : kTriangles_GrPrimitiveType;
|
| GrMesh mesh;
|
| @@ -228,24 +324,29 @@ private:
|
| TessellatingPathBatch(const GrColor& color,
|
| const GrShape& shape,
|
| const SkMatrix& viewMatrix,
|
| - const SkRect& clipBounds)
|
| + const SkIRect& devClipBounds,
|
| + bool antiAlias)
|
| : INHERITED(ClassID())
|
| , fColor(color)
|
| , fShape(shape)
|
| - , fViewMatrix(viewMatrix) {
|
| - const SkRect& pathBounds = shape.bounds();
|
| - fClipBounds = clipBounds;
|
| - // Because the clip bounds are used to add a contour for inverse fills, they must also
|
| - // include the path bounds.
|
| - fClipBounds.join(pathBounds);
|
| - const SkRect& srcBounds = shape.inverseFilled() ? fClipBounds : pathBounds;
|
| - this->setTransformedBounds(srcBounds, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
|
| + , fViewMatrix(viewMatrix)
|
| + , fDevClipBounds(devClipBounds)
|
| + , fAntiAlias(antiAlias) {
|
| + SkRect devBounds;
|
| + viewMatrix.mapRect(&devBounds, shape.bounds());
|
| + if (shape.inverseFilled()) {
|
| + // Because the clip bounds are used to add a contour for inverse fills, they must also
|
| + // include the path bounds.
|
| + devBounds.join(SkRect::Make(fDevClipBounds));
|
| + }
|
| + this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
|
| }
|
|
|
| GrColor fColor;
|
| GrShape fShape;
|
| SkMatrix fViewMatrix;
|
| - SkRect fClipBounds; // in source space
|
| + SkIRect fDevClipBounds;
|
| + bool fAntiAlias;
|
| GrXPOverridesForBatch fPipelineInfo;
|
|
|
| typedef GrVertexBatch INHERITED;
|
| @@ -254,22 +355,14 @@ private:
|
| bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
|
| GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(),
|
| "GrTessellatingPathRenderer::onDrawPath");
|
| - SkASSERT(!args.fAntiAlias);
|
| -
|
| SkIRect clipBoundsI;
|
| args.fClip->getConservativeBounds(args.fDrawContext->width(), args.fDrawContext->height(),
|
| &clipBoundsI);
|
| - SkRect clipBounds = SkRect::Make(clipBoundsI);
|
| - SkMatrix vmi;
|
| - if (!args.fViewMatrix->invert(&vmi)) {
|
| - return false;
|
| - }
|
| - vmi.mapRect(&clipBounds);
|
| - SkPath path;
|
| - args.fShape->asPath(&path);
|
| SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fPaint->getColor(),
|
| *args.fShape,
|
| - *args.fViewMatrix, clipBounds));
|
| + *args.fViewMatrix,
|
| + clipBoundsI,
|
| + args.fAntiAlias));
|
|
|
| GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fDrawContext->mustUseHWAA(*args.fPaint));
|
| pipelineBuilder.setUserStencil(args.fUserStencilSettings);
|
| @@ -287,19 +380,15 @@ DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) {
|
| GrColor color = GrRandomColor(random);
|
| SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
|
| SkPath path = GrTest::TestPath(random);
|
| - SkRect clipBounds = GrTest::TestRect(random);
|
| - SkMatrix vmi;
|
| - bool result = viewMatrix.invert(&vmi);
|
| - if (!result) {
|
| - SkFAIL("Cannot invert matrix\n");
|
| - }
|
| - vmi.mapRect(&clipBounds);
|
| + SkIRect devClipBounds = SkIRect::MakeXYWH(
|
| + random->nextU(), random->nextU(), random->nextU(), random->nextU());
|
| + bool antiAlias = random->nextBool();
|
| GrStyle style;
|
| do {
|
| GrTest::TestStyle(random, &style);
|
| - } while (style.strokeRec().isHairlineStyle());
|
| + } while (!style.isSimpleFill());
|
| GrShape shape(path, style);
|
| - return TessellatingPathBatch::Create(color, shape, viewMatrix, clipBounds);
|
| + return TessellatingPathBatch::Create(color, shape, viewMatrix, devClipBounds, antiAlias);
|
| }
|
|
|
| #endif
|
|
|