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 #include <stdio.h>
10 #include "GrAuditTrail.h"
11 #include "GrCaps.h"
12 #include "GrClip.h"
13 #include "GrDefaultGeoProcFactory.h"
14 #include "GrDrawOpTest.h"
15 #include "GrMesh.h"
16 #include "GrOpFlushState.h"
17 #include "GrPathUtils.h"
18 #include "GrResourceCache.h"
19 #include "GrResourceProvider.h"
20 #include "GrShape.h"
21 #include "GrSimpleMeshDrawOpHelper.h"
22 #include "GrStyle.h"
23 #include "GrTessellator.h"
24 #include "SkGeometry.h"
25 #include "ops/GrMeshDrawOp.h"
26 
27 #ifndef GR_AA_TESSELLATOR_MAX_VERB_COUNT
28 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 10
29 #endif
30 
31 /*
32  * This path renderer tessellates the path into triangles using GrTessellator, uploads the
33  * triangles to a vertex buffer, and renders them with a single draw call. It can do screenspace
34  * antialiasing with a one-pixel coverage ramp.
35  */
36 namespace {
37 
38 struct TessInfo {
39     SkScalar  fTolerance;
40     int       fCount;
41 };
42 
43 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
44 class PathInvalidator : public SkPathRef::GenIDChangeListener {
45 public:
PathInvalidator(const GrUniqueKey & key,uint32_t contextUniqueID)46     PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
47             : fMsg(key, contextUniqueID) {}
48 
49 private:
50     GrUniqueKeyInvalidatedMessage fMsg;
51 
onChange()52     void onChange() override {
53         SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
54     }
55 };
56 
cache_match(GrGpuBuffer * vertexBuffer,SkScalar tol,int * actualCount)57 bool cache_match(GrGpuBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
58     if (!vertexBuffer) {
59         return false;
60     }
61     const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
62     SkASSERT(data);
63     const TessInfo* info = static_cast<const TessInfo*>(data->data());
64     if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
65         *actualCount = info->fCount;
66         return true;
67     }
68     return false;
69 }
70 
71 class StaticVertexAllocator : public GrTessellator::VertexAllocator {
72 public:
StaticVertexAllocator(size_t stride,GrResourceProvider * resourceProvider,bool canMapVB)73     StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
74       : VertexAllocator(stride)
75       , fResourceProvider(resourceProvider)
76       , fCanMapVB(canMapVB)
77       , fVertices(nullptr) {
78     }
lock(int vertexCount)79     void* lock(int vertexCount) override {
80         size_t size = vertexCount * stride();
81         fVertexBuffer = fResourceProvider->createBuffer(size, GrGpuBufferType::kVertex,
82                                                         kStatic_GrAccessPattern);
83         if (!fVertexBuffer.get()) {
84             return nullptr;
85         }
86         if (fCanMapVB) {
87             fVertices = fVertexBuffer->map();
88         } else {
89             fVertices = sk_malloc_throw(vertexCount * stride());
90         }
91         return fVertices;
92     }
unlock(int actualCount)93     void unlock(int actualCount) override {
94         if (fCanMapVB) {
95             fVertexBuffer->unmap();
96         } else {
97             fVertexBuffer->updateData(fVertices, actualCount * stride());
98             sk_free(fVertices);
99         }
100         fVertices = nullptr;
101     }
detachVertexBuffer()102     sk_sp<GrGpuBuffer> detachVertexBuffer() { return std::move(fVertexBuffer); }
103 
104 private:
105     sk_sp<GrGpuBuffer> fVertexBuffer;
106     GrResourceProvider* fResourceProvider;
107     bool fCanMapVB;
108     void* fVertices;
109 };
110 
111 class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
112 public:
DynamicVertexAllocator(size_t stride,GrMeshDrawOp::Target * target)113     DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
114             : VertexAllocator(stride)
115             , fTarget(target)
116             , fVertexBuffer(nullptr)
117             , fVertices(nullptr) {}
lock(int vertexCount)118     void* lock(int vertexCount) override {
119         fVertexCount = vertexCount;
120         fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
121         return fVertices;
122     }
unlock(int actualCount)123     void unlock(int actualCount) override {
124         fTarget->putBackVertices(fVertexCount - actualCount, stride());
125         fVertices = nullptr;
126     }
detachVertexBuffer() const127     sk_sp<const GrBuffer> detachVertexBuffer() const { return std::move(fVertexBuffer); }
firstVertex() const128     int firstVertex() const { return fFirstVertex; }
129 
130 private:
131     GrMeshDrawOp::Target* fTarget;
132     sk_sp<const GrBuffer> fVertexBuffer;
133     int fVertexCount;
134     int fFirstVertex;
135     void* fVertices;
136 };
137 
138 }  // namespace
139 
GrTessellatingPathRenderer()140 GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
141 }
142 
143 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const144 GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
145     // This path renderer can draw fill styles, and can do screenspace antialiasing via a
146     // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex
147     // ones to simpler algorithms. We pass on paths that have styles, though they may come back
148     // around after applying the styling information to the geometry to create a filled path. In
149     // the non-AA case, We skip paths that don't have a key since the real advantage of this path
150     // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we
151     // accept paths without keys.
152     if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
153         return CanDrawPath::kNo;
154     }
155     if (GrAAType::kCoverage == args.fAAType) {
156         SkPath path;
157         args.fShape->asPath(&path);
158         if (path.countVerbs() > GR_AA_TESSELLATOR_MAX_VERB_COUNT) {
159             return CanDrawPath::kNo;
160         }
161     } else if (!args.fShape->hasUnstyledKey()) {
162         return CanDrawPath::kNo;
163     }
164     return CanDrawPath::kYes;
165 }
166 
167 namespace {
168 
169 class TessellatingPathOp final : public GrMeshDrawOp {
170 private:
171     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
172 
173 public:
174     DEFINE_OP_CLASS_ID
175 
Make(GrRecordingContext * context,GrPaint && paint,const GrShape & shape,const SkMatrix & viewMatrix,SkIRect devClipBounds,GrAAType aaType,const GrUserStencilSettings * stencilSettings)176     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
177                                           GrPaint&& paint,
178                                           const GrShape& shape,
179                                           const SkMatrix& viewMatrix,
180                                           SkIRect devClipBounds,
181                                           GrAAType aaType,
182                                           const GrUserStencilSettings* stencilSettings) {
183         return Helper::FactoryHelper<TessellatingPathOp>(context, std::move(paint), shape,
184                                                          viewMatrix, devClipBounds,
185                                                          aaType, stencilSettings);
186     }
187 
name() const188     const char* name() const override { return "TessellatingPathOp"; }
189 
visitProxies(const VisitProxyFunc & func,VisitorType) const190     void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
191         fHelper.visitProxies(func);
192     }
193 
194 #ifdef SK_DEBUG
dumpInfo() const195     SkString dumpInfo() const override {
196         SkString string;
197         string.appendf("Color 0x%08x, aa: %d\n", fColor.toBytes_RGBA(), fAntiAlias);
198         string += fHelper.dumpInfo();
199         string += INHERITED::dumpInfo();
200         return string;
201     }
202 #endif
203 
TessellatingPathOp(Helper::MakeArgs helperArgs,const SkPMColor4f & color,const GrShape & shape,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,GrAAType aaType,const GrUserStencilSettings * stencilSettings)204     TessellatingPathOp(Helper::MakeArgs helperArgs,
205                        const SkPMColor4f& color,
206                        const GrShape& shape,
207                        const SkMatrix& viewMatrix,
208                        const SkIRect& devClipBounds,
209                        GrAAType aaType,
210                        const GrUserStencilSettings* stencilSettings)
211             : INHERITED(ClassID())
212             , fHelper(helperArgs, aaType, stencilSettings)
213             , fColor(color)
214             , fShape(shape)
215             , fViewMatrix(viewMatrix)
216             , fDevClipBounds(devClipBounds)
217             , fAntiAlias(GrAAType::kCoverage == aaType) {
218         SkRect devBounds;
219         viewMatrix.mapRect(&devBounds, shape.bounds());
220         if (shape.inverseFilled()) {
221             // Because the clip bounds are used to add a contour for inverse fills, they must also
222             // include the path bounds.
223             devBounds.join(SkRect::Make(fDevClipBounds));
224         }
225         this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
226     }
227 
fixedFunctionFlags() const228     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
229 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrFSAAType fsaaType,GrClampType clampType)230     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
231                                       GrFSAAType fsaaType, GrClampType clampType) override {
232         GrProcessorAnalysisCoverage coverage = fAntiAlias
233                                                        ? GrProcessorAnalysisCoverage::kSingleChannel
234                                                        : GrProcessorAnalysisCoverage::kNone;
235         return fHelper.finalizeProcessors(caps, clip, fsaaType, clampType, coverage, &fColor);
236     }
237 
238 private:
getPath() const239     SkPath getPath() const {
240         SkASSERT(!fShape.style().applies());
241         SkPath path;
242         fShape.asPath(&path);
243         return path;
244     }
245 
draw(Target * target,sk_sp<const GrGeometryProcessor> gp,size_t vertexStride)246     void draw(Target* target, sk_sp<const GrGeometryProcessor> gp, size_t vertexStride) {
247         SkASSERT(!fAntiAlias);
248         GrResourceProvider* rp = target->resourceProvider();
249         bool inverseFill = fShape.inverseFilled();
250         // construct a cache key from the path's genID and the view matrix
251         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
252         GrUniqueKey key;
253         static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
254         int shapeKeyDataCnt = fShape.unstyledKeySize();
255         SkASSERT(shapeKeyDataCnt >= 0);
256         GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt, "Path");
257         fShape.writeUnstyledKey(&builder[0]);
258         // For inverse fills, the tessellation is dependent on clip bounds.
259         if (inverseFill) {
260             memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds));
261         } else {
262             memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds));
263         }
264         builder.finish();
265         sk_sp<GrGpuBuffer> cachedVertexBuffer(rp->findByUniqueKey<GrGpuBuffer>(key));
266         int actualCount;
267         SkScalar tol = GrPathUtils::kDefaultTolerance;
268         tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
269         if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
270             this->drawVertices(target, std::move(gp), std::move(cachedVertexBuffer), 0,
271                                actualCount);
272             return;
273         }
274 
275         SkRect clipBounds = SkRect::Make(fDevClipBounds);
276 
277         SkMatrix vmi;
278         if (!fViewMatrix.invert(&vmi)) {
279             return;
280         }
281         vmi.mapRect(&clipBounds);
282         bool isLinear;
283         bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
284         StaticVertexAllocator allocator(vertexStride, rp, canMapVB);
285         int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator, false,
286                                                    &isLinear);
287         if (count == 0) {
288             return;
289         }
290         sk_sp<GrGpuBuffer> vb = allocator.detachVertexBuffer();
291         TessInfo info;
292         info.fTolerance = isLinear ? 0 : tol;
293         info.fCount = count;
294         fShape.addGenIDChangeListener(sk_make_sp<PathInvalidator>(key, target->contextUniqueID()));
295         key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
296         rp->assignUniqueKeyToResource(key, vb.get());
297 
298         this->drawVertices(target, std::move(gp), std::move(vb), 0, count);
299     }
300 
drawAA(Target * target,sk_sp<const GrGeometryProcessor> gp,size_t vertexStride)301     void drawAA(Target* target, sk_sp<const GrGeometryProcessor> gp, size_t vertexStride) {
302         SkASSERT(fAntiAlias);
303         SkPath path = getPath();
304         if (path.isEmpty()) {
305             return;
306         }
307         SkRect clipBounds = SkRect::Make(fDevClipBounds);
308         path.transform(fViewMatrix);
309         SkScalar tol = GrPathUtils::kDefaultTolerance;
310         bool isLinear;
311         DynamicVertexAllocator allocator(vertexStride, target);
312         int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, true,
313                                                    &isLinear);
314         if (count == 0) {
315             return;
316         }
317         this->drawVertices(target, std::move(gp), allocator.detachVertexBuffer(),
318                            allocator.firstVertex(), count);
319     }
320 
onPrepareDraws(Target * target)321     void onPrepareDraws(Target* target) override {
322         sk_sp<GrGeometryProcessor> gp;
323         {
324             using namespace GrDefaultGeoProcFactory;
325 
326             Color color(fColor);
327             LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
328                                                         ? LocalCoords::kUsePosition_Type
329                                                         : LocalCoords::kUnused_Type;
330             Coverage::Type coverageType;
331             if (fAntiAlias) {
332                 if (fHelper.compatibleWithAlphaAsCoverage()) {
333                     coverageType = Coverage::kAttributeTweakAlpha_Type;
334                 } else {
335                     coverageType = Coverage::kAttribute_Type;
336                 }
337             } else {
338                 coverageType = Coverage::kSolid_Type;
339             }
340             if (fAntiAlias) {
341                 gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(target->caps().shaderCaps(),
342                                                                  color, coverageType,
343                                                                  localCoordsType, fViewMatrix);
344             } else {
345                 gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
346                                                    color, coverageType, localCoordsType,
347                                                    fViewMatrix);
348             }
349         }
350         if (!gp.get()) {
351             return;
352         }
353         size_t vertexStride = gp->vertexStride();
354         if (fAntiAlias) {
355             this->drawAA(target, std::move(gp), vertexStride);
356         } else {
357             this->draw(target, std::move(gp), vertexStride);
358         }
359     }
360 
drawVertices(Target * target,sk_sp<const GrGeometryProcessor> gp,sk_sp<const GrBuffer> vb,int firstVertex,int count)361     void drawVertices(Target* target, sk_sp<const GrGeometryProcessor> gp, sk_sp<const GrBuffer> vb,
362                       int firstVertex, int count) {
363         GrMesh* mesh = target->allocMesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines
364                                                                : GrPrimitiveType::kTriangles);
365         mesh->setNonIndexedNonInstanced(count);
366         mesh->setVertexData(std::move(vb), firstVertex);
367         target->recordDraw(std::move(gp), mesh);
368     }
369 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)370     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
371         fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
372     }
373 
374     Helper fHelper;
375     SkPMColor4f             fColor;
376     GrShape                 fShape;
377     SkMatrix                fViewMatrix;
378     SkIRect                 fDevClipBounds;
379     bool                    fAntiAlias;
380 
381     typedef GrMeshDrawOp INHERITED;
382 };
383 
384 }  // anonymous namespace
385 
onDrawPath(const DrawPathArgs & args)386 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
387     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
388                               "GrTessellatingPathRenderer::onDrawPath");
389     SkIRect clipBoundsI;
390     args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
391                                       args.fRenderTargetContext->height(),
392                                       &clipBoundsI);
393     std::unique_ptr<GrDrawOp> op = TessellatingPathOp::Make(args.fContext,
394                                                             std::move(args.fPaint),
395                                                             *args.fShape,
396                                                             *args.fViewMatrix,
397                                                             clipBoundsI,
398                                                             args.fAAType,
399                                                             args.fUserStencilSettings);
400     args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
401     return true;
402 }
403 
404 ///////////////////////////////////////////////////////////////////////////////////////////////////
405 
406 #if GR_TEST_UTILS
407 
GR_DRAW_OP_TEST_DEFINE(TesselatingPathOp)408 GR_DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
409     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
410     SkPath path = GrTest::TestPath(random);
411     SkIRect devClipBounds = SkIRect::MakeLTRB(
412         random->nextU(), random->nextU(), random->nextU(), random->nextU());
413     devClipBounds.sort();
414     static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kMSAA, GrAAType::kCoverage};
415     GrAAType aaType;
416     do {
417         aaType = kAATypes[random->nextULessThan(SK_ARRAY_COUNT(kAATypes))];
418     } while(GrAAType::kMSAA == aaType && GrFSAAType::kUnifiedMSAA != fsaaType);
419     GrStyle style;
420     do {
421         GrTest::TestStyle(random, &style);
422     } while (!style.isSimpleFill());
423     GrShape shape(path, style);
424     return TessellatingPathOp::Make(context, std::move(paint), shape, viewMatrix, devClipBounds,
425                                     aaType, GrGetRandomStencil(random, context));
426 }
427 
428 #endif
429