1 /*
2  * Copyright 2015 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 "GrTessellatingPathRenderer.h"
9 
10 #include "GrAuditTrail.h"
11 #include "GrClip.h"
12 #include "GrDefaultGeoProcFactory.h"
13 #include "GrDrawOpTest.h"
14 #include "GrMesh.h"
15 #include "GrOpFlushState.h"
16 #include "GrPathUtils.h"
17 #include "GrPipelineBuilder.h"
18 #include "GrResourceCache.h"
19 #include "GrResourceProvider.h"
20 #include "GrTessellator.h"
21 #include "SkGeometry.h"
22 
23 #include "ops/GrMeshDrawOp.h"
24 
25 #include <stdio.h>
26 
27 /*
28  * This path renderer tessellates the path into triangles using GrTessellator, uploads the
29  * triangles to a vertex buffer, and renders them with a single draw call. It can do screenspace
30  * antialiasing with a one-pixel coverage ramp.
31  */
32 namespace {
33 
34 struct TessInfo {
35     SkScalar  fTolerance;
36     int       fCount;
37 };
38 
39 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
40 class PathInvalidator : public SkPathRef::GenIDChangeListener {
41 public:
PathInvalidator(const GrUniqueKey & key)42     explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
43 private:
44     GrUniqueKeyInvalidatedMessage fMsg;
45 
onChange()46     void onChange() override {
47         SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
48     }
49 };
50 
cache_match(GrBuffer * vertexBuffer,SkScalar tol,int * actualCount)51 bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
52     if (!vertexBuffer) {
53         return false;
54     }
55     const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
56     SkASSERT(data);
57     const TessInfo* info = static_cast<const TessInfo*>(data->data());
58     if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
59         *actualCount = info->fCount;
60         return true;
61     }
62     return false;
63 }
64 
65 class StaticVertexAllocator : public GrTessellator::VertexAllocator {
66 public:
StaticVertexAllocator(size_t stride,GrResourceProvider * resourceProvider,bool canMapVB)67     StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
68       : VertexAllocator(stride)
69       , fResourceProvider(resourceProvider)
70       , fCanMapVB(canMapVB)
71       , fVertices(nullptr) {
72     }
lock(int vertexCount)73     void* lock(int vertexCount) override {
74         size_t size = vertexCount * stride();
75         fVertexBuffer.reset(fResourceProvider->createBuffer(
76             size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0));
77         if (!fVertexBuffer.get()) {
78             return nullptr;
79         }
80         if (fCanMapVB) {
81             fVertices = fVertexBuffer->map();
82         } else {
83             fVertices = sk_malloc_throw(vertexCount * stride());
84         }
85         return fVertices;
86     }
unlock(int actualCount)87     void unlock(int actualCount) override {
88         if (fCanMapVB) {
89             fVertexBuffer->unmap();
90         } else {
91             fVertexBuffer->updateData(fVertices, actualCount * stride());
92             sk_free(fVertices);
93         }
94         fVertices = nullptr;
95     }
vertexBuffer()96     GrBuffer* vertexBuffer() { return fVertexBuffer.get(); }
97 private:
98     sk_sp<GrBuffer> fVertexBuffer;
99     GrResourceProvider* fResourceProvider;
100     bool fCanMapVB;
101     void* fVertices;
102 };
103 
104 class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
105 public:
DynamicVertexAllocator(size_t stride,GrMeshDrawOp::Target * target)106     DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
107         : VertexAllocator(stride), fTarget(target), fVertexBuffer(nullptr), fVertices(nullptr) {}
lock(int vertexCount)108     void* lock(int vertexCount) override {
109         fVertexCount = vertexCount;
110         fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
111         return fVertices;
112     }
unlock(int actualCount)113     void unlock(int actualCount) override {
114         fTarget->putBackVertices(fVertexCount - actualCount, stride());
115         fVertices = nullptr;
116     }
vertexBuffer() const117     const GrBuffer* vertexBuffer() const { return fVertexBuffer; }
firstVertex() const118     int firstVertex() const { return fFirstVertex; }
119 private:
120     GrMeshDrawOp::Target* fTarget;
121     const GrBuffer* fVertexBuffer;
122     int fVertexCount;
123     int fFirstVertex;
124     void* fVertices;
125 };
126 
127 }  // namespace
128 
GrTessellatingPathRenderer()129 GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
130 }
131 
onCanDrawPath(const CanDrawPathArgs & args) const132 bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
133     // This path renderer can draw fill styles, and can do screenspace antialiasing via a
134     // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex
135     // ones to simpler algorithms. We pass on paths that have styles, though they may come back
136     // around after applying the styling information to the geometry to create a filled path. In
137     // the non-AA case, We skip paths thta don't have a key since the real advantage of this path
138     // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we
139     // accept paths without keys.
140     if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
141         return false;
142     }
143     if (GrAAType::kCoverage == args.fAAType) {
144 #ifdef SK_DISABLE_SCREENSPACE_TESS_AA_PATH_RENDERER
145         return false;
146 #else
147         SkPath path;
148         args.fShape->asPath(&path);
149         if (path.countVerbs() > 10) {
150             return false;
151         }
152 #endif
153     } else if (!args.fShape->hasUnstyledKey()) {
154         return false;
155     }
156     return true;
157 }
158 
159 class TessellatingPathOp final : public GrMeshDrawOp {
160 public:
161     DEFINE_OP_CLASS_ID
162 
Make(const GrColor & color,const GrShape & shape,const SkMatrix & viewMatrix,SkIRect devClipBounds,bool antiAlias)163     static std::unique_ptr<GrMeshDrawOp> Make(const GrColor& color,
164                                               const GrShape& shape,
165                                               const SkMatrix& viewMatrix,
166                                               SkIRect devClipBounds,
167                                               bool antiAlias) {
168         return std::unique_ptr<GrMeshDrawOp>(
169                 new TessellatingPathOp(color, shape, viewMatrix, devClipBounds, antiAlias));
170     }
171 
name() const172     const char* name() const override { return "TessellatingPathOp"; }
173 
dumpInfo() const174     SkString dumpInfo() const override {
175         SkString string;
176         string.appendf("Color 0x%08x, aa: %d\n", fColor, fAntiAlias);
177         string.append(DumpPipelineInfo(*this->pipeline()));
178         string.append(INHERITED::dumpInfo());
179         return string;
180     }
181 
182 private:
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const183     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
184                                             GrPipelineAnalysisCoverage* coverage) const override {
185         color->setToConstant(fColor);
186         *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
187     }
188 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)189     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
190         optimizations.getOverrideColorIfSet(&fColor);
191         fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
192         fNeedsLocalCoords = optimizations.readsLocalCoords();
193     }
194 
getPath() const195     SkPath getPath() const {
196         SkASSERT(!fShape.style().applies());
197         SkPath path;
198         fShape.asPath(&path);
199         return path;
200     }
201 
draw(Target * target,const GrGeometryProcessor * gp) const202     void draw(Target* target, const GrGeometryProcessor* gp) const {
203         SkASSERT(!fAntiAlias);
204         GrResourceProvider* rp = target->resourceProvider();
205         bool inverseFill = fShape.inverseFilled();
206         // construct a cache key from the path's genID and the view matrix
207         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
208         GrUniqueKey key;
209         static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
210         int shapeKeyDataCnt = fShape.unstyledKeySize();
211         SkASSERT(shapeKeyDataCnt >= 0);
212         GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
213         fShape.writeUnstyledKey(&builder[0]);
214         // For inverse fills, the tessellation is dependent on clip bounds.
215         if (inverseFill) {
216             memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds));
217         } else {
218             memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds));
219         }
220         builder.finish();
221         sk_sp<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
222         int actualCount;
223         SkScalar tol = GrPathUtils::kDefaultTolerance;
224         tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
225         if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
226             this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
227             return;
228         }
229 
230         SkRect clipBounds = SkRect::Make(fDevClipBounds);
231 
232         SkMatrix vmi;
233         if (!fViewMatrix.invert(&vmi)) {
234             return;
235         }
236         vmi.mapRect(&clipBounds);
237         bool isLinear;
238         bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
239         StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB);
240         int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator,
241                                                    false, GrColor(), false, &isLinear);
242         if (count == 0) {
243             return;
244         }
245         this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
246         TessInfo info;
247         info.fTolerance = isLinear ? 0 : tol;
248         info.fCount = count;
249         key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
250         rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
251     }
252 
drawAA(Target * target,const GrGeometryProcessor * gp) const253     void drawAA(Target* target, const GrGeometryProcessor* gp) const {
254         SkASSERT(fAntiAlias);
255         SkPath path = getPath();
256         if (path.isEmpty()) {
257             return;
258         }
259         SkRect clipBounds = SkRect::Make(fDevClipBounds);
260         path.transform(fViewMatrix);
261         SkScalar tol = GrPathUtils::kDefaultTolerance;
262         bool isLinear;
263         DynamicVertexAllocator allocator(gp->getVertexStride(), target);
264         int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator,
265                                                    true, fColor, fCanTweakAlphaForCoverage,
266                                                    &isLinear);
267         if (count == 0) {
268             return;
269         }
270         drawVertices(target, gp, allocator.vertexBuffer(), allocator.firstVertex(), count);
271     }
272 
onPrepareDraws(Target * target) const273     void onPrepareDraws(Target* target) const override {
274         sk_sp<GrGeometryProcessor> gp;
275         {
276             using namespace GrDefaultGeoProcFactory;
277 
278             Color color(fColor);
279             LocalCoords::Type localCoordsType = fNeedsLocalCoords
280                                                         ? LocalCoords::kUsePosition_Type
281                                                         : LocalCoords::kUnused_Type;
282             Coverage::Type coverageType;
283             if (fAntiAlias) {
284                 color = Color(Color::kPremulGrColorAttribute_Type);
285                 if (fCanTweakAlphaForCoverage) {
286                     coverageType = Coverage::kSolid_Type;
287                 } else {
288                     coverageType = Coverage::kAttribute_Type;
289                 }
290             } else {
291                 coverageType = Coverage::kSolid_Type;
292             }
293             if (fAntiAlias) {
294                 gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverageType,
295                                                                  localCoordsType, fViewMatrix);
296             } else {
297                 gp = GrDefaultGeoProcFactory::Make(color, coverageType, localCoordsType,
298                                                    fViewMatrix);
299             }
300         }
301         if (!gp.get()) {
302             return;
303         }
304         if (fAntiAlias) {
305             this->drawAA(target, gp.get());
306         } else {
307             this->draw(target, gp.get());
308         }
309     }
310 
drawVertices(Target * target,const GrGeometryProcessor * gp,const GrBuffer * vb,int firstVertex,int count) const311     void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
312                       int firstVertex, int count) const {
313         GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
314                                                               : kTriangles_GrPrimitiveType;
315         GrMesh mesh;
316         mesh.init(primitiveType, vb, firstVertex, count);
317         target->draw(gp, mesh);
318     }
319 
onCombineIfPossible(GrOp *,const GrCaps &)320     bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
321 
TessellatingPathOp(const GrColor & color,const GrShape & shape,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,bool antiAlias)322     TessellatingPathOp(const GrColor& color,
323                        const GrShape& shape,
324                        const SkMatrix& viewMatrix,
325                        const SkIRect& devClipBounds,
326                        bool antiAlias)
327             : INHERITED(ClassID())
328             , fColor(color)
329             , fShape(shape)
330             , fViewMatrix(viewMatrix)
331             , fDevClipBounds(devClipBounds)
332             , fAntiAlias(antiAlias) {
333         SkRect devBounds;
334         viewMatrix.mapRect(&devBounds, shape.bounds());
335         if (shape.inverseFilled()) {
336             // Because the clip bounds are used to add a contour for inverse fills, they must also
337             // include the path bounds.
338             devBounds.join(SkRect::Make(fDevClipBounds));
339         }
340         this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
341     }
342 
343     GrColor                 fColor;
344     GrShape                 fShape;
345     SkMatrix                fViewMatrix;
346     SkIRect                 fDevClipBounds;
347     bool                    fAntiAlias;
348     bool                    fCanTweakAlphaForCoverage;
349     bool                    fNeedsLocalCoords;
350 
351     typedef GrMeshDrawOp INHERITED;
352 };
353 
onDrawPath(const DrawPathArgs & args)354 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
355     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
356                               "GrTessellatingPathRenderer::onDrawPath");
357     SkIRect clipBoundsI;
358     args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
359                                       args.fRenderTargetContext->height(),
360                                       &clipBoundsI);
361     std::unique_ptr<GrMeshDrawOp> op =
362             TessellatingPathOp::Make(args.fPaint.getColor(),
363                                      *args.fShape,
364                                      *args.fViewMatrix,
365                                      clipBoundsI,
366                                      GrAAType::kCoverage == args.fAAType);
367     GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
368     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
369     args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
370     return true;
371 }
372 
373 ///////////////////////////////////////////////////////////////////////////////////////////////////
374 
375 #if GR_TEST_UTILS
376 
DRAW_OP_TEST_DEFINE(TesselatingPathOp)377 DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
378     GrColor color = GrRandomColor(random);
379     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
380     SkPath path = GrTest::TestPath(random);
381     SkIRect devClipBounds = SkIRect::MakeLTRB(
382         random->nextU(), random->nextU(), random->nextU(), random->nextU());
383     devClipBounds.sort();
384     bool antiAlias = random->nextBool();
385     GrStyle style;
386     do {
387         GrTest::TestStyle(random, &style);
388     } while (!style.isSimpleFill());
389     GrShape shape(path, style);
390     return TessellatingPathOp::Make(color, shape, viewMatrix, devClipBounds, antiAlias);
391 }
392 
393 #endif
394