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