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