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