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