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 */
32namespace {
33
34struct TessInfo {
35    SkScalar  fTolerance;
36    int       fCount;
37};
38
39// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
40class PathInvalidator : public SkPathRef::GenIDChangeListener {
41public:
42    explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
43private:
44    GrUniqueKeyInvalidatedMessage fMsg;
45
46    void onChange() override {
47        SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
48    }
49};
50
51bool 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
65class StaticVertexAllocator : public GrTessellator::VertexAllocator {
66public:
67    StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
68      : VertexAllocator(stride)
69      , fResourceProvider(resourceProvider)
70      , fCanMapVB(canMapVB)
71      , fVertices(nullptr) {
72    }
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    }
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    }
96    GrBuffer* vertexBuffer() { return fVertexBuffer.get(); }
97private:
98    sk_sp<GrBuffer> fVertexBuffer;
99    GrResourceProvider* fResourceProvider;
100    bool fCanMapVB;
101    void* fVertices;
102};
103
104class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
105public:
106    DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
107        : VertexAllocator(stride), fTarget(target), fVertexBuffer(nullptr), fVertices(nullptr) {}
108    void* lock(int vertexCount) override {
109        fVertexCount = vertexCount;
110        fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
111        return fVertices;
112    }
113    void unlock(int actualCount) override {
114        fTarget->putBackVertices(fVertexCount - actualCount, stride());
115        fVertices = nullptr;
116    }
117    const GrBuffer* vertexBuffer() const { return fVertexBuffer; }
118    int firstVertex() const { return fFirstVertex; }
119private:
120    GrMeshDrawOp::Target* fTarget;
121    const GrBuffer* fVertexBuffer;
122    int fVertexCount;
123    int fFirstVertex;
124    void* fVertices;
125};
126
127}  // namespace
128
129GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
130}
131
132bool 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
159class TessellatingPathOp final : public GrMeshDrawOp {
160public:
161    DEFINE_OP_CLASS_ID
162
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
172    const char* name() const override { return "TessellatingPathOp"; }
173
174    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
182private:
183    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
184                                            GrPipelineAnalysisCoverage* coverage) const override {
185        color->setToConstant(fColor);
186        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
187    }
188
189    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
190        optimizations.getOverrideColorIfSet(&fColor);
191        fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
192        fNeedsLocalCoords = optimizations.readsLocalCoords();
193    }
194
195    SkPath getPath() const {
196        SkASSERT(!fShape.style().applies());
197        SkPath path;
198        fShape.asPath(&path);
199        return path;
200    }
201
202    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
253    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
273    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
311    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
320    bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
321
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
354bool 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
377DRAW_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