Invalidate textures cached by SW path renderer when paths go away

Bug: skia:
Change-Id: Icfc2f1bd57c0cf7be54469b6d86cbd436b59155d
Reviewed-on: https://skia-review.googlesource.com/51201
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2017-09-26 14:05:19 -04:00 committed by Skia Commit-Bot
parent 832b222efb
commit e9242ca695
2 changed files with 59 additions and 15 deletions

View File

@ -200,6 +200,18 @@ private:
GrAA fAA;
};
// 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);
}
};
}
////////////////////////////////////////////////////////////////////////////////
@ -344,6 +356,7 @@ bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
if (useCache) {
SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
fResourceProvider->assignUniqueKeyToProxy(maskKey, proxy.get());
args.fShape->addGenIDChangeListener(new PathInvalidator(maskKey));
}
}
if (inverseFilled) {

View File

@ -14,6 +14,7 @@
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrResourceCache.h"
#include "GrSoftwarePathRenderer.h"
#include "effects/GrPorterDuffXferProcessor.h"
#include "ops/GrTessellatingPathRenderer.h"
@ -31,8 +32,8 @@ static void draw_path(GrContext* ctx,
GrRenderTargetContext* renderTargetContext,
const SkPath& path,
GrPathRenderer* pr,
GrAAType aaType = GrAAType::kNone,
GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) {
GrAAType aaType,
const GrStyle& style) {
GrPaint paint;
paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
@ -57,14 +58,23 @@ static void draw_path(GrContext* ctx,
pr->drawPath(args);
}
static bool cache_non_scratch_resources_equals(GrResourceCache* cache, int expected) {
#if GR_CACHE_STATS
GrResourceCache::Stats stats;
cache->getStats(&stats);
return (stats.fTotal - stats.fScratch) == expected;
#else
return true;
#endif
}
static void test_path(skiatest::Reporter* reporter,
std::function<SkPath(void)> createPath,
GrPathRenderer* pathRenderer,
int expectedResources = 1,
std::function<GrPathRenderer*(GrContext*)> createPathRenderer,
GrAAType aaType = GrAAType::kNone,
GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) {
sk_sp<GrContext> ctx = GrContext::MakeMock(nullptr);
ctx->setResourceCacheLimits(100, 10000);
ctx->setResourceCacheLimits(100, 100000);
GrResourceCache* cache = ctx->getResourceCache();
sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext(
@ -74,40 +84,61 @@ static void test_path(skiatest::Reporter* reporter,
return;
}
sk_sp<GrPathRenderer> pathRenderer(createPathRenderer(ctx.get()));
SkPath path = createPath();
// Initially, cache only has the render target context
REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 0));
// Draw the path, check that new resource count matches expectations
draw_path(ctx.get(), rtc.get(), path, pathRenderer);
draw_path(ctx.get(), rtc.get(), path, pathRenderer.get(), aaType, style);
ctx->flush();
REPORTER_ASSERT(reporter, (1 + expectedResources) == cache->getResourceCount());
REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 1));
// Nothing should be purgeable yet
cache->purgeAsNeeded();
REPORTER_ASSERT(reporter, (1 + expectedResources) == cache->getResourceCount());
REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 1));
// 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());
REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 0));
}
// Test that deleting the original path invalidates the VBs cached by the tessellating path renderer
DEF_GPUTEST(TessellatingPathRendererCacheTest, reporter, factory) {
GrTessellatingPathRenderer tess;
auto createPR = [](GrContext*) {
return new GrTessellatingPathRenderer();
};
// Tessellating path renderer stores vertex buffers in the cache (for non-AA paths)
test_path(reporter, create_concave_path, &tess);
test_path(reporter, create_concave_path, createPR);
// 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.
// Test with a style that alters the path geometry. 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);
test_path(reporter, create_concave_path, createPR, GrAAType::kNone, style);
}
// Test that deleting the original path invalidates the textures cached by the SW path renderer
DEF_GPUTEST(SoftwarePathRendererCacheTest, reporter, factory) {
auto createPR = [](GrContext* ctx) {
return new GrSoftwarePathRenderer(ctx->resourceProvider(), true);
};
// Tessellating path renderer stores vertex buffers in the cache (for non-AA paths)
test_path(reporter, create_concave_path, createPR, GrAAType::kCoverage);
// Test with a style that alters the path geometry. 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, createPR, GrAAType::kCoverage, style);
}
#endif