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