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:
PathInvalidator(const GrUniqueKey & key)39     explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
40 private:
41     GrUniqueKeyInvalidatedMessage fMsg;
42 
onChange()43     void onChange() override {
44         SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
45     }
46 };
47 
cache_match(GrVertexBuffer * vertexBuffer,SkScalar tol,int * actualCount)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 
GrTessellatingPathRenderer()64 GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
65 }
66 
onCanDrawPath(const CanDrawPathArgs & args) const67 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 
Create(const GrColor & color,const SkPath & path,const GrStrokeInfo & stroke,const SkMatrix & viewMatrix,SkRect clipBounds)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 
name() const87     const char* name() const override { return "TessellatingPathBatch"; }
88 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const89     void computePipelineOptimizations(GrInitInvariantOutput* color,
90                                       GrInitInvariantOutput* coverage,
91                                       GrBatchToXPOverrides* overrides) const override {
92         color->setKnownFourComponents(fColor);
93         coverage->setUnknownSingleComponent();
94     }
95 
96 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)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 
tessellate(GrUniqueKey * key,GrResourceProvider * resourceProvider,SkAutoTUnref<GrVertexBuffer> & vertexBuffer,bool canMapVB) const106     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 
onPrepareDraws(Target * target) const145     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 
onCombineIfPossible(GrBatch *,const GrCaps &)205     bool onCombineIfPossible(GrBatch*, const GrCaps&) override { return false; }
206 
TessellatingPathBatch(const GrColor & color,const SkPath & path,const GrStrokeInfo & stroke,const SkMatrix & viewMatrix,const SkRect & clipBounds)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 
onDrawPath(const DrawPathArgs & args)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 
DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch)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