GrAALinearizingConvexPathRenderer.cpp revision f8334781914363caf537f22f012fcd5c03c60dad
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 "GrAALinearizingConvexPathRenderer.h"
9
10#include "GrAAConvexTessellator.h"
11#include "GrContext.h"
12#include "GrDefaultGeoProcFactory.h"
13#include "GrDrawOpTest.h"
14#include "GrGeometryProcessor.h"
15#include "GrInvariantOutput.h"
16#include "GrOpFlushState.h"
17#include "GrPathUtils.h"
18#include "GrPipelineBuilder.h"
19#include "GrProcessor.h"
20#include "GrStyle.h"
21#include "SkGeometry.h"
22#include "SkPathPriv.h"
23#include "SkString.h"
24#include "SkTraceEvent.h"
25#include "glsl/GrGLSLGeometryProcessor.h"
26#include "ops/GrMeshDrawOp.h"
27
28static const int DEFAULT_BUFFER_SIZE = 100;
29
30// The thicker the stroke, the harder it is to produce high-quality results using tessellation. For
31// the time being, we simply drop back to software rendering above this stroke width.
32static const SkScalar kMaxStrokeWidth = 20.0;
33
34GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
35}
36
37///////////////////////////////////////////////////////////////////////////////
38
39bool GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
40    if (GrAAType::kCoverage != args.fAAType) {
41        return false;
42    }
43    if (!args.fShape->knownToBeConvex()) {
44        return false;
45    }
46    if (args.fShape->style().pathEffect()) {
47        return false;
48    }
49    if (args.fShape->inverseFilled()) {
50        return false;
51    }
52    const SkStrokeRec& stroke = args.fShape->style().strokeRec();
53
54    if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
55        stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
56        if (!args.fViewMatrix->isSimilarity()) {
57            return false;
58        }
59        SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth();
60        if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) {
61            return false;
62        }
63        return strokeWidth <= kMaxStrokeWidth &&
64               args.fShape->knownToBeClosed() &&
65               stroke.getJoin() != SkPaint::Join::kRound_Join;
66    }
67    return stroke.getStyle() == SkStrokeRec::kFill_Style;
68}
69
70// extract the result vertices and indices from the GrAAConvexTessellator
71static void extract_verts(const GrAAConvexTessellator& tess,
72                          void* vertices,
73                          size_t vertexStride,
74                          GrColor color,
75                          uint16_t firstIndex,
76                          uint16_t* idxs,
77                          bool tweakAlphaForCoverage) {
78    intptr_t verts = reinterpret_cast<intptr_t>(vertices);
79
80    for (int i = 0; i < tess.numPts(); ++i) {
81        *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
82    }
83
84    // Make 'verts' point to the colors
85    verts += sizeof(SkPoint);
86    for (int i = 0; i < tess.numPts(); ++i) {
87        if (tweakAlphaForCoverage) {
88            SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
89            unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
90            GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
91            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
92        } else {
93            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
94            *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
95                    tess.coverage(i);
96        }
97    }
98
99    for (int i = 0; i < tess.numIndices(); ++i) {
100        idxs[i] = tess.index(i) + firstIndex;
101    }
102}
103
104static sk_sp<GrGeometryProcessor> create_fill_gp(bool tweakAlphaForCoverage,
105                                                 const SkMatrix& viewMatrix,
106                                                 bool usesLocalCoords,
107                                                 bool coverageIgnored) {
108    using namespace GrDefaultGeoProcFactory;
109
110    Color color(Color::kAttribute_Type);
111    Coverage::Type coverageType;
112    // TODO remove coverage if coverage is ignored
113    /*if (coverageIgnored) {
114        coverageType = Coverage::kNone_Type;
115    } else*/ if (tweakAlphaForCoverage) {
116        coverageType = Coverage::kSolid_Type;
117    } else {
118        coverageType = Coverage::kAttribute_Type;
119    }
120    Coverage coverage(coverageType);
121    LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type :
122                                              LocalCoords::kUnused_Type);
123    return MakeForDeviceSpace(color, coverage, localCoords, viewMatrix);
124}
125
126class AAFlatteningConvexPathOp final : public GrMeshDrawOp {
127public:
128    DEFINE_OP_CLASS_ID
129    static std::unique_ptr<GrDrawOp> Make(GrColor color,
130                                          const SkMatrix& viewMatrix,
131                                          const SkPath& path,
132                                          SkScalar strokeWidth,
133                                          SkStrokeRec::Style style,
134                                          SkPaint::Join join,
135                                          SkScalar miterLimit) {
136        return std::unique_ptr<GrDrawOp>(new AAFlatteningConvexPathOp(
137                color, viewMatrix, path, strokeWidth, style, join, miterLimit));
138    }
139
140    const char* name() const override { return "AAFlatteningConvexPathOp"; }
141
142    SkString dumpInfo() const override {
143        SkString string;
144        for (const auto& path : fPaths) {
145            string.appendf(
146                    "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, "
147                    "MiterLimit: %.2f\n",
148                    path.fColor, path.fStrokeWidth, path.fStyle, path.fJoin, path.fMiterLimit);
149        }
150        string.append(DumpPipelineInfo(*this->pipeline()));
151        string.append(INHERITED::dumpInfo());
152        return string;
153    }
154
155private:
156    AAFlatteningConvexPathOp(GrColor color,
157                             const SkMatrix& viewMatrix,
158                             const SkPath& path,
159                             SkScalar strokeWidth,
160                             SkStrokeRec::Style style,
161                             SkPaint::Join join,
162                             SkScalar miterLimit)
163            : INHERITED(ClassID()) {
164        fPaths.emplace_back(
165                PathData{color, viewMatrix, path, strokeWidth, style, join, miterLimit});
166
167        // compute bounds
168        SkRect bounds = path.getBounds();
169        SkScalar w = strokeWidth;
170        if (w > 0) {
171            w /= 2;
172            // If the half stroke width is < 1 then we effectively fallback to bevel joins.
173            if (SkPaint::kMiter_Join == join && w > 1.f) {
174                w *= miterLimit;
175            }
176            bounds.outset(w, w);
177        }
178        this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
179    }
180
181    void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override {
182        input->pipelineColorInput()->setKnownFourComponents(fPaths[0].fColor);
183        input->pipelineCoverageInput()->setUnknownSingleComponent();
184    }
185
186    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
187        if (!optimizations.readsColor()) {
188            fPaths[0].fColor = GrColor_ILLEGAL;
189        }
190        optimizations.getOverrideColorIfSet(&fPaths[0].fColor);
191
192        fColor = fPaths[0].fColor;
193        fUsesLocalCoords = optimizations.readsLocalCoords();
194        fCoverageIgnored = !optimizations.readsCoverage();
195        fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
196    }
197
198    void draw(GrMeshDrawOp::Target* target, const GrGeometryProcessor* gp, int vertexCount,
199              size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
200        if (vertexCount == 0 || indexCount == 0) {
201            return;
202        }
203        const GrBuffer* vertexBuffer;
204        GrMesh mesh;
205        int firstVertex;
206        void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
207                                              &firstVertex);
208        if (!verts) {
209            SkDebugf("Could not allocate vertices\n");
210            return;
211        }
212        memcpy(verts, vertices, vertexCount * vertexStride);
213
214        const GrBuffer* indexBuffer;
215        int firstIndex;
216        uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
217        if (!idxs) {
218            SkDebugf("Could not allocate indices\n");
219            return;
220        }
221        memcpy(idxs, indices, indexCount * sizeof(uint16_t));
222        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
223                         firstIndex, vertexCount, indexCount);
224        target->draw(gp, mesh);
225    }
226
227    void onPrepareDraws(Target* target) const override {
228        bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
229
230        // Setup GrGeometryProcessor
231        sk_sp<GrGeometryProcessor> gp(create_fill_gp(canTweakAlphaForCoverage,
232                                                     this->viewMatrix(),
233                                                     this->usesLocalCoords(),
234                                                     this->coverageIgnored()));
235        if (!gp) {
236            SkDebugf("Couldn't create a GrGeometryProcessor\n");
237            return;
238        }
239
240        size_t vertexStride = gp->getVertexStride();
241
242        SkASSERT(canTweakAlphaForCoverage ?
243                 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
244                 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
245
246        int instanceCount = fPaths.count();
247
248        int vertexCount = 0;
249        int indexCount = 0;
250        int maxVertices = DEFAULT_BUFFER_SIZE;
251        int maxIndices = DEFAULT_BUFFER_SIZE;
252        uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride);
253        uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t));
254        for (int i = 0; i < instanceCount; i++) {
255            const PathData& args = fPaths[i];
256            GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth,
257                                       args.fJoin, args.fMiterLimit);
258
259            if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
260                continue;
261            }
262
263            int currentIndices = tess.numIndices();
264            SkASSERT(currentIndices <= UINT16_MAX);
265            if (indexCount + currentIndices > UINT16_MAX) {
266                // if we added the current instance, we would overflow the indices we can store in a
267                // uint16_t. Draw what we've got so far and reset.
268                this->draw(target, gp.get(),
269                           vertexCount, vertexStride, vertices, indexCount, indices);
270                vertexCount = 0;
271                indexCount = 0;
272            }
273            int currentVertices = tess.numPts();
274            if (vertexCount + currentVertices > maxVertices) {
275                maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2);
276                vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride);
277            }
278            if (indexCount + currentIndices > maxIndices) {
279                maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2);
280                indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t));
281            }
282
283            extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor,
284                    vertexCount, indices + indexCount, canTweakAlphaForCoverage);
285            vertexCount += currentVertices;
286            indexCount += currentIndices;
287        }
288        this->draw(target, gp.get(), vertexCount, vertexStride, vertices, indexCount, indices);
289        sk_free(vertices);
290        sk_free(indices);
291    }
292
293    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
294        AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>();
295        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
296                                    that->bounds(), caps)) {
297            return false;
298        }
299
300        SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
301        if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
302            return false;
303        }
304
305        // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
306        // tweaking
307        if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
308            fCanTweakAlphaForCoverage = false;
309        }
310
311        fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
312        this->joinBounds(*that);
313        return true;
314    }
315
316    GrColor color() const { return fColor; }
317    bool usesLocalCoords() const { return fUsesLocalCoords; }
318    bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
319    const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; }
320    bool coverageIgnored() const { return fCoverageIgnored; }
321
322    struct PathData {
323        GrColor fColor;
324        SkMatrix fViewMatrix;
325        SkPath fPath;
326        SkScalar fStrokeWidth;
327        SkStrokeRec::Style fStyle;
328        SkPaint::Join fJoin;
329        SkScalar fMiterLimit;
330    };
331
332    GrColor fColor;
333    bool fUsesLocalCoords;
334    bool fCoverageIgnored;
335    bool fCanTweakAlphaForCoverage;
336    SkSTArray<1, PathData, true> fPaths;
337
338    typedef GrMeshDrawOp INHERITED;
339};
340
341bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
342    GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
343                              "GrAALinearizingConvexPathRenderer::onDrawPath");
344    SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
345    SkASSERT(!args.fShape->isEmpty());
346    SkASSERT(!args.fShape->style().pathEffect());
347
348    SkPath path;
349    args.fShape->asPath(&path);
350    bool fill = args.fShape->style().isSimpleFill();
351    const SkStrokeRec& stroke = args.fShape->style().strokeRec();
352    SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth();
353    SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin();
354    SkScalar miterLimit = stroke.getMiter();
355
356    std::unique_ptr<GrDrawOp> op =
357            AAFlatteningConvexPathOp::Make(args.fPaint->getColor(), *args.fViewMatrix, path,
358                                           strokeWidth, stroke.getStyle(), join, miterLimit);
359
360    GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fAAType);
361    pipelineBuilder.setUserStencil(args.fUserStencilSettings);
362
363    args.fRenderTargetContext->addDrawOp(pipelineBuilder, *args.fClip, std::move(op));
364
365    return true;
366}
367
368///////////////////////////////////////////////////////////////////////////////////////////////////
369
370#ifdef GR_TEST_UTILS
371
372DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
373    GrColor color = GrRandomColor(random);
374    SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
375    SkPath path = GrTest::TestPathConvex(random);
376
377    SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style,
378                                     SkStrokeRec::kStroke_Style,
379                                     SkStrokeRec::kStrokeAndFill_Style };
380
381    SkStrokeRec::Style style = styles[random->nextU() % 3];
382
383    SkScalar strokeWidth = -1.f;
384    SkPaint::Join join = SkPaint::kMiter_Join;
385    SkScalar miterLimit = 0.5f;
386
387    if (SkStrokeRec::kFill_Style != style) {
388        strokeWidth = random->nextRangeF(1.0f, 10.0f);
389        if (random->nextBool()) {
390            join = SkPaint::kMiter_Join;
391        } else {
392            join = SkPaint::kBevel_Join;
393        }
394        miterLimit = random->nextRangeF(0.5f, 2.0f);
395    }
396
397    return AAFlatteningConvexPathOp::Make(color, viewMatrix, path, strokeWidth, style, join,
398                                          miterLimit);
399}
400
401#endif
402