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