1/* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "Test.h" 9 10#include "SkPath.h" 11 12#if SK_SUPPORT_GPU 13#include "GrClip.h" 14#include "GrContext.h" 15#include "GrContextPriv.h" 16#include "GrResourceCache.h" 17#include "GrSoftwarePathRenderer.h" 18#include "effects/GrPorterDuffXferProcessor.h" 19#include "ops/GrTessellatingPathRenderer.h" 20 21static SkPath create_concave_path() { 22 SkPath path; 23 path.moveTo(100, 0); 24 path.lineTo(200, 200); 25 path.lineTo(100, 150); 26 path.lineTo(0, 200); 27 path.close(); 28 return path; 29} 30 31static void draw_path(GrContext* ctx, 32 GrRenderTargetContext* renderTargetContext, 33 const SkPath& path, 34 GrPathRenderer* pr, 35 GrAAType aaType, 36 const GrStyle& style) { 37 GrPaint paint; 38 paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); 39 40 GrNoClip noClip; 41 SkIRect clipConservativeBounds = SkIRect::MakeWH(renderTargetContext->width(), 42 renderTargetContext->height()); 43 GrShape shape(path, style); 44 if (shape.style().applies()) { 45 shape = shape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, 1.0f); 46 } 47 SkMatrix matrix = SkMatrix::I(); 48 GrPathRenderer::DrawPathArgs args{ctx, 49 std::move(paint), 50 &GrUserStencilSettings::kUnused, 51 renderTargetContext, 52 &noClip, 53 &clipConservativeBounds, 54 &matrix, 55 &shape, 56 aaType, 57 false}; 58 pr->drawPath(args); 59} 60 61static bool cache_non_scratch_resources_equals(GrResourceCache* cache, int expected) { 62#if GR_CACHE_STATS 63 GrResourceCache::Stats stats; 64 cache->getStats(&stats); 65 return (stats.fTotal - stats.fScratch) == expected; 66#else 67 return true; 68#endif 69} 70 71static void test_path(skiatest::Reporter* reporter, 72 std::function<SkPath(void)> createPath, 73 std::function<GrPathRenderer*(GrContext*)> createPathRenderer, 74 int expected, 75 GrAAType aaType = GrAAType::kNone, 76 GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) { 77 sk_sp<GrContext> ctx = GrContext::MakeMock(nullptr); 78 // The cache needs to be big enough that nothing gets flushed, or our expectations can be wrong 79 ctx->setResourceCacheLimits(100, 8000000); 80 GrResourceCache* cache = ctx->contextPriv().getResourceCache(); 81 82 sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext( 83 SkBackingFit::kApprox, 800, 800, kRGBA_8888_GrPixelConfig, nullptr, 1, GrMipMapped::kNo, 84 kTopLeft_GrSurfaceOrigin)); 85 if (!rtc) { 86 return; 87 } 88 89 sk_sp<GrPathRenderer> pathRenderer(createPathRenderer(ctx.get())); 90 SkPath path = createPath(); 91 92 // Initially, cache only has the render target context 93 REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 0)); 94 95 // Draw the path, check that new resource count matches expectations 96 draw_path(ctx.get(), rtc.get(), path, pathRenderer.get(), aaType, style); 97 ctx->flush(); 98 REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected)); 99 100 // Nothing should be purgeable yet 101 cache->purgeAsNeeded(); 102 REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected)); 103 104 // Reset the path to change the GenID, which should invalidate one resource in the cache. 105 // Some path renderers may leave other unique-keyed resources in the cache, though. 106 path.reset(); 107 cache->purgeAsNeeded(); 108 REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected - 1)); 109} 110 111// Test that deleting the original path invalidates the VBs cached by the tessellating path renderer 112DEF_GPUTEST(TessellatingPathRendererCacheTest, reporter, /* options */) { 113 auto createPR = [](GrContext*) { 114 return new GrTessellatingPathRenderer(); 115 }; 116 117 // Tessellating path renderer creates a single vertex buffer for non-AA paths. No other 118 // resources should be created. 119 const int kExpectedResources = 1; 120 121 test_path(reporter, create_concave_path, createPR, kExpectedResources); 122 123 // Test with a style that alters the path geometry. This needs to attach the invalidation logic 124 // to the original path, not the modified path produced by the style. 125 SkPaint paint; 126 paint.setStyle(SkPaint::kStroke_Style); 127 paint.setStrokeWidth(1); 128 GrStyle style(paint); 129 test_path(reporter, create_concave_path, createPR, kExpectedResources, GrAAType::kNone, style); 130} 131 132// Test that deleting the original path invalidates the textures cached by the SW path renderer 133DEF_GPUTEST(SoftwarePathRendererCacheTest, reporter, /* options */) { 134 auto createPR = [](GrContext* ctx) { 135 return new GrSoftwarePathRenderer(ctx->contextPriv().proxyProvider(), true); 136 }; 137 138 // Software path renderer creates a mask texture, but also renders with a non-AA rect, which 139 // refs the quad index buffer. 140 const int kExpectedResources = 2; 141 142 test_path(reporter, create_concave_path, createPR, kExpectedResources, GrAAType::kCoverage); 143 144 // Test with a style that alters the path geometry. This needs to attach the invalidation logic 145 // to the original path, not the modified path produced by the style. 146 SkPaint paint; 147 paint.setStyle(SkPaint::kStroke_Style); 148 paint.setStrokeWidth(1); 149 GrStyle style(paint); 150 test_path(reporter, create_concave_path, createPR, kExpectedResources, GrAAType::kCoverage, 151 style); 152} 153 154#endif 155