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 |