aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2017-09-25 16:49:55 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-09-26 12:52:36 +0000
commitf6f7cf60985d8e109cc136f25e2a3c3383ca05ea (patch)
tree51fb0a5c7dc44a75b0f89d1436a01b01358e5b88
parent6bba6de94461cdb97a451bc7f88eec3b1ab71079 (diff)
Invalidate path VBs when paths are destroyed
For this to work, we need access to the "original" path, before any style was applied. To that end, add an original path to GrShape (and unit tests of that functionality). Then add a version of addGenIDChangeListener to GrShape, that propagates to the original path, and use that in tessellating path renderer. Includes unit tests of caching behavior in the PR, all of which failed without this change. Bug: skia: Change-Id: I98bb505f521e8ff07184f5c3fbd3c5fd1a22d3d5 Reviewed-on: https://skia-review.googlesource.com/50300 Reviewed-by: Robert Phillips <robertphillips@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
-rw-r--r--gn/tests.gni1
-rw-r--r--src/gpu/GrShape.cpp10
-rw-r--r--src/gpu/GrShape.h17
-rw-r--r--src/gpu/ops/GrTessellatingPathRenderer.cpp1
-rw-r--r--tests/GrShapeTest.cpp11
-rw-r--r--tests/PathRendererCacheTests.cpp113
6 files changed, 150 insertions, 3 deletions
diff --git a/gn/tests.gni b/gn/tests.gni
index 9cea3ea355..389d8769c2 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -156,6 +156,7 @@ tests_sources = [
"$_tests/PDFOpaqueSrcModeToSrcOverTest.cpp",
"$_tests/PDFPrimitivesTest.cpp",
"$_tests/OnFlushCallbackTest.cpp",
+ "$_tests/PathRendererCacheTests.cpp",
"$_tests/PictureBBHTest.cpp",
"$_tests/PictureShaderTest.cpp",
"$_tests/PictureTest.cpp",
diff --git a/src/gpu/GrShape.cpp b/src/gpu/GrShape.cpp
index f7ed18f768..9636427c8e 100644
--- a/src/gpu/GrShape.cpp
+++ b/src/gpu/GrShape.cpp
@@ -9,6 +9,7 @@
GrShape& GrShape::operator=(const GrShape& that) {
fStyle = that.fStyle;
+ fOriginalPath = that.fOriginalPath;
this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
switch (fType) {
case Type::kEmpty:
@@ -66,6 +67,7 @@ GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) {
return original;
}
GrShape result;
+ result.fOriginalPath = original.fOriginalPath;
switch (original.fType) {
case Type::kRRect:
result.fType = original.fType;
@@ -324,7 +326,11 @@ void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkSca
}
}
-GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
+void GrShape::addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) const {
+ SkPathPriv::AddGenIDChangeListener(fOriginalPath, listener);
+}
+
+GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle), fOriginalPath(that.fOriginalPath) {
const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
this->initType(that.fType, thatPath);
switch (fType) {
@@ -380,6 +386,7 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
scale)) {
tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
*this = tmpParent.get()->applyStyle(apply, scale);
+ fOriginalPath = parent.fOriginalPath;
return;
}
// A path effect has access to change the res scale but we aren't expecting it to and it
@@ -430,6 +437,7 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
scale));
fStyle.resetToInitStyle(fillOrHairline);
}
+ fOriginalPath = parent.fOriginalPath;
this->attemptToSimplifyPath();
this->setInheritedKey(*parentForKey, apply, scale);
}
diff --git a/src/gpu/GrShape.h b/src/gpu/GrShape.h
index 2b48eca633..dd8aa850d6 100644
--- a/src/gpu/GrShape.h
+++ b/src/gpu/GrShape.h
@@ -46,7 +46,7 @@ public:
explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
- GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
+ GrShape(const SkPath& path, const GrStyle& style) : fStyle(style), fOriginalPath(path) {
this->initType(Type::kPath, &path);
this->attemptToSimplifyPath();
}
@@ -91,7 +91,7 @@ public:
this->attemptToSimplifyRRect();
}
- GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
+ GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint), fOriginalPath(path) {
this->initType(Type::kPath, &path);
this->attemptToSimplifyPath();
}
@@ -365,6 +365,18 @@ public:
*/
void writeUnstyledKey(uint32_t* key) const;
+ /**
+ * Adds a listener to the *original* path. Typically used to invalidate cached resources when
+ * a path is no longer in-use. If the shape started out as something other than a path, this
+ * does nothing (but will delete the listener).
+ */
+ void addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) const;
+
+ /**
+ * Gets the generation ID of the *original* path. This is only exposed for unit tests.
+ */
+ uint32_t testingOnly_getOriginalGenerationID() const;
+
private:
enum class Type {
@@ -492,6 +504,7 @@ private:
} fLineData;
};
GrStyle fStyle;
+ SkPath fOriginalPath;
SkAutoSTArray<8, uint32_t> fInheritedKey;
};
#endif
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 099cb1151f..13e4af5015 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -280,6 +280,7 @@ private:
info.fCount = count;
key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
+ fShape.addGenIDChangeListener(new PathInvalidator(key));
}
void drawAA(Target* target, const GrGeometryProcessor* gp) {
diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp
index 087ac525c8..2b2a169477 100644
--- a/tests/GrShapeTest.cpp
+++ b/tests/GrShapeTest.cpp
@@ -17,6 +17,10 @@
#include "SkSurface.h"
#include "SkClipOpPriv.h"
+uint32_t GrShape::testingOnly_getOriginalGenerationID() const {
+ return fOriginalPath.getGenerationID();
+}
+
using Key = SkTArray<uint32_t>;
static bool make_key(Key* key, const GrShape& shape) {
@@ -493,6 +497,13 @@ private:
make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
make_key(&fAppliedFullKey, fAppliedFull);
+ // All shapes should report the same "original" path, so that path renderers can get to it
+ // if necessary.
+ uint32_t baseGenID = fBase.testingOnly_getOriginalGenerationID();
+ REPORTER_ASSERT(r, baseGenID == fAppliedPE.testingOnly_getOriginalGenerationID());
+ REPORTER_ASSERT(r, baseGenID == fAppliedPEThenStroke.testingOnly_getOriginalGenerationID());
+ REPORTER_ASSERT(r, baseGenID == fAppliedFull.testingOnly_getOriginalGenerationID());
+
// Applying the path effect and then the stroke should always be the same as applying
// both in one go.
REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
diff --git a/tests/PathRendererCacheTests.cpp b/tests/PathRendererCacheTests.cpp
new file mode 100644
index 0000000000..da58fa4d7d
--- /dev/null
+++ b/tests/PathRendererCacheTests.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#include "SkPath.h"
+
+#if SK_SUPPORT_GPU
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceCache.h"
+#include "effects/GrPorterDuffXferProcessor.h"
+#include "ops/GrTessellatingPathRenderer.h"
+
+static SkPath create_concave_path() {
+ SkPath path;
+ path.moveTo(100, 0);
+ path.lineTo(200, 200);
+ path.lineTo(100, 150);
+ path.lineTo(0, 200);
+ path.close();
+ return path;
+}
+
+static void draw_path(GrContext* ctx,
+ GrRenderTargetContext* renderTargetContext,
+ const SkPath& path,
+ GrPathRenderer* pr,
+ GrAAType aaType = GrAAType::kNone,
+ GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) {
+ GrPaint paint;
+ paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
+
+ GrNoClip noClip;
+ SkIRect clipConservativeBounds = SkIRect::MakeWH(renderTargetContext->width(),
+ renderTargetContext->height());
+ GrShape shape(path, style);
+ if (shape.style().applies()) {
+ shape = shape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, 1.0f);
+ }
+ SkMatrix matrix = SkMatrix::I();
+ GrPathRenderer::DrawPathArgs args{ctx,
+ std::move(paint),
+ &GrUserStencilSettings::kUnused,
+ renderTargetContext,
+ &noClip,
+ &clipConservativeBounds,
+ &matrix,
+ &shape,
+ aaType,
+ false};
+ pr->drawPath(args);
+}
+
+static void test_path(skiatest::Reporter* reporter,
+ std::function<SkPath(void)> createPath,
+ GrPathRenderer* pathRenderer,
+ int expectedResources = 1,
+ GrAAType aaType = GrAAType::kNone,
+ GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) {
+ sk_sp<GrContext> ctx = GrContext::MakeMock(nullptr);
+ ctx->setResourceCacheLimits(100, 10000);
+ GrResourceCache* cache = ctx->getResourceCache();
+
+ sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext(
+ SkBackingFit::kApprox, 800, 800, kRGBA_8888_GrPixelConfig, nullptr, 0,
+ kTopLeft_GrSurfaceOrigin));
+ if (!rtc) {
+ return;
+ }
+
+ SkPath path = createPath();
+
+ // Initially, cache only has the render target context
+ REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+
+ // Draw the path, check that new resource count matches expectations
+ draw_path(ctx.get(), rtc.get(), path, pathRenderer);
+ ctx->flush();
+ REPORTER_ASSERT(reporter, (1 + expectedResources) == cache->getResourceCount());
+
+ // Nothing should be purgeable yet
+ cache->purgeAsNeeded();
+ REPORTER_ASSERT(reporter, (1 + expectedResources) == cache->getResourceCount());
+
+ // Reset the path to change the GenID, which should invalidate any resources in the cache
+ path.reset();
+ cache->purgeAsNeeded();
+ REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+}
+
+// Test that deleting the original path invalidates the VBs cached by the tessellating path renderer
+DEF_GPUTEST(TessellatingPathRendererCacheTest, reporter, factory) {
+ GrTessellatingPathRenderer tess;
+
+ // Tessellating path renderer stores vertex buffers in the cache (for non-AA paths)
+ test_path(reporter, create_concave_path, &tess);
+
+ // Test with a shape that applies. This needs to attach the invalidation logic to the original
+ // path, not the modified path produced by the style.
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(1);
+ GrStyle style(paint);
+ test_path(reporter, create_concave_path, &tess, 1, GrAAType::kNone, style);
+}
+
+#endif