Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(309)

Unified Diff: src/gpu/GrTessellatingPathRenderer.cpp

Issue 1114353004: Implement vertex buffer caching in the tessellated path renderer. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Integrated cache invalidation based on SkPath GenID change Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/gpu/GrTessellatingPathRenderer.cpp
diff --git a/src/gpu/GrTessellatingPathRenderer.cpp b/src/gpu/GrTessellatingPathRenderer.cpp
index f420372ca516c708c89d1ea6152ea02b94903f11..1245a44720ce67ac6ba0f01fb68445c804f27f95 100644
--- a/src/gpu/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/GrTessellatingPathRenderer.cpp
@@ -13,6 +13,8 @@
#include "GrDefaultGeoProcFactory.h"
#include "GrPathUtils.h"
#include "GrVertices.h"
+#include "GrResourceCache.h"
+#include "GrResourceProvider.h"
#include "SkChunkAlloc.h"
#include "SkGeometry.h"
@@ -538,12 +540,13 @@ Vertex* generate_cubic_points(const SkPoint& p0,
// Stage 1: convert the input path to a set of linear contours (linked list of Vertices).
void path_to_contours(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
- Vertex** contours, SkChunkAlloc& alloc) {
+ Vertex** contours, SkChunkAlloc& alloc, bool *isLinear) {
SkScalar toleranceSqd = tolerance * tolerance;
SkPoint pts[4];
bool done = false;
+ *isLinear = true;
SkPath::Iter iter(path, false);
Vertex* prev = NULL;
Vertex* head = NULL;
@@ -571,6 +574,7 @@ void path_to_contours(const SkPath& path, SkScalar tolerance, const SkRect& clip
toleranceSqd, prev, &head, pointsLeft, alloc);
quadPts += 2;
}
+ *isLinear = false;
break;
}
case SkPath::kMove_Verb:
@@ -590,12 +594,14 @@ void path_to_contours(const SkPath& path, SkScalar tolerance, const SkRect& clip
int pointsLeft = GrPathUtils::quadraticPointCount(pts, tolerance);
prev = generate_quadratic_points(pts[0], pts[1], pts[2], toleranceSqd, prev,
&head, pointsLeft, alloc);
+ *isLinear = false;
break;
}
case SkPath::kCubic_Verb: {
int pointsLeft = GrPathUtils::cubicPointCount(pts, tolerance);
prev = generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
toleranceSqd, prev, &head, pointsLeft, alloc);
+ *isLinear = false;
break;
}
case SkPath::kClose_Verb:
@@ -1329,9 +1335,30 @@ SkPoint* polys_to_triangles(Poly* polys, SkPath::FillType fillType, SkPoint* dat
return d;
}
+bool cacheMatch(GrVertexBuffer* vertexBuffer, const SkMatrix& newMatrix) {
+ if (!vertexBuffer) {
+ return false;
+ }
+ const SkData* data = vertexBuffer->getCustomData();
+ if (!data || !data->data()) { // path is linear; matches any scale
+ return true;
+ }
+ const SkSize* cachedScale = static_cast<const SkSize*>(data->data());
bsalomon 2015/07/28 14:55:48 Rather than use the data on the VB, can we canonic
Stephen White 2015/07/28 15:10:28 I could do that, but then a single path with an an
bsalomon 2015/07/28 21:23:30 That makes sense. I wonder if there is an efficien
Stephen White 2015/07/28 22:11:23 I've done the latter (I think..)
+ SkASSERT(cachedScale);
+ SkSize newScale;
+ if (!newMatrix.decomposeScale(&newScale)) {
+ return false;
+ }
+ if (newScale.width() > 0.01f * cachedScale->width() && newScale.width() < 3.0f * cachedScale->width()
+ && newScale.height() > 0.01f * cachedScale->height() && newScale.height() < 3.0f * cachedScale->height()) {
+ return true;
+ }
+ return false;
+}
+
};
-GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
+GrTessellatingPathRenderer::GrTessellatingPathRenderer(GrContext* context) : fContext(context) {
}
GrPathRenderer::StencilSupport GrTessellatingPathRenderer::onGetStencilSupport(
@@ -1342,6 +1369,23 @@ GrPathRenderer::StencilSupport GrTessellatingPathRenderer::onGetStencilSupport(
return GrPathRenderer::kNoSupport_StencilSupport;
}
+namespace {
+
+// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
+class PathInvalidator : public SkPathRef::GenIDChangeListener {
+public:
+ explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
+private:
+ GrUniqueKeyInvalidatedMessage fMsg;
+
+ void onChange() override {
+ SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
+ }
+};
+
+} // namespace
+
+
bool GrTessellatingPathRenderer::canDrawPath(const GrDrawTarget* target,
const GrPipelineBuilder* pipelineBuilder,
const SkMatrix& viewMatrix,
@@ -1359,8 +1403,9 @@ public:
static GrBatch* Create(const GrColor& color,
const SkPath& path,
const SkMatrix& viewMatrix,
- SkRect clipBounds) {
- return SkNEW_ARGS(TessellatingPathBatch, (color, path, viewMatrix, clipBounds));
+ SkRect clipBounds,
+ GrContext* context) {
+ return SkNEW_ARGS(TessellatingPathBatch, (color, path, viewMatrix, clipBounds, context));
}
const char* name() const override { return "TessellatingPathBatch"; }
@@ -1382,7 +1427,7 @@ public:
fPipelineInfo = init;
}
- void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+ int tessellate(const GrUniqueKey& key, SkAutoTUnref<GrVertexBuffer>& vertexBuffer) {
SkRect pathBounds = fPath.getBounds();
Comparator c;
if (pathBounds.width() > pathBounds.height()) {
@@ -1397,11 +1442,11 @@ public:
int contourCnt;
int maxPts = GrPathUtils::worstCasePointCount(fPath, &contourCnt, tol);
if (maxPts <= 0) {
- return;
+ return 0;
}
if (maxPts > ((int)SK_MaxU16 + 1)) {
SkDebugf("Path not rendered, too many verts (%d)\n", maxPts);
- return;
+ return 0;
}
SkPath::FillType fillType = fPath.getFillType();
if (SkPath::IsInverseFillType(fillType)) {
@@ -1409,13 +1454,6 @@ public:
}
LOG("got %d pts, %d contours\n", maxPts, contourCnt);
- uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType;
- SkAutoTUnref<const GrGeometryProcessor> gp(
- GrDefaultGeoProcFactory::Create(flags, fColor, fPipelineInfo.readsLocalCoords(),
- !fPipelineInfo.readsCoverage(), fViewMatrix,
- SkMatrix::I()));
- batchTarget->initDraw(gp, pipeline);
-
SkAutoTDeleteArray<Vertex*> contours(SkNEW_ARRAY(Vertex *, contourCnt));
// For the initial size of the chunk allocator, estimate based on the point count:
@@ -1423,7 +1461,8 @@ public:
// resulting Polys, since the same point may end up in two Polys. Assume minimal
// connectivity of one Edge per Vertex (will grow for intersections).
SkChunkAlloc alloc(maxPts * (3 * sizeof(Vertex) + sizeof(Edge)));
- path_to_contours(fPath, tol, fClipBounds, contours.get(), alloc);
+ bool isLinear;
+ path_to_contours(fPath, tol, fClipBounds, contours.get(), alloc, &isLinear);
Poly* polys;
polys = contours_to_polys(contours.get(), contourCnt, c, alloc);
int count = 0;
@@ -1433,34 +1472,65 @@ public:
}
}
if (0 == count) {
- return;
+ return 0;
}
- size_t stride = gp->getVertexStride();
- SkASSERT(stride == sizeof(SkPoint));
- const GrVertexBuffer* vertexBuffer;
- int firstVertex;
- SkPoint* verts = static_cast<SkPoint*>(
- batchTarget->makeVertSpace(stride, count, &vertexBuffer, &firstVertex));
- if (!verts) {
+ vertexBuffer.reset(fContext->getGpu()->createVertexBuffer(count * sizeof(SkPoint), false));
+ if (!vertexBuffer.get()) {
SkDebugf("Could not allocate vertices\n");
- return;
+ return 0;
}
-
+ SkPoint* verts = static_cast<SkPoint*>(vertexBuffer->map());
LOG("emitting %d verts\n", count);
SkPoint* end = polys_to_triangles(polys, fillType, verts);
+ vertexBuffer->unmap();
int actualCount = static_cast<int>(end - verts);
LOG("actual count: %d\n", actualCount);
SkASSERT(actualCount <= count);
+ if (!fPath.isVolatile()) {
+ SkSize scale;
+ if (!isLinear && fViewMatrix.decomposeScale(&scale)) {
+ vertexBuffer->setCustomData(SkData::NewWithCopy(&scale, sizeof(scale)));
+ }
+ fContext->resourceProvider()->assignUniqueKeyToResource(key, vertexBuffer.get());
+ fPath.pathRef()->addGenIDChangeListener(SkNEW(PathInvalidator(key)));
+ }
+ return actualCount;
+ }
+
+ void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+ // construct a cache key from the path's genID and the view matrix
+ static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+ GrUniqueKey key;
+ GrUniqueKey::Builder builder(&key, kDomain, 1);
+ builder[0] = fPath.getGenerationID();
+ builder.finish();
+ SkAutoTUnref<GrVertexBuffer> vertexBuffer(fContext->resourceProvider()->findAndRefTByUniqueKey<GrVertexBuffer>(key));
+ int actualCount;
+ if (cacheMatch(vertexBuffer.get(), fViewMatrix)) {
+ actualCount = vertexBuffer->gpuMemorySize() / sizeof(SkPoint);
+ } else {
+ actualCount = tessellate(key, vertexBuffer);
+ }
+
+ if (actualCount == 0) {
+ return;
+ }
+
+ uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType;
+ SkAutoTUnref<const GrGeometryProcessor> gp(
+ GrDefaultGeoProcFactory::Create(flags, fColor, fPipelineInfo.readsLocalCoords(),
+ !fPipelineInfo.readsCoverage(), fViewMatrix,
+ SkMatrix::I()));
+ batchTarget->initDraw(gp, pipeline);
+ SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
+
GrPrimitiveType primitiveType = WIREFRAME ? kLines_GrPrimitiveType
: kTriangles_GrPrimitiveType;
GrVertices vertices;
- vertices.init(primitiveType, vertexBuffer, firstVertex, actualCount);
+ vertices.init(primitiveType, vertexBuffer.get(), 0, actualCount);
batchTarget->draw(vertices);
-
- batchTarget->putBackVertices((size_t)(count - actualCount), stride);
- return;
}
bool onCombineIfPossible(GrBatch*) override {
@@ -1471,11 +1541,13 @@ private:
TessellatingPathBatch(const GrColor& color,
const SkPath& path,
const SkMatrix& viewMatrix,
- const SkRect& clipBounds)
+ const SkRect& clipBounds,
+ GrContext* context)
: fColor(color)
, fPath(path)
, fViewMatrix(viewMatrix)
- , fClipBounds(clipBounds) {
+ , fClipBounds(clipBounds)
+ , fContext(context) {
this->initClassID<TessellatingPathBatch>();
fBounds = path.getBounds();
@@ -1486,6 +1558,7 @@ private:
SkPath fPath;
SkMatrix fViewMatrix;
SkRect fClipBounds; // in source space
+ GrContext* fContext;
GrPipelineInfo fPipelineInfo;
};
@@ -1510,7 +1583,7 @@ bool GrTessellatingPathRenderer::onDrawPath(GrDrawTarget* target,
return false;
}
vmi.mapRect(&clipBounds);
- SkAutoTUnref<GrBatch> batch(TessellatingPathBatch::Create(color, path, viewM, clipBounds));
+ SkAutoTUnref<GrBatch> batch(TessellatingPathBatch::Create(color, path, viewM, clipBounds, fContext));
target->drawBatch(*pipelineBuilder, batch);
return true;
@@ -1531,7 +1604,7 @@ BATCH_TEST_DEFINE(TesselatingPathBatch) {
SkFAIL("Cannot invert matrix\n");
}
vmi.mapRect(&clipBounds);
- return TessellatingPathBatch::Create(color, path, viewMatrix, clipBounds);
+ return TessellatingPathBatch::Create(color, path, viewMatrix, clipBounds, context);
}
#endif

Powered by Google App Engine
This is Rietveld 408576698