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