Index: src/gpu/batches/GrTessellatingPathRenderer.cpp |
diff --git a/src/gpu/batches/GrTessellatingPathRenderer.cpp b/src/gpu/batches/GrTessellatingPathRenderer.cpp |
index 3aa7ce8338c659fdc793a2e78ca9ee080832e1f7..b2c3138aed438f9e0979826cdacd121dd8bc5da2 100644 |
--- a/src/gpu/batches/GrTessellatingPathRenderer.cpp |
+++ b/src/gpu/batches/GrTessellatingPathRenderer.cpp |
@@ -12,7 +12,6 @@ |
#include "GrBatchTest.h" |
#include "GrClip.h" |
#include "GrDefaultGeoProcFactory.h" |
-#include "GrDrawTarget.h" |
#include "GrMesh.h" |
#include "GrPathUtils.h" |
#include "GrPipelineBuilder.h" |
@@ -25,12 +24,10 @@ |
#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 can do screenspace |
- * antialiasing with a one-pixel coverage ramp. |
+ * 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. |
*/ |
namespace { |
@@ -67,23 +64,22 @@ |
class StaticVertexAllocator : public GrTessellator::VertexAllocator { |
public: |
- StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB) |
- : VertexAllocator(stride) |
- , fResourceProvider(resourceProvider) |
+ StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB) |
+ : fResourceProvider(resourceProvider) |
, fCanMapVB(canMapVB) |
, fVertices(nullptr) { |
} |
- void* lock(int vertexCount) override { |
- size_t size = vertexCount * stride(); |
+ SkPoint* lock(int vertexCount) override { |
+ size_t size = vertexCount * sizeof(SkPoint); |
fVertexBuffer.reset(fResourceProvider->createBuffer( |
size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0)); |
if (!fVertexBuffer.get()) { |
return nullptr; |
} |
if (fCanMapVB) { |
- fVertices = fVertexBuffer->map(); |
+ fVertices = static_cast<SkPoint*>(fVertexBuffer->map()); |
} else { |
- fVertices = sk_malloc_throw(vertexCount * stride()); |
+ fVertices = new SkPoint[vertexCount]; |
} |
return fVertices; |
} |
@@ -91,8 +87,8 @@ |
if (fCanMapVB) { |
fVertexBuffer->unmap(); |
} else { |
- fVertexBuffer->updateData(fVertices, actualCount * stride()); |
- sk_free(fVertices); |
+ fVertexBuffer->updateData(fVertices, actualCount * sizeof(SkPoint)); |
+ delete[] fVertices; |
} |
fVertices = nullptr; |
} |
@@ -101,66 +97,22 @@ |
SkAutoTUnref<GrBuffer> fVertexBuffer; |
GrResourceProvider* fResourceProvider; |
bool fCanMapVB; |
- void* fVertices; |
+ SkPoint* 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 |
GrTessellatingPathRenderer::GrTessellatingPathRenderer() { |
} |
bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { |
- // 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; |
+ // 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(); |
} |
class TessellatingPathBatch : public GrVertexBatch { |
@@ -170,9 +122,8 @@ |
static GrDrawBatch* Create(const GrColor& color, |
const GrShape& shape, |
const SkMatrix& viewMatrix, |
- SkIRect devClipBounds, |
- bool antiAlias) { |
- return new TessellatingPathBatch(color, shape, viewMatrix, devClipBounds, antiAlias); |
+ SkRect clipBounds) { |
+ return new TessellatingPathBatch(color, shape, viewMatrix, clipBounds); |
} |
const char* name() const override { return "TessellatingPathBatch"; } |
@@ -194,53 +145,41 @@ |
fPipelineInfo = overrides; |
} |
- SkPath getPath() const { |
- SkASSERT(!fShape.style().applies()); |
+ 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 path; |
fShape.asPath(&path); |
- return path; |
- } |
- |
- void draw(Target* target, const GrGeometryProcessor* gp) const { |
- SkASSERT(!fAntiAlias); |
- GrResourceProvider* rp = target->resourceProvider(); |
- bool inverseFill = fShape.inverseFilled(); |
+ bool inverseFill = path.isInverseFillType(); |
// 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(fDevClipBounds) / sizeof(uint32_t); |
+ static constexpr int kClipBoundsCnt = sizeof(fClipBounds) / 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], &fDevClipBounds, sizeof(fDevClipBounds)); |
+ memcpy(&builder[shapeKeyDataCnt], &fClipBounds, sizeof(fClipBounds)); |
} else { |
- memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds)); |
+ memset(&builder[shapeKeyDataCnt], 0, sizeof(fClipBounds)); |
} |
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(gp->getVertexStride(), rp, canMapVB); |
- int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator, |
- false, GrColor(), false, &isLinear); |
+ StaticVertexAllocator allocator(rp, canMapVB); |
+ int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear); |
if (count == 0) { |
return; |
} |
@@ -252,27 +191,6 @@ |
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; |
{ |
@@ -283,35 +201,21 @@ |
LocalCoords::kUsePosition_Type : |
LocalCoords::kUnused_Type); |
Coverage::Type coverageType; |
- if (fAntiAlias) { |
- color = Color(Color::kAttribute_Type); |
- if (fPipelineInfo.canTweakAlphaForCoverage()) { |
- coverageType = Coverage::kSolid_Type; |
- } else { |
- coverageType = Coverage::kAttribute_Type; |
- } |
- } else if (fPipelineInfo.readsCoverage()) { |
+ if (fPipelineInfo.readsCoverage()) { |
coverageType = Coverage::kSolid_Type; |
} else { |
coverageType = Coverage::kNone_Type; |
} |
Coverage coverage(coverageType); |
- 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()); |
- } |
+ gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, fViewMatrix); |
+ } |
+ 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; |
@@ -324,29 +228,24 @@ |
TessellatingPathBatch(const GrColor& color, |
const GrShape& shape, |
const SkMatrix& viewMatrix, |
- const SkIRect& devClipBounds, |
- bool antiAlias) |
+ const SkRect& clipBounds) |
: INHERITED(ClassID()) |
, fColor(color) |
, fShape(shape) |
- , 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); |
+ , 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); |
} |
GrColor fColor; |
GrShape fShape; |
SkMatrix fViewMatrix; |
- SkIRect fDevClipBounds; |
- bool fAntiAlias; |
+ SkRect fClipBounds; // in source space |
GrXPOverridesForBatch fPipelineInfo; |
typedef GrVertexBatch INHERITED; |
@@ -355,14 +254,22 @@ |
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, |
- clipBoundsI, |
- args.fAntiAlias)); |
+ *args.fViewMatrix, clipBounds)); |
GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fDrawContext->mustUseHWAA(*args.fPaint)); |
pipelineBuilder.setUserStencil(args.fUserStencilSettings); |
@@ -380,15 +287,19 @@ |
GrColor color = GrRandomColor(random); |
SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); |
SkPath path = GrTest::TestPath(random); |
- SkIRect devClipBounds = SkIRect::MakeXYWH( |
- random->nextU(), random->nextU(), random->nextU(), random->nextU()); |
- bool antiAlias = random->nextBool(); |
+ SkRect clipBounds = GrTest::TestRect(random); |
+ SkMatrix vmi; |
+ bool result = viewMatrix.invert(&vmi); |
+ if (!result) { |
+ SkFAIL("Cannot invert matrix\n"); |
+ } |
+ vmi.mapRect(&clipBounds); |
GrStyle style; |
do { |
GrTest::TestStyle(random, &style); |
} while (style.strokeRec().isHairlineStyle()); |
GrShape shape(path, style); |
- return TessellatingPathBatch::Create(color, shape, viewMatrix, devClipBounds, antiAlias); |
+ return TessellatingPathBatch::Create(color, shape, viewMatrix, clipBounds); |
} |
#endif |