1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2017 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "tests/Test.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkPath.h"
11cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
12cb93a386Sopenharmony_ci#include "include/gpu/GrRecordingContext.h"
13cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h"
14cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h"
15cb93a386Sopenharmony_ci#include "src/gpu/GrResourceCache.h"
16cb93a386Sopenharmony_ci#include "src/gpu/GrStyle.h"
17cb93a386Sopenharmony_ci#include "src/gpu/effects/GrPorterDuffXferProcessor.h"
18cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrStyledShape.h"
19cb93a386Sopenharmony_ci#include "src/gpu/ops/SoftwarePathRenderer.h"
20cb93a386Sopenharmony_ci#include "src/gpu/ops/TriangulatingPathRenderer.h"
21cb93a386Sopenharmony_ci#include "src/gpu/v1/SurfaceDrawContext_v1.h"
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_cistatic SkPath create_concave_path() {
24cb93a386Sopenharmony_ci    SkPath path;
25cb93a386Sopenharmony_ci    path.moveTo(100, 0);
26cb93a386Sopenharmony_ci    path.lineTo(200, 200);
27cb93a386Sopenharmony_ci    path.lineTo(100, 150);
28cb93a386Sopenharmony_ci    path.lineTo(0, 200);
29cb93a386Sopenharmony_ci    path.close();
30cb93a386Sopenharmony_ci    return path;
31cb93a386Sopenharmony_ci}
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_cistatic void draw_path(GrRecordingContext* rContext,
34cb93a386Sopenharmony_ci                      skgpu::v1::SurfaceDrawContext* sdc,
35cb93a386Sopenharmony_ci                      const SkPath& path,
36cb93a386Sopenharmony_ci                      skgpu::v1::PathRenderer* pr,
37cb93a386Sopenharmony_ci                      GrAAType aaType,
38cb93a386Sopenharmony_ci                      const GrStyle& style,
39cb93a386Sopenharmony_ci                      float scaleX = 1.f) {
40cb93a386Sopenharmony_ci    GrPaint paint;
41cb93a386Sopenharmony_ci    paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_ci    SkIRect clipConservativeBounds = SkIRect::MakeWH(sdc->width(),
44cb93a386Sopenharmony_ci                                                     sdc->height());
45cb93a386Sopenharmony_ci    GrStyledShape shape(path, style);
46cb93a386Sopenharmony_ci    if (shape.style().applies()) {
47cb93a386Sopenharmony_ci        shape = shape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, 1.0f);
48cb93a386Sopenharmony_ci    }
49cb93a386Sopenharmony_ci    SkMatrix matrix = SkMatrix::I();
50cb93a386Sopenharmony_ci    matrix.setScaleX(scaleX);
51cb93a386Sopenharmony_ci    skgpu::v1::PathRenderer::DrawPathArgs args{rContext,
52cb93a386Sopenharmony_ci                                               std::move(paint),
53cb93a386Sopenharmony_ci                                               &GrUserStencilSettings::kUnused,
54cb93a386Sopenharmony_ci                                               sdc,
55cb93a386Sopenharmony_ci                                               nullptr,
56cb93a386Sopenharmony_ci                                               &clipConservativeBounds,
57cb93a386Sopenharmony_ci                                               &matrix,
58cb93a386Sopenharmony_ci                                               &shape,
59cb93a386Sopenharmony_ci                                               aaType,
60cb93a386Sopenharmony_ci                                               false};
61cb93a386Sopenharmony_ci    pr->drawPath(args);
62cb93a386Sopenharmony_ci}
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_cistatic bool cache_non_scratch_resources_equals(GrResourceCache* cache, int expected) {
65cb93a386Sopenharmony_ci#if GR_CACHE_STATS
66cb93a386Sopenharmony_ci    GrResourceCache::Stats stats;
67cb93a386Sopenharmony_ci    cache->getStats(&stats);
68cb93a386Sopenharmony_ci    return (stats.fTotal - stats.fScratch) == expected;
69cb93a386Sopenharmony_ci#else
70cb93a386Sopenharmony_ci    return true;
71cb93a386Sopenharmony_ci#endif
72cb93a386Sopenharmony_ci}
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_cistatic void test_path(skiatest::Reporter* reporter,
75cb93a386Sopenharmony_ci                      std::function<SkPath(void)> createPath,
76cb93a386Sopenharmony_ci                      std::function<skgpu::v1::PathRenderer*(GrRecordingContext*)> makePathRenderer,
77cb93a386Sopenharmony_ci                      int expected,
78cb93a386Sopenharmony_ci                      bool checkListeners,
79cb93a386Sopenharmony_ci                      GrAAType aaType = GrAAType::kNone,
80cb93a386Sopenharmony_ci                      GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) {
81cb93a386Sopenharmony_ci    sk_sp<GrDirectContext> dContext = GrDirectContext::MakeMock(nullptr);
82cb93a386Sopenharmony_ci    // The cache needs to be big enough that nothing gets flushed, or our expectations can be wrong
83cb93a386Sopenharmony_ci    dContext->setResourceCacheLimit(8000000);
84cb93a386Sopenharmony_ci    GrResourceCache* cache = dContext->priv().getResourceCache();
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_ci    auto sdc = skgpu::v1::SurfaceDrawContext::Make(
87cb93a386Sopenharmony_ci            dContext.get(), GrColorType::kRGBA_8888, nullptr, SkBackingFit::kApprox, {800, 800},
88cb93a386Sopenharmony_ci            SkSurfaceProps(), 1, GrMipmapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
89cb93a386Sopenharmony_ci    if (!sdc) {
90cb93a386Sopenharmony_ci        return;
91cb93a386Sopenharmony_ci    }
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_ci    sk_sp<skgpu::v1::PathRenderer> pathRenderer(makePathRenderer(dContext.get()));
94cb93a386Sopenharmony_ci    SkPath path = createPath();
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ci    // Initially, cache only has the render target context
97cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 0));
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci    // Draw the path, check that new resource count matches expectations
100cb93a386Sopenharmony_ci    draw_path(dContext.get(), sdc.get(), path, pathRenderer.get(), aaType, style);
101cb93a386Sopenharmony_ci    dContext->flushAndSubmit();
102cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected));
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ci    // Nothing should be purgeable yet
105cb93a386Sopenharmony_ci    cache->purgeAsNeeded();
106cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected));
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ci    // Reset the path to change the GenID, which should invalidate one resource in the cache.
109cb93a386Sopenharmony_ci    // Some path renderers may leave other unique-keyed resources in the cache, though.
110cb93a386Sopenharmony_ci    path.reset();
111cb93a386Sopenharmony_ci    cache->purgeAsNeeded();
112cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected - 1));
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    if (!checkListeners) {
115cb93a386Sopenharmony_ci        return;
116cb93a386Sopenharmony_ci    }
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_ci    // Test that purging the cache of masks also removes listeners from the path.
119cb93a386Sopenharmony_ci    path = createPath();
120cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkPathPriv::GenIDChangeListenersCount(path) == 0);
121cb93a386Sopenharmony_ci    for (int i = 0; i < 20; ++i) {
122cb93a386Sopenharmony_ci        float scaleX = 1 + ((float)i + 1)/20.f;
123cb93a386Sopenharmony_ci        draw_path(dContext.get(), sdc.get(), path, pathRenderer.get(), aaType, style, scaleX);
124cb93a386Sopenharmony_ci    }
125cb93a386Sopenharmony_ci    dContext->flushAndSubmit();
126cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkPathPriv::GenIDChangeListenersCount(path) == 20);
127cb93a386Sopenharmony_ci    cache->purgeUnlockedResources();
128cb93a386Sopenharmony_ci    // The listeners don't actually purge until we try to add another one.
129cb93a386Sopenharmony_ci    draw_path(dContext.get(), sdc.get(), path, pathRenderer.get(), aaType, style);
130cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkPathPriv::GenIDChangeListenersCount(path) == 1);
131cb93a386Sopenharmony_ci}
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ci// Test that deleting the original path invalidates the VBs cached by the tessellating path renderer
134cb93a386Sopenharmony_ciDEF_GPUTEST(TriangulatingPathRendererCacheTest, reporter, /* options */) {
135cb93a386Sopenharmony_ci    auto createPR = [](GrRecordingContext*) {
136cb93a386Sopenharmony_ci        return new skgpu::v1::TriangulatingPathRenderer();
137cb93a386Sopenharmony_ci    };
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci    // Triangulating path renderer creates a single vertex buffer for non-AA paths. No other
140cb93a386Sopenharmony_ci    // resources should be created.
141cb93a386Sopenharmony_ci    const int kExpectedResources = 1;
142cb93a386Sopenharmony_ci
143cb93a386Sopenharmony_ci    test_path(reporter, create_concave_path, createPR, kExpectedResources, false);
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_ci    // Test with a style that alters the path geometry. This needs to attach the invalidation logic
146cb93a386Sopenharmony_ci    // to the original path, not the modified path produced by the style.
147cb93a386Sopenharmony_ci    SkPaint paint;
148cb93a386Sopenharmony_ci    paint.setStyle(SkPaint::kStroke_Style);
149cb93a386Sopenharmony_ci    paint.setStrokeWidth(1);
150cb93a386Sopenharmony_ci    GrStyle style(paint);
151cb93a386Sopenharmony_ci    test_path(reporter, create_concave_path, createPR, kExpectedResources, false, GrAAType::kNone,
152cb93a386Sopenharmony_ci              style);
153cb93a386Sopenharmony_ci}
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci// Test that deleting the original path invalidates the textures cached by the SW path renderer
156cb93a386Sopenharmony_ciDEF_GPUTEST(SoftwarePathRendererCacheTest, reporter, /* options */) {
157cb93a386Sopenharmony_ci    auto createPR = [](GrRecordingContext* rContext) {
158cb93a386Sopenharmony_ci        return new skgpu::v1::SoftwarePathRenderer(rContext->priv().proxyProvider(), true);
159cb93a386Sopenharmony_ci    };
160cb93a386Sopenharmony_ci
161cb93a386Sopenharmony_ci    // Software path renderer creates a mask texture and renders with a non-AA rect, but the flush
162cb93a386Sopenharmony_ci    // only contains a single quad so FillRectOp doesn't need to use the shared index buffer.
163cb93a386Sopenharmony_ci    const int kExpectedResources = 1;
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci    test_path(reporter, create_concave_path, createPR, kExpectedResources, true,
166cb93a386Sopenharmony_ci              GrAAType::kCoverage);
167cb93a386Sopenharmony_ci
168cb93a386Sopenharmony_ci    // Test with a style that alters the path geometry. This needs to attach the invalidation logic
169cb93a386Sopenharmony_ci    // to the original path, not the modified path produced by the style.
170cb93a386Sopenharmony_ci    SkPaint paint;
171cb93a386Sopenharmony_ci    paint.setStyle(SkPaint::kStroke_Style);
172cb93a386Sopenharmony_ci    paint.setStrokeWidth(1);
173cb93a386Sopenharmony_ci    GrStyle style(paint);
174cb93a386Sopenharmony_ci    test_path(reporter, create_concave_path, createPR, kExpectedResources, true,
175cb93a386Sopenharmony_ci              GrAAType::kCoverage, style);
176cb93a386Sopenharmony_ci}
177