Home | History | Annotate | Download | only in batches
      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 "GrBatchFlushState.h"
     11 #include "GrBatchTest.h"
     12 #include "GrDefaultGeoProcFactory.h"
     13 #include "GrPathUtils.h"
     14 #include "GrVertices.h"
     15 #include "GrResourceCache.h"
     16 #include "GrResourceProvider.h"
     17 #include "GrTessellator.h"
     18 #include "SkGeometry.h"
     19 
     20 #include "batches/GrVertexBatch.h"
     21 
     22 #include <stdio.h>
     23 
     24 /*
     25  * This path renderer tessellates the path into triangles using GrTessellator, uploads the triangles
     26  * to a vertex buffer, and renders them with a single draw call. It does not currently do
     27  * antialiasing, so it must be used in conjunction with multisampling.
     28  */
     29 namespace {
     30 
     31 struct TessInfo {
     32     SkScalar  fTolerance;
     33     int       fCount;
     34 };
     35 
     36 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
     37 class PathInvalidator : public SkPathRef::GenIDChangeListener {
     38 public:
     39     explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
     40 private:
     41     GrUniqueKeyInvalidatedMessage fMsg;
     42 
     43     void onChange() override {
     44         SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
     45     }
     46 };
     47 
     48 bool cache_match(GrVertexBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
     49     if (!vertexBuffer) {
     50         return false;
     51     }
     52     const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
     53     SkASSERT(data);
     54     const TessInfo* info = static_cast<const TessInfo*>(data->data());
     55     if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
     56         *actualCount = info->fCount;
     57         return true;
     58     }
     59     return false;
     60 }
     61 
     62 }  // namespace
     63 
     64 GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
     65 }
     66 
     67 bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
     68     // This path renderer can draw all fill styles, all stroke styles except hairlines, but does
     69     // not do antialiasing. It can do convex and concave paths, but we'll leave the convex ones to
     70     // simpler algorithms.
     71     return !IsStrokeHairlineOrEquivalent(*args.fStroke, *args.fViewMatrix, nullptr) &&
     72            !args.fAntiAlias && !args.fPath->isConvex();
     73 }
     74 
     75 class TessellatingPathBatch : public GrVertexBatch {
     76 public:
     77     DEFINE_BATCH_CLASS_ID
     78 
     79     static GrDrawBatch* Create(const GrColor& color,
     80                                const SkPath& path,
     81                                const GrStrokeInfo& stroke,
     82                                const SkMatrix& viewMatrix,
     83                                SkRect clipBounds) {
     84         return new TessellatingPathBatch(color, path, stroke, viewMatrix, clipBounds);
     85     }
     86 
     87     const char* name() const override { return "TessellatingPathBatch"; }
     88 
     89     void computePipelineOptimizations(GrInitInvariantOutput* color,
     90                                       GrInitInvariantOutput* coverage,
     91                                       GrBatchToXPOverrides* overrides) const override {
     92         color->setKnownFourComponents(fColor);
     93         coverage->setUnknownSingleComponent();
     94     }
     95 
     96 private:
     97     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
     98         // Handle any color overrides
     99         if (!overrides.readsColor()) {
    100             fColor = GrColor_ILLEGAL;
    101         }
    102         overrides.getOverrideColorIfSet(&fColor);
    103         fPipelineInfo = overrides;
    104     }
    105 
    106     int tessellate(GrUniqueKey* key,
    107                    GrResourceProvider* resourceProvider,
    108                    SkAutoTUnref<GrVertexBuffer>& vertexBuffer,
    109                    bool canMapVB) const {
    110         SkPath path;
    111         GrStrokeInfo stroke(fStroke);
    112         if (stroke.isDashed()) {
    113             if (!stroke.applyDashToPath(&path, &stroke, fPath)) {
    114                 return 0;
    115             }
    116         } else {
    117             path = fPath;
    118         }
    119         if (!stroke.isFillStyle()) {
    120             stroke.setResScale(SkScalarAbs(fViewMatrix.getMaxScale()));
    121             if (!stroke.applyToPath(&path, path)) {
    122                 return 0;
    123             }
    124             stroke.setFillStyle();
    125         }
    126         SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
    127         SkRect pathBounds = path.getBounds();
    128         SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix, pathBounds);
    129 
    130         bool isLinear;
    131         int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, resourceProvider,
    132                                                    vertexBuffer, canMapVB, &isLinear);
    133         if (!fPath.isVolatile()) {
    134             TessInfo info;
    135             info.fTolerance = isLinear ? 0 : tol;
    136             info.fCount = count;
    137             SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info)));
    138             key->setCustomData(data.get());
    139             resourceProvider->assignUniqueKeyToResource(*key, vertexBuffer.get());
    140             SkPathPriv::AddGenIDChangeListener(fPath, new PathInvalidator(*key));
    141         }
    142         return count;
    143     }
    144 
    145     void onPrepareDraws(Target* target) const override {
    146         // construct a cache key from the path's genID and the view matrix
    147         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    148         GrUniqueKey key;
    149         int clipBoundsSize32 =
    150             fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) : 0;
    151         int strokeDataSize32 = fStroke.computeUniqueKeyFragmentData32Cnt();
    152         GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsSize32 + strokeDataSize32);
    153         builder[0] = fPath.getGenerationID();
    154         builder[1] = fPath.getFillType();
    155         // For inverse fills, the tessellation is dependent on clip bounds.
    156         if (fPath.isInverseFillType()) {
    157             memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds));
    158         }
    159         fStroke.asUniqueKeyFragment(&builder[2 + clipBoundsSize32]);
    160         builder.finish();
    161         GrResourceProvider* rp = target->resourceProvider();
    162         SkAutoTUnref<GrVertexBuffer> vertexBuffer(rp->findAndRefTByUniqueKey<GrVertexBuffer>(key));
    163         int actualCount;
    164         SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
    165         SkScalar tol = GrPathUtils::scaleToleranceToSrc(
    166             screenSpaceTol, fViewMatrix, fPath.getBounds());
    167         if (!cache_match(vertexBuffer.get(), tol, &actualCount)) {
    168             bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
    169             actualCount = this->tessellate(&key, rp, vertexBuffer, canMapVB);
    170         }
    171 
    172         if (actualCount == 0) {
    173             return;
    174         }
    175 
    176         SkAutoTUnref<const GrGeometryProcessor> gp;
    177         {
    178             using namespace GrDefaultGeoProcFactory;
    179 
    180             Color color(fColor);
    181             LocalCoords localCoords(fPipelineInfo.readsLocalCoords() ?
    182                                     LocalCoords::kUsePosition_Type :
    183                                     LocalCoords::kUnused_Type);
    184             Coverage::Type coverageType;
    185             if (fPipelineInfo.readsCoverage()) {
    186                 coverageType = Coverage::kSolid_Type;
    187             } else {
    188                 coverageType = Coverage::kNone_Type;
    189             }
    190             Coverage coverage(coverageType);
    191             gp.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords,
    192                                                      fViewMatrix));
    193         }
    194 
    195         target->initDraw(gp, this->pipeline());
    196         SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
    197 
    198         GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
    199                                                               : kTriangles_GrPrimitiveType;
    200         GrVertices vertices;
    201         vertices.init(primitiveType, vertexBuffer.get(), 0, actualCount);
    202         target->draw(vertices);
    203     }
    204 
    205     bool onCombineIfPossible(GrBatch*, const GrCaps&) override { return false; }
    206 
    207     TessellatingPathBatch(const GrColor& color,
    208                           const SkPath& path,
    209                           const GrStrokeInfo& stroke,
    210                           const SkMatrix& viewMatrix,
    211                           const SkRect& clipBounds)
    212       : INHERITED(ClassID())
    213       , fColor(color)
    214       , fPath(path)
    215       , fStroke(stroke)
    216       , fViewMatrix(viewMatrix) {
    217         const SkRect& pathBounds = path.getBounds();
    218         fClipBounds = clipBounds;
    219         // Because the clip bounds are used to add a contour for inverse fills, they must also
    220         // include the path bounds.
    221         fClipBounds.join(pathBounds);
    222         if (path.isInverseFillType()) {
    223             fBounds = fClipBounds;
    224         } else {
    225             fBounds = path.getBounds();
    226         }
    227         if (!stroke.isFillStyle()) {
    228             SkScalar radius = SkScalarHalf(stroke.getWidth());
    229             if (stroke.getJoin() == SkPaint::kMiter_Join) {
    230                 SkScalar scale = stroke.getMiter();
    231                 if (scale > SK_Scalar1) {
    232                     radius = SkScalarMul(radius, scale);
    233                 }
    234             }
    235             fBounds.outset(radius, radius);
    236         }
    237         viewMatrix.mapRect(&fBounds);
    238     }
    239 
    240     GrColor                 fColor;
    241     SkPath                  fPath;
    242     GrStrokeInfo            fStroke;
    243     SkMatrix                fViewMatrix;
    244     SkRect                  fClipBounds; // in source space
    245     GrXPOverridesForBatch   fPipelineInfo;
    246 
    247     typedef GrVertexBatch INHERITED;
    248 };
    249 
    250 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
    251     GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),
    252                               "GrTessellatingPathRenderer::onDrawPath");
    253     SkASSERT(!args.fAntiAlias);
    254     const GrRenderTarget* rt = args.fPipelineBuilder->getRenderTarget();
    255     if (nullptr == rt) {
    256         return false;
    257     }
    258 
    259     SkIRect clipBoundsI;
    260     args.fPipelineBuilder->clip().getConservativeBounds(rt->width(), rt->height(), &clipBoundsI);
    261     SkRect clipBounds = SkRect::Make(clipBoundsI);
    262     SkMatrix vmi;
    263     if (!args.fViewMatrix->invert(&vmi)) {
    264         return false;
    265     }
    266     vmi.mapRect(&clipBounds);
    267     SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fColor, *args.fPath,
    268                                                                   *args.fStroke, *args.fViewMatrix,
    269                                                                   clipBounds));
    270     args.fTarget->drawBatch(*args.fPipelineBuilder, batch);
    271 
    272     return true;
    273 }
    274 
    275 ///////////////////////////////////////////////////////////////////////////////////////////////////
    276 
    277 #ifdef GR_TEST_UTILS
    278 
    279 DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) {
    280     GrColor color = GrRandomColor(random);
    281     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
    282     SkPath path = GrTest::TestPath(random);
    283     SkRect clipBounds = GrTest::TestRect(random);
    284     SkMatrix vmi;
    285     bool result = viewMatrix.invert(&vmi);
    286     if (!result) {
    287         SkFAIL("Cannot invert matrix\n");
    288     }
    289     vmi.mapRect(&clipBounds);
    290     GrStrokeInfo strokeInfo = GrTest::TestStrokeInfo(random);
    291     return TessellatingPathBatch::Create(color, path, strokeInfo, viewMatrix, clipBounds);
    292 }
    293 
    294 #endif
    295