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 #include <stdio.h>
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 "GrResourceCache.h"
18 #include "GrResourceProvider.h"
19 #include "GrShape.h"
20 #include "GrSimpleMeshDrawOpHelper.h"
21 #include "GrStyle.h"
22 #include "GrTessellator.h"
23 #include "SkGeometry.h"
24 #include "ops/GrMeshDrawOp.h"
25
26 #ifndef GR_AA_TESSELLATOR_MAX_VERB_COUNT
27 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 10
28 #endif
29
30 /*
31 * This path renderer tessellates the path into triangles using GrTessellator, uploads the
32 * triangles to a vertex buffer, and renders them with a single draw call. It can do screenspace
33 * antialiasing with a one-pixel coverage ramp.
34 */
35 namespace {
36
37 struct TessInfo {
38 SkScalar fTolerance;
39 int fCount;
40 };
41
42 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
43 class PathInvalidator : public SkPathRef::GenIDChangeListener {
44 public:
PathInvalidator(const GrUniqueKey & key,uint32_t contextUniqueID)45 PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
46 : fMsg(key, contextUniqueID) {}
47
48 private:
49 GrUniqueKeyInvalidatedMessage fMsg;
50
onChange()51 void onChange() override {
52 SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
53 }
54 };
55
cache_match(GrBuffer * vertexBuffer,SkScalar tol,int * actualCount)56 bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
57 if (!vertexBuffer) {
58 return false;
59 }
60 const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
61 SkASSERT(data);
62 const TessInfo* info = static_cast<const TessInfo*>(data->data());
63 if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
64 *actualCount = info->fCount;
65 return true;
66 }
67 return false;
68 }
69
70 class StaticVertexAllocator : public GrTessellator::VertexAllocator {
71 public:
StaticVertexAllocator(size_t stride,GrResourceProvider * resourceProvider,bool canMapVB)72 StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
73 : VertexAllocator(stride)
74 , fResourceProvider(resourceProvider)
75 , fCanMapVB(canMapVB)
76 , fVertices(nullptr) {
77 }
lock(int vertexCount)78 void* lock(int vertexCount) override {
79 size_t size = vertexCount * stride();
80 fVertexBuffer =
81 fResourceProvider->createBuffer(size, kVertex_GrBufferType, kStatic_GrAccessPattern,
82 GrResourceProvider::Flags::kNone);
83 if (!fVertexBuffer.get()) {
84 return nullptr;
85 }
86 if (fCanMapVB) {
87 fVertices = fVertexBuffer->map();
88 } else {
89 fVertices = sk_malloc_throw(vertexCount * stride());
90 }
91 return fVertices;
92 }
unlock(int actualCount)93 void unlock(int actualCount) override {
94 if (fCanMapVB) {
95 fVertexBuffer->unmap();
96 } else {
97 fVertexBuffer->updateData(fVertices, actualCount * stride());
98 sk_free(fVertices);
99 }
100 fVertices = nullptr;
101 }
detachVertexBuffer()102 sk_sp<GrBuffer> detachVertexBuffer() { return std::move(fVertexBuffer); }
103
104 private:
105 sk_sp<GrBuffer> fVertexBuffer;
106 GrResourceProvider* fResourceProvider;
107 bool fCanMapVB;
108 void* fVertices;
109 };
110
111 class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
112 public:
DynamicVertexAllocator(size_t stride,GrMeshDrawOp::Target * target)113 DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target)
114 : VertexAllocator(stride)
115 , fTarget(target)
116 , fVertexBuffer(nullptr)
117 , fVertices(nullptr) {}
lock(int vertexCount)118 void* lock(int vertexCount) override {
119 fVertexCount = vertexCount;
120 fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
121 return fVertices;
122 }
unlock(int actualCount)123 void unlock(int actualCount) override {
124 fTarget->putBackVertices(fVertexCount - actualCount, stride());
125 fVertices = nullptr;
126 }
detachVertexBuffer() const127 sk_sp<const GrBuffer> detachVertexBuffer() const { return std::move(fVertexBuffer); }
firstVertex() const128 int firstVertex() const { return fFirstVertex; }
129
130 private:
131 GrMeshDrawOp::Target* fTarget;
132 sk_sp<const GrBuffer> fVertexBuffer;
133 int fVertexCount;
134 int fFirstVertex;
135 void* fVertices;
136 };
137
138 } // namespace
139
GrTessellatingPathRenderer()140 GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
141 }
142
143 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const144 GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
145 // This path renderer can draw fill styles, and can do screenspace antialiasing via a
146 // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex
147 // ones to simpler algorithms. We pass on paths that have styles, though they may come back
148 // around after applying the styling information to the geometry to create a filled path. In
149 // the non-AA case, We skip paths that don't have a key since the real advantage of this path
150 // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we
151 // accept paths without keys.
152 if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
153 return CanDrawPath::kNo;
154 }
155 if (GrAAType::kCoverage == args.fAAType) {
156 SkPath path;
157 args.fShape->asPath(&path);
158 if (path.countVerbs() > GR_AA_TESSELLATOR_MAX_VERB_COUNT) {
159 return CanDrawPath::kNo;
160 }
161 } else if (!args.fShape->hasUnstyledKey()) {
162 return CanDrawPath::kNo;
163 }
164 return CanDrawPath::kYes;
165 }
166
167 namespace {
168
169 class TessellatingPathOp final : public GrMeshDrawOp {
170 private:
171 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
172
173 public:
174 DEFINE_OP_CLASS_ID
175
Make(GrContext * context,GrPaint && paint,const GrShape & shape,const SkMatrix & viewMatrix,SkIRect devClipBounds,GrAAType aaType,const GrUserStencilSettings * stencilSettings)176 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
177 GrPaint&& paint,
178 const GrShape& shape,
179 const SkMatrix& viewMatrix,
180 SkIRect devClipBounds,
181 GrAAType aaType,
182 const GrUserStencilSettings* stencilSettings) {
183 return Helper::FactoryHelper<TessellatingPathOp>(context, std::move(paint), shape,
184 viewMatrix, devClipBounds,
185 aaType, stencilSettings);
186 }
187
name() const188 const char* name() const override { return "TessellatingPathOp"; }
189
visitProxies(const VisitProxyFunc & func,VisitorType) const190 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
191 fHelper.visitProxies(func);
192 }
193
194 #ifdef SK_DEBUG
dumpInfo() const195 SkString dumpInfo() const override {
196 SkString string;
197 string.appendf("Color 0x%08x, aa: %d\n", fColor.toBytes_RGBA(), fAntiAlias);
198 string += fHelper.dumpInfo();
199 string += INHERITED::dumpInfo();
200 return string;
201 }
202 #endif
203
TessellatingPathOp(Helper::MakeArgs helperArgs,const SkPMColor4f & color,const GrShape & shape,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,GrAAType aaType,const GrUserStencilSettings * stencilSettings)204 TessellatingPathOp(Helper::MakeArgs helperArgs,
205 const SkPMColor4f& color,
206 const GrShape& shape,
207 const SkMatrix& viewMatrix,
208 const SkIRect& devClipBounds,
209 GrAAType aaType,
210 const GrUserStencilSettings* stencilSettings)
211 : INHERITED(ClassID())
212 , fHelper(helperArgs, aaType, stencilSettings)
213 , fColor(color)
214 , fShape(shape)
215 , fViewMatrix(viewMatrix)
216 , fDevClipBounds(devClipBounds)
217 , fAntiAlias(GrAAType::kCoverage == aaType) {
218 SkRect devBounds;
219 viewMatrix.mapRect(&devBounds, shape.bounds());
220 if (shape.inverseFilled()) {
221 // Because the clip bounds are used to add a contour for inverse fills, they must also
222 // include the path bounds.
223 devBounds.join(SkRect::Make(fDevClipBounds));
224 }
225 this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
226 }
227
fixedFunctionFlags() const228 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
229
finalize(const GrCaps & caps,const GrAppliedClip * clip)230 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
231 GrProcessorAnalysisCoverage coverage = fAntiAlias
232 ? GrProcessorAnalysisCoverage::kSingleChannel
233 : GrProcessorAnalysisCoverage::kNone;
234 return fHelper.finalizeProcessors(caps, clip, coverage, &fColor);
235 }
236
237 private:
getPath() const238 SkPath getPath() const {
239 SkASSERT(!fShape.style().applies());
240 SkPath path;
241 fShape.asPath(&path);
242 return path;
243 }
244
draw(Target * target,sk_sp<const GrGeometryProcessor> gp,size_t vertexStride)245 void draw(Target* target, sk_sp<const GrGeometryProcessor> gp, size_t vertexStride) {
246 SkASSERT(!fAntiAlias);
247 GrResourceProvider* rp = target->resourceProvider();
248 bool inverseFill = fShape.inverseFilled();
249 // construct a cache key from the path's genID and the view matrix
250 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
251 GrUniqueKey key;
252 static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
253 int shapeKeyDataCnt = fShape.unstyledKeySize();
254 SkASSERT(shapeKeyDataCnt >= 0);
255 GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt, "Path");
256 fShape.writeUnstyledKey(&builder[0]);
257 // For inverse fills, the tessellation is dependent on clip bounds.
258 if (inverseFill) {
259 memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds));
260 } else {
261 memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds));
262 }
263 builder.finish();
264 sk_sp<GrBuffer> cachedVertexBuffer(rp->findByUniqueKey<GrBuffer>(key));
265 int actualCount;
266 SkScalar tol = GrPathUtils::kDefaultTolerance;
267 tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
268 if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
269 this->drawVertices(target, std::move(gp), std::move(cachedVertexBuffer), 0,
270 actualCount);
271 return;
272 }
273
274 SkRect clipBounds = SkRect::Make(fDevClipBounds);
275
276 SkMatrix vmi;
277 if (!fViewMatrix.invert(&vmi)) {
278 return;
279 }
280 vmi.mapRect(&clipBounds);
281 bool isLinear;
282 bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
283 StaticVertexAllocator allocator(vertexStride, rp, canMapVB);
284 int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator, false,
285 &isLinear);
286 if (count == 0) {
287 return;
288 }
289 sk_sp<GrBuffer> vb = allocator.detachVertexBuffer();
290 TessInfo info;
291 info.fTolerance = isLinear ? 0 : tol;
292 info.fCount = count;
293 fShape.addGenIDChangeListener(sk_make_sp<PathInvalidator>(key, target->contextUniqueID()));
294 key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
295 rp->assignUniqueKeyToResource(key, vb.get());
296
297 this->drawVertices(target, std::move(gp), std::move(vb), 0, count);
298 }
299
drawAA(Target * target,sk_sp<const GrGeometryProcessor> gp,size_t vertexStride)300 void drawAA(Target* target, sk_sp<const GrGeometryProcessor> gp, size_t vertexStride) {
301 SkASSERT(fAntiAlias);
302 SkPath path = getPath();
303 if (path.isEmpty()) {
304 return;
305 }
306 SkRect clipBounds = SkRect::Make(fDevClipBounds);
307 path.transform(fViewMatrix);
308 SkScalar tol = GrPathUtils::kDefaultTolerance;
309 bool isLinear;
310 DynamicVertexAllocator allocator(vertexStride, target);
311 int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, true,
312 &isLinear);
313 if (count == 0) {
314 return;
315 }
316 this->drawVertices(target, std::move(gp), allocator.detachVertexBuffer(),
317 allocator.firstVertex(), count);
318 }
319
onPrepareDraws(Target * target)320 void onPrepareDraws(Target* target) override {
321 sk_sp<GrGeometryProcessor> gp;
322 {
323 using namespace GrDefaultGeoProcFactory;
324
325 Color color(fColor);
326 LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
327 ? LocalCoords::kUsePosition_Type
328 : LocalCoords::kUnused_Type;
329 Coverage::Type coverageType;
330 if (fAntiAlias) {
331 if (fHelper.compatibleWithAlphaAsCoverage()) {
332 coverageType = Coverage::kAttributeTweakAlpha_Type;
333 } else {
334 coverageType = Coverage::kAttribute_Type;
335 }
336 } else {
337 coverageType = Coverage::kSolid_Type;
338 }
339 if (fAntiAlias) {
340 gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(target->caps().shaderCaps(),
341 color, coverageType,
342 localCoordsType, fViewMatrix);
343 } else {
344 gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
345 color, coverageType, localCoordsType,
346 fViewMatrix);
347 }
348 }
349 if (!gp.get()) {
350 return;
351 }
352 size_t vertexStride = gp->vertexStride();
353 if (fAntiAlias) {
354 this->drawAA(target, std::move(gp), vertexStride);
355 } else {
356 this->draw(target, std::move(gp), vertexStride);
357 }
358 }
359
drawVertices(Target * target,sk_sp<const GrGeometryProcessor> gp,sk_sp<const GrBuffer> vb,int firstVertex,int count)360 void drawVertices(Target* target, sk_sp<const GrGeometryProcessor> gp, sk_sp<const GrBuffer> vb,
361 int firstVertex, int count) {
362 GrMesh* mesh = target->allocMesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines
363 : GrPrimitiveType::kTriangles);
364 mesh->setNonIndexedNonInstanced(count);
365 mesh->setVertexData(std::move(vb), firstVertex);
366 auto pipe = fHelper.makePipeline(target);
367 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
368 }
369
370 Helper fHelper;
371 SkPMColor4f fColor;
372 GrShape fShape;
373 SkMatrix fViewMatrix;
374 SkIRect fDevClipBounds;
375 bool fAntiAlias;
376
377 typedef GrMeshDrawOp INHERITED;
378 };
379
380 } // anonymous namespace
381
onDrawPath(const DrawPathArgs & args)382 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
383 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
384 "GrTessellatingPathRenderer::onDrawPath");
385 SkIRect clipBoundsI;
386 args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
387 args.fRenderTargetContext->height(),
388 &clipBoundsI);
389 std::unique_ptr<GrDrawOp> op = TessellatingPathOp::Make(args.fContext,
390 std::move(args.fPaint),
391 *args.fShape,
392 *args.fViewMatrix,
393 clipBoundsI,
394 args.fAAType,
395 args.fUserStencilSettings);
396 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
397 return true;
398 }
399
400 ///////////////////////////////////////////////////////////////////////////////////////////////////
401
402 #if GR_TEST_UTILS
403
GR_DRAW_OP_TEST_DEFINE(TesselatingPathOp)404 GR_DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
405 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
406 SkPath path = GrTest::TestPath(random);
407 SkIRect devClipBounds = SkIRect::MakeLTRB(
408 random->nextU(), random->nextU(), random->nextU(), random->nextU());
409 devClipBounds.sort();
410 static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kMSAA, GrAAType::kCoverage};
411 GrAAType aaType;
412 do {
413 aaType = kAATypes[random->nextULessThan(SK_ARRAY_COUNT(kAATypes))];
414 } while(GrAAType::kMSAA == aaType && GrFSAAType::kUnifiedMSAA != fsaaType);
415 GrStyle style;
416 do {
417 GrTest::TestStyle(random, &style);
418 } while (!style.isSimpleFill());
419 GrShape shape(path, style);
420 return TessellatingPathOp::Make(context, std::move(paint), shape, viewMatrix, devClipBounds,
421 aaType, GrGetRandomStencil(random, context));
422 }
423
424 #endif
425