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 "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 "GrResourceCache.h"
     18 #include "GrResourceProvider.h"
     19 #include "GrTessellator.h"
     20 #include "SkGeometry.h"
     21 
     22 #include "GrSimpleMeshDrawOpHelper.h"
     23 #include "ops/GrMeshDrawOp.h"
     24 
     25 #include <stdio.h>
     26 
     27 #ifndef GR_AA_TESSELLATOR_MAX_VERB_COUNT
     28 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 10
     29 #endif
     30 
     31 /*
     32  * This path renderer tessellates the path into triangles using GrTessellator, uploads the
     33  * triangles to a vertex buffer, and renders them with a single draw call. It can do screenspace
     34  * antialiasing with a one-pixel coverage ramp.
     35  */
     36 namespace {
     37 
     38 struct TessInfo {
     39     SkScalar  fTolerance;
     40     int       fCount;
     41 };
     42 
     43 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
     44 class PathInvalidator : public SkPathRef::GenIDChangeListener {
     45 public:
     46     explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
     47 private:
     48     GrUniqueKeyInvalidatedMessage fMsg;
     49 
     50     void onChange() override {
     51         SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
     52     }
     53 };
     54 
     55 bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
     56     if (!vertexBuffer) {
     57         return false;
     58     }
     59     const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
     60     SkASSERT(data);
     61     const TessInfo* info = static_cast<const TessInfo*>(data->data());
     62     if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
     63         *actualCount = info->fCount;
     64         return true;
     65     }
     66     return false;
     67 }
     68 
     69 class StaticVertexAllocator : public GrTessellator::VertexAllocator {
     70 public:
     71     StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
     72       : VertexAllocator(stride)
     73       , fResourceProvider(resourceProvider)
     74       , fCanMapVB(canMapVB)
     75       , fVertices(nullptr) {
     76     }
     77     void* lock(int vertexCount) override {
     78         size_t size = vertexCount * stride();
     79         fVertexBuffer.reset(fResourceProvider->createBuffer(
     80             size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0));
     81         if (!fVertexBuffer.get()) {
     82             return nullptr;
     83         }
     84         if (fCanMapVB) {
     85             fVertices = fVertexBuffer->map();
     86         } else {
     87             fVertices = sk_malloc_throw(vertexCount * stride());
     88         }
     89         return fVertices;
     90     }
     91     void unlock(int actualCount) override {
     92         if (fCanMapVB) {
     93             fVertexBuffer->unmap();
     94         } else {
     95             fVertexBuffer->updateData(fVertices, actualCount * stride());
     96             sk_free(fVertices);
     97         }
     98         fVertices = nullptr;
     99     }
    100     GrBuffer* vertexBuffer() { return fVertexBuffer.get(); }
    101 private:
    102     sk_sp<GrBuffer> fVertexBuffer;
    103     GrResourceProvider* fResourceProvider;
    104     bool fCanMapVB;
    105     void* fVertices;
    106 };
    107 
    108 class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
    109 public:
    110     DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
    111             : VertexAllocator(stride)
    112             , fTarget(target)
    113             , fVertexBuffer(nullptr)
    114             , fVertices(nullptr) {}
    115     void* lock(int vertexCount) override {
    116         fVertexCount = vertexCount;
    117         fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
    118         return fVertices;
    119     }
    120     void unlock(int actualCount) override {
    121         fTarget->putBackVertices(fVertexCount - actualCount, stride());
    122         fVertices = nullptr;
    123     }
    124     const GrBuffer* vertexBuffer() const { return fVertexBuffer; }
    125     int firstVertex() const { return fFirstVertex; }
    126 private:
    127     GrMeshDrawOp::Target* fTarget;
    128     const GrBuffer* fVertexBuffer;
    129     int fVertexCount;
    130     int fFirstVertex;
    131     void* fVertices;
    132 };
    133 
    134 }  // namespace
    135 
    136 GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
    137 }
    138 
    139 GrPathRenderer::CanDrawPath
    140 GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
    141     // This path renderer can draw fill styles, and can do screenspace antialiasing via a
    142     // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex
    143     // ones to simpler algorithms. We pass on paths that have styles, though they may come back
    144     // around after applying the styling information to the geometry to create a filled path. In
    145     // the non-AA case, We skip paths that don't have a key since the real advantage of this path
    146     // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we
    147     // accept paths without keys.
    148     if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
    149         return CanDrawPath::kNo;
    150     }
    151     if (GrAAType::kCoverage == args.fAAType) {
    152         SkPath path;
    153         args.fShape->asPath(&path);
    154         if (path.countVerbs() > GR_AA_TESSELLATOR_MAX_VERB_COUNT) {
    155             return CanDrawPath::kNo;
    156         }
    157     } else if (!args.fShape->hasUnstyledKey()) {
    158         return CanDrawPath::kNo;
    159     }
    160     return CanDrawPath::kYes;
    161 }
    162 
    163 namespace {
    164 
    165 class TessellatingPathOp final : public GrMeshDrawOp {
    166 private:
    167     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
    168 
    169 public:
    170     DEFINE_OP_CLASS_ID
    171 
    172     static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint,
    173                                           const GrShape& shape,
    174                                           const SkMatrix& viewMatrix,
    175                                           SkIRect devClipBounds,
    176                                           GrAAType aaType,
    177                                           const GrUserStencilSettings* stencilSettings) {
    178         return Helper::FactoryHelper<TessellatingPathOp>(std::move(paint), shape, viewMatrix,
    179                                                          devClipBounds, aaType, stencilSettings);
    180     }
    181 
    182     const char* name() const override { return "TessellatingPathOp"; }
    183 
    184     void visitProxies(const VisitProxyFunc& func) const override {
    185         fHelper.visitProxies(func);
    186     }
    187 
    188     SkString dumpInfo() const override {
    189         SkString string;
    190         string.appendf("Color 0x%08x, aa: %d\n", fColor, fAntiAlias);
    191         string += fHelper.dumpInfo();
    192         string += INHERITED::dumpInfo();
    193         return string;
    194     }
    195 
    196     TessellatingPathOp(Helper::MakeArgs helperArgs,
    197                        GrColor color,
    198                        const GrShape& shape,
    199                        const SkMatrix& viewMatrix,
    200                        const SkIRect& devClipBounds,
    201                        GrAAType aaType,
    202                        const GrUserStencilSettings* stencilSettings)
    203             : INHERITED(ClassID())
    204             , fHelper(helperArgs, aaType, stencilSettings)
    205             , fColor(color)
    206             , fShape(shape)
    207             , fViewMatrix(viewMatrix)
    208             , fDevClipBounds(devClipBounds)
    209             , fAntiAlias(GrAAType::kCoverage == aaType) {
    210         SkRect devBounds;
    211         viewMatrix.mapRect(&devBounds, shape.bounds());
    212         if (shape.inverseFilled()) {
    213             // Because the clip bounds are used to add a contour for inverse fills, they must also
    214             // include the path bounds.
    215             devBounds.join(SkRect::Make(fDevClipBounds));
    216         }
    217         this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
    218     }
    219 
    220     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
    221 
    222     RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
    223                                 GrPixelConfigIsClamped dstIsClamped) override {
    224         GrProcessorAnalysisCoverage coverage = fAntiAlias
    225                                                        ? GrProcessorAnalysisCoverage::kSingleChannel
    226                                                        : GrProcessorAnalysisCoverage::kNone;
    227         return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, coverage, &fColor);
    228     }
    229 
    230 private:
    231     SkPath getPath() const {
    232         SkASSERT(!fShape.style().applies());
    233         SkPath path;
    234         fShape.asPath(&path);
    235         return path;
    236     }
    237 
    238     void draw(Target* target, const GrGeometryProcessor* gp) {
    239         SkASSERT(!fAntiAlias);
    240         GrResourceProvider* rp = target->resourceProvider();
    241         bool inverseFill = fShape.inverseFilled();
    242         // construct a cache key from the path's genID and the view matrix
    243         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    244         GrUniqueKey key;
    245         static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
    246         int shapeKeyDataCnt = fShape.unstyledKeySize();
    247         SkASSERT(shapeKeyDataCnt >= 0);
    248         GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt, "Path");
    249         fShape.writeUnstyledKey(&builder[0]);
    250         // For inverse fills, the tessellation is dependent on clip bounds.
    251         if (inverseFill) {
    252             memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds));
    253         } else {
    254             memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds));
    255         }
    256         builder.finish();
    257         sk_sp<GrBuffer> cachedVertexBuffer(rp->findByUniqueKey<GrBuffer>(key));
    258         int actualCount;
    259         SkScalar tol = GrPathUtils::kDefaultTolerance;
    260         tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
    261         if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
    262             this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
    263             return;
    264         }
    265 
    266         SkRect clipBounds = SkRect::Make(fDevClipBounds);
    267 
    268         SkMatrix vmi;
    269         if (!fViewMatrix.invert(&vmi)) {
    270             return;
    271         }
    272         vmi.mapRect(&clipBounds);
    273         bool isLinear;
    274         bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
    275         StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB);
    276         int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator,
    277                                                    false, GrColor(), false, &isLinear);
    278         if (count == 0) {
    279             return;
    280         }
    281         this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
    282         TessInfo info;
    283         info.fTolerance = isLinear ? 0 : tol;
    284         info.fCount = count;
    285         key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
    286         rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
    287         fShape.addGenIDChangeListener(new PathInvalidator(key));
    288     }
    289 
    290     void drawAA(Target* target, const GrGeometryProcessor* gp) {
    291         SkASSERT(fAntiAlias);
    292         SkPath path = getPath();
    293         if (path.isEmpty()) {
    294             return;
    295         }
    296         SkRect clipBounds = SkRect::Make(fDevClipBounds);
    297         path.transform(fViewMatrix);
    298         SkScalar tol = GrPathUtils::kDefaultTolerance;
    299         bool isLinear;
    300         DynamicVertexAllocator allocator(gp->getVertexStride(), target);
    301         int count =
    302                 GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, true, fColor,
    303                                                fHelper.compatibleWithAlphaAsCoverage(), &isLinear);
    304         if (count == 0) {
    305             return;
    306         }
    307         this->drawVertices(target, gp, allocator.vertexBuffer(), allocator.firstVertex(), count);
    308     }
    309 
    310     void onPrepareDraws(Target* target) override {
    311         sk_sp<GrGeometryProcessor> gp;
    312         {
    313             using namespace GrDefaultGeoProcFactory;
    314 
    315             Color color(fColor);
    316             LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
    317                                                         ? LocalCoords::kUsePosition_Type
    318                                                         : LocalCoords::kUnused_Type;
    319             Coverage::Type coverageType;
    320             if (fAntiAlias) {
    321                 color = Color(Color::kPremulGrColorAttribute_Type);
    322                 if (fHelper.compatibleWithAlphaAsCoverage()) {
    323                     coverageType = Coverage::kSolid_Type;
    324                 } else {
    325                     coverageType = Coverage::kAttribute_Type;
    326                 }
    327             } else {
    328                 coverageType = Coverage::kSolid_Type;
    329             }
    330             if (fAntiAlias) {
    331                 gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverageType,
    332                                                                  localCoordsType, fViewMatrix);
    333             } else {
    334                 gp = GrDefaultGeoProcFactory::Make(color, coverageType, localCoordsType,
    335                                                    fViewMatrix);
    336             }
    337         }
    338         if (!gp.get()) {
    339             return;
    340         }
    341         if (fAntiAlias) {
    342             this->drawAA(target, gp.get());
    343         } else {
    344             this->draw(target, gp.get());
    345         }
    346     }
    347 
    348     void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
    349                       int firstVertex, int count) {
    350         GrMesh mesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines : GrPrimitiveType::kTriangles);
    351         mesh.setNonIndexedNonInstanced(count);
    352         mesh.setVertexData(vb, firstVertex);
    353         target->draw(gp, fHelper.makePipeline(target), mesh);
    354     }
    355 
    356     bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
    357 
    358     Helper fHelper;
    359     GrColor                 fColor;
    360     GrShape                 fShape;
    361     SkMatrix                fViewMatrix;
    362     SkIRect                 fDevClipBounds;
    363     bool                    fAntiAlias;
    364 
    365     typedef GrMeshDrawOp INHERITED;
    366 };
    367 
    368 }  // anonymous namespace
    369 
    370 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
    371     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
    372                               "GrTessellatingPathRenderer::onDrawPath");
    373     SkIRect clipBoundsI;
    374     args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
    375                                       args.fRenderTargetContext->height(),
    376                                       &clipBoundsI);
    377     std::unique_ptr<GrDrawOp> op = TessellatingPathOp::Make(std::move(args.fPaint),
    378                                                             *args.fShape,
    379                                                             *args.fViewMatrix,
    380                                                             clipBoundsI,
    381                                                             args.fAAType,
    382                                                             args.fUserStencilSettings);
    383     args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
    384     return true;
    385 }
    386 
    387 ///////////////////////////////////////////////////////////////////////////////////////////////////
    388 
    389 #if GR_TEST_UTILS
    390 
    391 GR_DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
    392     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
    393     SkPath path = GrTest::TestPath(random);
    394     SkIRect devClipBounds = SkIRect::MakeLTRB(
    395         random->nextU(), random->nextU(), random->nextU(), random->nextU());
    396     devClipBounds.sort();
    397     static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kMSAA, GrAAType::kCoverage};
    398     GrAAType aaType;
    399     do {
    400         aaType = kAATypes[random->nextULessThan(SK_ARRAY_COUNT(kAATypes))];
    401     } while(GrAAType::kMSAA == aaType && GrFSAAType::kUnifiedMSAA != fsaaType);
    402     GrStyle style;
    403     do {
    404         GrTest::TestStyle(random, &style);
    405     } while (!style.isSimpleFill());
    406     GrShape shape(path, style);
    407     return TessellatingPathOp::Make(std::move(paint), shape, viewMatrix, devClipBounds, aaType,
    408                                     GrGetRandomStencil(random, context));
    409 }
    410 
    411 #endif
    412