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 "GrPipelineBuilder.h"
18 #include "GrResourceCache.h"
19 #include "GrResourceProvider.h"
20 #include "GrTessellator.h"
21 #include "SkGeometry.h"
22
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:
PathInvalidator(const GrUniqueKey & key)42 explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
43 private:
44 GrUniqueKeyInvalidatedMessage fMsg;
45
onChange()46 void onChange() override {
47 SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
48 }
49 };
50
cache_match(GrBuffer * vertexBuffer,SkScalar tol,int * actualCount)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:
StaticVertexAllocator(size_t stride,GrResourceProvider * resourceProvider,bool canMapVB)67 StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
68 : VertexAllocator(stride)
69 , fResourceProvider(resourceProvider)
70 , fCanMapVB(canMapVB)
71 , fVertices(nullptr) {
72 }
lock(int vertexCount)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 }
unlock(int actualCount)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 }
vertexBuffer()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:
DynamicVertexAllocator(size_t stride,GrMeshDrawOp::Target * target)106 DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
107 : VertexAllocator(stride), fTarget(target), fVertexBuffer(nullptr), fVertices(nullptr) {}
lock(int vertexCount)108 void* lock(int vertexCount) override {
109 fVertexCount = vertexCount;
110 fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
111 return fVertices;
112 }
unlock(int actualCount)113 void unlock(int actualCount) override {
114 fTarget->putBackVertices(fVertexCount - actualCount, stride());
115 fVertices = nullptr;
116 }
vertexBuffer() const117 const GrBuffer* vertexBuffer() const { return fVertexBuffer; }
firstVertex() const118 int firstVertex() const { return fFirstVertex; }
119 private:
120 GrMeshDrawOp::Target* fTarget;
121 const GrBuffer* fVertexBuffer;
122 int fVertexCount;
123 int fFirstVertex;
124 void* fVertices;
125 };
126
127 } // namespace
128
GrTessellatingPathRenderer()129 GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
130 }
131
onCanDrawPath(const CanDrawPathArgs & args) const132 bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
133 // This path renderer can draw fill styles, and can do screenspace antialiasing via a
134 // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex
135 // ones to simpler algorithms. We pass on paths that have styles, though they may come back
136 // around after applying the styling information to the geometry to create a filled path. In
137 // the non-AA case, We skip paths thta don't have a key since the real advantage of this path
138 // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we
139 // accept paths without keys.
140 if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
141 return false;
142 }
143 if (GrAAType::kCoverage == args.fAAType) {
144 #ifdef SK_DISABLE_SCREENSPACE_TESS_AA_PATH_RENDERER
145 return false;
146 #else
147 SkPath path;
148 args.fShape->asPath(&path);
149 if (path.countVerbs() > 10) {
150 return false;
151 }
152 #endif
153 } else if (!args.fShape->hasUnstyledKey()) {
154 return false;
155 }
156 return true;
157 }
158
159 class TessellatingPathOp final : public GrMeshDrawOp {
160 public:
161 DEFINE_OP_CLASS_ID
162
Make(const GrColor & color,const GrShape & shape,const SkMatrix & viewMatrix,SkIRect devClipBounds,bool antiAlias)163 static std::unique_ptr<GrMeshDrawOp> Make(const GrColor& color,
164 const GrShape& shape,
165 const SkMatrix& viewMatrix,
166 SkIRect devClipBounds,
167 bool antiAlias) {
168 return std::unique_ptr<GrMeshDrawOp>(
169 new TessellatingPathOp(color, shape, viewMatrix, devClipBounds, antiAlias));
170 }
171
name() const172 const char* name() const override { return "TessellatingPathOp"; }
173
dumpInfo() const174 SkString dumpInfo() const override {
175 SkString string;
176 string.appendf("Color 0x%08x, aa: %d\n", fColor, fAntiAlias);
177 string.append(DumpPipelineInfo(*this->pipeline()));
178 string.append(INHERITED::dumpInfo());
179 return string;
180 }
181
182 private:
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const183 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
184 GrPipelineAnalysisCoverage* coverage) const override {
185 color->setToConstant(fColor);
186 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
187 }
188
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)189 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
190 optimizations.getOverrideColorIfSet(&fColor);
191 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
192 fNeedsLocalCoords = optimizations.readsLocalCoords();
193 }
194
getPath() const195 SkPath getPath() const {
196 SkASSERT(!fShape.style().applies());
197 SkPath path;
198 fShape.asPath(&path);
199 return path;
200 }
201
draw(Target * target,const GrGeometryProcessor * gp) const202 void draw(Target* target, const GrGeometryProcessor* gp) const {
203 SkASSERT(!fAntiAlias);
204 GrResourceProvider* rp = target->resourceProvider();
205 bool inverseFill = fShape.inverseFilled();
206 // construct a cache key from the path's genID and the view matrix
207 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
208 GrUniqueKey key;
209 static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
210 int shapeKeyDataCnt = fShape.unstyledKeySize();
211 SkASSERT(shapeKeyDataCnt >= 0);
212 GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
213 fShape.writeUnstyledKey(&builder[0]);
214 // For inverse fills, the tessellation is dependent on clip bounds.
215 if (inverseFill) {
216 memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds));
217 } else {
218 memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds));
219 }
220 builder.finish();
221 sk_sp<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
222 int actualCount;
223 SkScalar tol = GrPathUtils::kDefaultTolerance;
224 tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
225 if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
226 this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
227 return;
228 }
229
230 SkRect clipBounds = SkRect::Make(fDevClipBounds);
231
232 SkMatrix vmi;
233 if (!fViewMatrix.invert(&vmi)) {
234 return;
235 }
236 vmi.mapRect(&clipBounds);
237 bool isLinear;
238 bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
239 StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB);
240 int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator,
241 false, GrColor(), false, &isLinear);
242 if (count == 0) {
243 return;
244 }
245 this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
246 TessInfo info;
247 info.fTolerance = isLinear ? 0 : tol;
248 info.fCount = count;
249 key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
250 rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
251 }
252
drawAA(Target * target,const GrGeometryProcessor * gp) const253 void drawAA(Target* target, const GrGeometryProcessor* gp) const {
254 SkASSERT(fAntiAlias);
255 SkPath path = getPath();
256 if (path.isEmpty()) {
257 return;
258 }
259 SkRect clipBounds = SkRect::Make(fDevClipBounds);
260 path.transform(fViewMatrix);
261 SkScalar tol = GrPathUtils::kDefaultTolerance;
262 bool isLinear;
263 DynamicVertexAllocator allocator(gp->getVertexStride(), target);
264 int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator,
265 true, fColor, fCanTweakAlphaForCoverage,
266 &isLinear);
267 if (count == 0) {
268 return;
269 }
270 drawVertices(target, gp, allocator.vertexBuffer(), allocator.firstVertex(), count);
271 }
272
onPrepareDraws(Target * target) const273 void onPrepareDraws(Target* target) const override {
274 sk_sp<GrGeometryProcessor> gp;
275 {
276 using namespace GrDefaultGeoProcFactory;
277
278 Color color(fColor);
279 LocalCoords::Type localCoordsType = fNeedsLocalCoords
280 ? LocalCoords::kUsePosition_Type
281 : LocalCoords::kUnused_Type;
282 Coverage::Type coverageType;
283 if (fAntiAlias) {
284 color = Color(Color::kPremulGrColorAttribute_Type);
285 if (fCanTweakAlphaForCoverage) {
286 coverageType = Coverage::kSolid_Type;
287 } else {
288 coverageType = Coverage::kAttribute_Type;
289 }
290 } else {
291 coverageType = Coverage::kSolid_Type;
292 }
293 if (fAntiAlias) {
294 gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverageType,
295 localCoordsType, fViewMatrix);
296 } else {
297 gp = GrDefaultGeoProcFactory::Make(color, coverageType, localCoordsType,
298 fViewMatrix);
299 }
300 }
301 if (!gp.get()) {
302 return;
303 }
304 if (fAntiAlias) {
305 this->drawAA(target, gp.get());
306 } else {
307 this->draw(target, gp.get());
308 }
309 }
310
drawVertices(Target * target,const GrGeometryProcessor * gp,const GrBuffer * vb,int firstVertex,int count) const311 void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
312 int firstVertex, int count) const {
313 GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
314 : kTriangles_GrPrimitiveType;
315 GrMesh mesh;
316 mesh.init(primitiveType, vb, firstVertex, count);
317 target->draw(gp, mesh);
318 }
319
onCombineIfPossible(GrOp *,const GrCaps &)320 bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
321
TessellatingPathOp(const GrColor & color,const GrShape & shape,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,bool antiAlias)322 TessellatingPathOp(const GrColor& color,
323 const GrShape& shape,
324 const SkMatrix& viewMatrix,
325 const SkIRect& devClipBounds,
326 bool antiAlias)
327 : INHERITED(ClassID())
328 , fColor(color)
329 , fShape(shape)
330 , fViewMatrix(viewMatrix)
331 , fDevClipBounds(devClipBounds)
332 , fAntiAlias(antiAlias) {
333 SkRect devBounds;
334 viewMatrix.mapRect(&devBounds, shape.bounds());
335 if (shape.inverseFilled()) {
336 // Because the clip bounds are used to add a contour for inverse fills, they must also
337 // include the path bounds.
338 devBounds.join(SkRect::Make(fDevClipBounds));
339 }
340 this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
341 }
342
343 GrColor fColor;
344 GrShape fShape;
345 SkMatrix fViewMatrix;
346 SkIRect fDevClipBounds;
347 bool fAntiAlias;
348 bool fCanTweakAlphaForCoverage;
349 bool fNeedsLocalCoords;
350
351 typedef GrMeshDrawOp INHERITED;
352 };
353
onDrawPath(const DrawPathArgs & args)354 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
355 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
356 "GrTessellatingPathRenderer::onDrawPath");
357 SkIRect clipBoundsI;
358 args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
359 args.fRenderTargetContext->height(),
360 &clipBoundsI);
361 std::unique_ptr<GrMeshDrawOp> op =
362 TessellatingPathOp::Make(args.fPaint.getColor(),
363 *args.fShape,
364 *args.fViewMatrix,
365 clipBoundsI,
366 GrAAType::kCoverage == args.fAAType);
367 GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
368 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
369 args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
370 return true;
371 }
372
373 ///////////////////////////////////////////////////////////////////////////////////////////////////
374
375 #if GR_TEST_UTILS
376
DRAW_OP_TEST_DEFINE(TesselatingPathOp)377 DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
378 GrColor color = GrRandomColor(random);
379 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
380 SkPath path = GrTest::TestPath(random);
381 SkIRect devClipBounds = SkIRect::MakeLTRB(
382 random->nextU(), random->nextU(), random->nextU(), random->nextU());
383 devClipBounds.sort();
384 bool antiAlias = random->nextBool();
385 GrStyle style;
386 do {
387 GrTest::TestStyle(random, &style);
388 } while (!style.isSimpleFill());
389 GrShape shape(path, style);
390 return TessellatingPathOp::Make(color, shape, viewMatrix, devClipBounds, antiAlias);
391 }
392
393 #endif
394