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; 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) { if (useCache) {
SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
fResourceProvider->assignUniqueKeyToProxy(maskKey, proxy.get()); fResourceProvider->assignUniqueKeyToProxy(maskKey, proxy.get());
args.fShape->addGenIDChangeListener(new PathInvalidator(maskKey));
} }
} }
if (inverseFilled) { if (inverseFilled) {

View File

@ -14,6 +14,7 @@
#include "GrContext.h" #include "GrContext.h"
#include "GrContextPriv.h" #include "GrContextPriv.h"
#include "GrResourceCache.h" #include "GrResourceCache.h"
#include "GrSoftwarePathRenderer.h"
#include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrPorterDuffXferProcessor.h"
#include "ops/GrTessellatingPathRenderer.h" #include "ops/GrTessellatingPathRenderer.h"
@ -31,8 +32,8 @@ static void draw_path(GrContext* ctx,
GrRenderTargetContext* renderTargetContext, GrRenderTargetContext* renderTargetContext,
const SkPath& path, const SkPath& path,
GrPathRenderer* pr, GrPathRenderer* pr,
GrAAType aaType = GrAAType::kNone, GrAAType aaType,
GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) { const GrStyle& style) {
GrPaint paint; GrPaint paint;
paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
@ -57,14 +58,23 @@ static void draw_path(GrContext* ctx,
pr->drawPath(args); 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, static void test_path(skiatest::Reporter* reporter,
std::function<SkPath(void)> createPath, std::function<SkPath(void)> createPath,
GrPathRenderer* pathRenderer, std::function<GrPathRenderer*(GrContext*)> createPathRenderer,
int expectedResources = 1,
GrAAType aaType = GrAAType::kNone, GrAAType aaType = GrAAType::kNone,
GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) { GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) {
sk_sp<GrContext> ctx = GrContext::MakeMock(nullptr); sk_sp<GrContext> ctx = GrContext::MakeMock(nullptr);
ctx->setResourceCacheLimits(100, 10000); ctx->setResourceCacheLimits(100, 100000);
GrResourceCache* cache = ctx->getResourceCache(); GrResourceCache* cache = ctx->getResourceCache();
sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext( sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext(
@ -74,40 +84,61 @@ static void test_path(skiatest::Reporter* reporter,
return; return;
} }
sk_sp<GrPathRenderer> pathRenderer(createPathRenderer(ctx.get()));
SkPath path = createPath(); SkPath path = createPath();
// Initially, cache only has the render target context // 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 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(); ctx->flush();
REPORTER_ASSERT(reporter, (1 + expectedResources) == cache->getResourceCount()); REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 1));
// Nothing should be purgeable yet // Nothing should be purgeable yet
cache->purgeAsNeeded(); 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 // Reset the path to change the GenID, which should invalidate any resources in the cache
path.reset(); path.reset();
cache->purgeAsNeeded(); 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 // Test that deleting the original path invalidates the VBs cached by the tessellating path renderer
DEF_GPUTEST(TessellatingPathRendererCacheTest, reporter, factory) { 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) // 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 // Test with a style that alters the path geometry. This needs to attach the invalidation logic
// path, not the modified path produced by the style. // to the original path, not the modified path produced by the style.
SkPaint paint; SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style); paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(1); paint.setStrokeWidth(1);
GrStyle style(paint); 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 #endif