• 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 "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/GrDirectContext.h"
20 #include "include/gpu/GrRecordingContext.h"
21 #include "include/gpu/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::v1::SurfaceDrawContext * sdc,const SkPath & path,skgpu::v1::PathRenderer * pr,GrAAType aaType,const GrStyle & style,float scaleX=1.f)56 static void draw_path(GrRecordingContext* rContext,
57                       skgpu::v1::SurfaceDrawContext* sdc,
58                       const SkPath& path,
59                       skgpu::v1::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::v1::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,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))97 static void test_path(skiatest::Reporter* reporter,
98                       std::function<SkPath(void)> createPath,
99                       std::function<skgpu::v1::PathRenderer*(GrRecordingContext*)> makePathRenderer,
100                       int expected,
101                       bool checkListeners,
102                       GrAAType aaType = GrAAType::kNone,
103                       GrStyle style = GrStyle(SkStrokeRec::kFill_InitStyle)) {
104     sk_sp<GrDirectContext> dContext = GrDirectContext::MakeMock(nullptr);
105     // The cache needs to be big enough that nothing gets flushed, or our expectations can be wrong
106     dContext->setResourceCacheLimit(8000000);
107     GrResourceCache* cache = dContext->priv().getResourceCache();
108 
109     auto sdc = skgpu::v1::SurfaceDrawContext::Make(
110             dContext.get(), GrColorType::kRGBA_8888, nullptr, SkBackingFit::kApprox, {800, 800},
111             SkSurfaceProps(), /*label=*/{}, 1, GrMipmapped::kNo, GrProtected::kNo,
112             kTopLeft_GrSurfaceOrigin);
113     if (!sdc) {
114         return;
115     }
116 
117     sk_sp<skgpu::v1::PathRenderer> pathRenderer(makePathRenderer(dContext.get()));
118     SkPath path = createPath();
119 
120     // Initially, cache only has the render target context
121     REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, 0));
122 
123     // Draw the path, check that new resource count matches expectations
124     draw_path(dContext.get(), sdc.get(), path, pathRenderer.get(), aaType, style);
125     dContext->flushAndSubmit();
126     REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected));
127 
128     // Nothing should be purgeable yet
129     cache->purgeAsNeeded();
130     REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected));
131 
132     // Reset the path to change the GenID, which should invalidate one resource in the cache.
133     // Some path renderers may leave other unique-keyed resources in the cache, though.
134     path.reset();
135     cache->purgeAsNeeded();
136     REPORTER_ASSERT(reporter, cache_non_scratch_resources_equals(cache, expected - 1));
137 
138     if (!checkListeners) {
139         return;
140     }
141 
142     // Test that purging the cache of masks also removes listeners from the path.
143     path = createPath();
144     REPORTER_ASSERT(reporter, SkPathPriv::GenIDChangeListenersCount(path) == 0);
145     for (int i = 0; i < 20; ++i) {
146         float scaleX = 1 + ((float)i + 1)/20.f;
147         draw_path(dContext.get(), sdc.get(), path, pathRenderer.get(), aaType, style, scaleX);
148     }
149     dContext->flushAndSubmit();
150     REPORTER_ASSERT(reporter, SkPathPriv::GenIDChangeListenersCount(path) == 20);
151     cache->purgeUnlockedResources();
152     // The listeners don't actually purge until we try to add another one.
153     draw_path(dContext.get(), sdc.get(), path, pathRenderer.get(), aaType, style);
154     REPORTER_ASSERT(reporter, SkPathPriv::GenIDChangeListenersCount(path) == 1);
155 }
156 
157 #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
158 // Test that deleting the original path invalidates the VBs cached by the tessellating path renderer
159 DEF_GANESH_TEST(TriangulatingPathRendererCacheTest,
160                 reporter,
161                 /* options */,
162                 CtsEnforcement::kNever) {
__anondf1355cb0102(GrRecordingContext*) 163     auto createPR = [](GrRecordingContext*) {
164         return new skgpu::v1::TriangulatingPathRenderer();
165     };
166 
167     // Triangulating path renderer creates a single vertex buffer for non-AA paths. No other
168     // resources should be created.
169     const int kExpectedResources = 1;
170 
171     test_path(reporter, create_concave_path, createPR, kExpectedResources, false);
172 
173     // Test with a style that alters the path geometry. This needs to attach the invalidation logic
174     // to the original path, not the modified path produced by the style.
175     SkPaint paint;
176     paint.setStyle(SkPaint::kStroke_Style);
177     paint.setStrokeWidth(1);
178     GrStyle style(paint);
179     test_path(reporter, create_concave_path, createPR, kExpectedResources, false, GrAAType::kNone,
180               style);
181 }
182 #endif
183 
184 // Test that deleting the original path invalidates the textures cached by the SW path renderer
185 DEF_GANESH_TEST(SoftwarePathRendererCacheTest,
186                 reporter,
187                 /* options */,
188                 CtsEnforcement::kApiLevel_T) {
__anondf1355cb0202(GrRecordingContext* rContext) 189     auto createPR = [](GrRecordingContext* rContext) {
190         return new skgpu::v1::SoftwarePathRenderer(rContext->priv().proxyProvider(), true);
191     };
192 
193     // Software path renderer creates a mask texture and renders with a non-AA rect, but the flush
194     // only contains a single quad so FillRectOp doesn't need to use the shared index buffer.
195     const int kExpectedResources = 1;
196 
197     test_path(reporter, create_concave_path, createPR, kExpectedResources, true,
198               GrAAType::kCoverage);
199 
200     // Test with a style that alters the path geometry. This needs to attach the invalidation logic
201     // to the original path, not the modified path produced by the style.
202     SkPaint paint;
203     paint.setStyle(SkPaint::kStroke_Style);
204     paint.setStrokeWidth(1);
205     GrStyle style(paint);
206     test_path(reporter, create_concave_path, createPR, kExpectedResources, true,
207               GrAAType::kCoverage, style);
208 }
209