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