void draw(Target* target, const GrGeometryProcessor* gp) const {
        GrResourceProvider* rp = target->resourceProvider();
        SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
        SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix,
                                                        fShape.bounds());

        SkPath path;
        fShape.asPath(&path);
        bool inverseFill = path.isInverseFillType();
        // construct a cache key from the path's genID and the view matrix
        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
        GrUniqueKey key;
        static constexpr int kClipBoundsCnt = sizeof(fClipBounds) / sizeof(uint32_t);
        int shapeKeyDataCnt = fShape.unstyledKeySize();
        SkASSERT(shapeKeyDataCnt >= 0);
        GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
        fShape.writeUnstyledKey(&builder[0]);
        // For inverse fills, the tessellation is dependent on clip bounds.
        if (inverseFill) {
            memcpy(&builder[shapeKeyDataCnt], &fClipBounds, sizeof(fClipBounds));
        } else {
            memset(&builder[shapeKeyDataCnt], 0, sizeof(fClipBounds));
        }
        builder.finish();
        SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
        int actualCount;
        if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
            this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
            return;
        }

        bool isLinear;
        bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
        StaticVertexAllocator allocator(rp, canMapVB);
        int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear);
        if (count == 0) {
            return;
        }
        this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
        TessInfo info;
        info.fTolerance = isLinear ? 0 : tol;
        info.fCount = count;
        SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info)));
        key.setCustomData(data.get());
        rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
    }
    void draw(Target* target, const GrGeometryProcessor* gp) const {
        GrResourceProvider* rp = target->resourceProvider();
        SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
        SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix,
                                                        fPath.getBounds());

        SkScalar styleScale = SK_Scalar1;
        if (fStyle.applies()) {
            styleScale = GrStyle::MatrixToScaleFactor(fViewMatrix);
        }

        // construct a cache key from the path's genID and the view matrix
        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
        GrUniqueKey key;
        int clipBoundsCnt =
            fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) : 0;
        int styleDataCnt = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
        if (styleDataCnt >= 0) {
            GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsCnt + styleDataCnt);
            builder[0] = fPath.getGenerationID();
            builder[1] = fPath.getFillType();
            // For inverse fills, the tessellation is dependent on clip bounds.
            if (fPath.isInverseFillType()) {
                memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds));
            }
            if (styleDataCnt) {
                GrStyle::WriteKey(&builder[2 + clipBoundsCnt], fStyle,
                                  GrStyle::Apply::kPathEffectAndStrokeRec, styleScale);
            }
            builder.finish();
            SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
            int actualCount;
            if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
                this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
                return;
            }
        }

        SkPath path;
        if (fStyle.applies()) {
            SkStrokeRec::InitStyle fill;
            SkAssertResult(fStyle.applyToPath(&path, &fill, fPath, styleScale));
            SkASSERT(SkStrokeRec::kFill_InitStyle == fill);
        } else {
            path = fPath;
        }
        bool isLinear;
        bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
        StaticVertexAllocator allocator(rp, canMapVB);
        int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear);
        if (count == 0) {
            return;
        }
        this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
        if (!fPath.isVolatile() && styleDataCnt >= 0) {
            TessInfo info;
            info.fTolerance = isLinear ? 0 : tol;
            info.fCount = count;
            SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info)));
            key.setCustomData(data.get());
            rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
            SkPathPriv::AddGenIDChangeListener(fPath, new PathInvalidator(key));
        }
    }