aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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