• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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