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