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