• 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 "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 "GrResourceCache.h"
18 #include "GrResourceProvider.h"
19 #include "GrTessellator.h"
20 #include "SkGeometry.h"
21 
22 #include "GrSimpleMeshDrawOpHelper.h"
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)
108             , fTarget(target)
109             , fVertexBuffer(nullptr)
110             , fVertices(nullptr) {}
lock(int vertexCount)111     void* lock(int vertexCount) override {
112         fVertexCount = vertexCount;
113         fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
114         return fVertices;
115     }
unlock(int actualCount)116     void unlock(int actualCount) override {
117         fTarget->putBackVertices(fVertexCount - actualCount, stride());
118         fVertices = nullptr;
119     }
vertexBuffer() const120     const GrBuffer* vertexBuffer() const { return fVertexBuffer; }
firstVertex() const121     int firstVertex() const { return fFirstVertex; }
122 private:
123     GrMeshDrawOp::Target* fTarget;
124     const GrBuffer* fVertexBuffer;
125     int fVertexCount;
126     int fFirstVertex;
127     void* fVertices;
128 };
129 
130 }  // namespace
131 
GrTessellatingPathRenderer()132 GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
133 }
134 
onCanDrawPath(const CanDrawPathArgs & args) const135 bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
136     // This path renderer can draw fill styles, and can do screenspace antialiasing via a
137     // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex
138     // ones to simpler algorithms. We pass on paths that have styles, though they may come back
139     // around after applying the styling information to the geometry to create a filled path. In
140     // the non-AA case, We skip paths thta don't have a key since the real advantage of this path
141     // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we
142     // accept paths without keys.
143     if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
144         return false;
145     }
146     if (GrAAType::kCoverage == args.fAAType) {
147         SkPath path;
148         args.fShape->asPath(&path);
149         if (path.countVerbs() > 10) {
150             return false;
151         }
152     } else if (!args.fShape->hasUnstyledKey()) {
153         return false;
154     }
155     return true;
156 }
157 
158 namespace {
159 
160 class TessellatingPathOp final : public GrMeshDrawOp {
161 private:
162     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
163 
164 public:
165     DEFINE_OP_CLASS_ID
166 
Make(GrPaint && paint,const GrShape & shape,const SkMatrix & viewMatrix,SkIRect devClipBounds,GrAAType aaType,const GrUserStencilSettings * stencilSettings)167     static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint,
168                                           const GrShape& shape,
169                                           const SkMatrix& viewMatrix,
170                                           SkIRect devClipBounds,
171                                           GrAAType aaType,
172                                           const GrUserStencilSettings* stencilSettings) {
173         return Helper::FactoryHelper<TessellatingPathOp>(std::move(paint), shape, viewMatrix,
174                                                          devClipBounds, aaType, stencilSettings);
175     }
176 
name() const177     const char* name() const override { return "TessellatingPathOp"; }
178 
dumpInfo() const179     SkString dumpInfo() const override {
180         SkString string;
181         string.appendf("Color 0x%08x, aa: %d\n", fColor, fAntiAlias);
182         string += fHelper.dumpInfo();
183         string += INHERITED::dumpInfo();
184         return string;
185     }
186 
TessellatingPathOp(Helper::MakeArgs helperArgs,GrColor color,const GrShape & shape,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,GrAAType aaType,const GrUserStencilSettings * stencilSettings)187     TessellatingPathOp(Helper::MakeArgs helperArgs,
188                        GrColor color,
189                        const GrShape& shape,
190                        const SkMatrix& viewMatrix,
191                        const SkIRect& devClipBounds,
192                        GrAAType aaType,
193                        const GrUserStencilSettings* stencilSettings)
194             : INHERITED(ClassID())
195             , fHelper(helperArgs, aaType, stencilSettings)
196             , fColor(color)
197             , fShape(shape)
198             , fViewMatrix(viewMatrix)
199             , fDevClipBounds(devClipBounds)
200             , fAntiAlias(GrAAType::kCoverage == aaType) {
201         SkRect devBounds;
202         viewMatrix.mapRect(&devBounds, shape.bounds());
203         if (shape.inverseFilled()) {
204             // Because the clip bounds are used to add a contour for inverse fills, they must also
205             // include the path bounds.
206             devBounds.join(SkRect::Make(fDevClipBounds));
207         }
208         this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
209     }
210 
fixedFunctionFlags() const211     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
212 
finalize(const GrCaps & caps,const GrAppliedClip * clip)213     RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
214         GrProcessorAnalysisCoverage coverage = fAntiAlias
215                                                        ? GrProcessorAnalysisCoverage::kSingleChannel
216                                                        : GrProcessorAnalysisCoverage::kNone;
217         return fHelper.xpRequiresDstTexture(caps, clip, coverage, &fColor);
218     }
219 
220 private:
getPath() const221     SkPath getPath() const {
222         SkASSERT(!fShape.style().applies());
223         SkPath path;
224         fShape.asPath(&path);
225         return path;
226     }
227 
draw(Target * target,const GrGeometryProcessor * gp) const228     void draw(Target* target, const GrGeometryProcessor* gp) const {
229         SkASSERT(!fAntiAlias);
230         GrResourceProvider* rp = target->resourceProvider();
231         bool inverseFill = fShape.inverseFilled();
232         // construct a cache key from the path's genID and the view matrix
233         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
234         GrUniqueKey key;
235         static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
236         int shapeKeyDataCnt = fShape.unstyledKeySize();
237         SkASSERT(shapeKeyDataCnt >= 0);
238         GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
239         fShape.writeUnstyledKey(&builder[0]);
240         // For inverse fills, the tessellation is dependent on clip bounds.
241         if (inverseFill) {
242             memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds));
243         } else {
244             memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds));
245         }
246         builder.finish();
247         sk_sp<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
248         int actualCount;
249         SkScalar tol = GrPathUtils::kDefaultTolerance;
250         tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
251         if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
252             this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
253             return;
254         }
255 
256         SkRect clipBounds = SkRect::Make(fDevClipBounds);
257 
258         SkMatrix vmi;
259         if (!fViewMatrix.invert(&vmi)) {
260             return;
261         }
262         vmi.mapRect(&clipBounds);
263         bool isLinear;
264         bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
265         StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB);
266         int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator,
267                                                    false, GrColor(), false, &isLinear);
268         if (count == 0) {
269             return;
270         }
271         this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
272         TessInfo info;
273         info.fTolerance = isLinear ? 0 : tol;
274         info.fCount = count;
275         key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
276         rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
277     }
278 
drawAA(Target * target,const GrGeometryProcessor * gp) const279     void drawAA(Target* target, const GrGeometryProcessor* gp) const {
280         SkASSERT(fAntiAlias);
281         SkPath path = getPath();
282         if (path.isEmpty()) {
283             return;
284         }
285         SkRect clipBounds = SkRect::Make(fDevClipBounds);
286         path.transform(fViewMatrix);
287         SkScalar tol = GrPathUtils::kDefaultTolerance;
288         bool isLinear;
289         DynamicVertexAllocator allocator(gp->getVertexStride(), target);
290         int count =
291                 GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, true, fColor,
292                                                fHelper.compatibleWithAlphaAsCoverage(), &isLinear);
293         if (count == 0) {
294             return;
295         }
296         drawVertices(target, gp, allocator.vertexBuffer(), allocator.firstVertex(), count);
297     }
298 
onPrepareDraws(Target * target) const299     void onPrepareDraws(Target* target) const override {
300         sk_sp<GrGeometryProcessor> gp;
301         {
302             using namespace GrDefaultGeoProcFactory;
303 
304             Color color(fColor);
305             LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
306                                                         ? LocalCoords::kUsePosition_Type
307                                                         : LocalCoords::kUnused_Type;
308             Coverage::Type coverageType;
309             if (fAntiAlias) {
310                 color = Color(Color::kPremulGrColorAttribute_Type);
311                 if (fHelper.compatibleWithAlphaAsCoverage()) {
312                     coverageType = Coverage::kSolid_Type;
313                 } else {
314                     coverageType = Coverage::kAttribute_Type;
315                 }
316             } else {
317                 coverageType = Coverage::kSolid_Type;
318             }
319             if (fAntiAlias) {
320                 gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverageType,
321                                                                  localCoordsType, fViewMatrix);
322             } else {
323                 gp = GrDefaultGeoProcFactory::Make(color, coverageType, localCoordsType,
324                                                    fViewMatrix);
325             }
326         }
327         if (!gp.get()) {
328             return;
329         }
330         if (fAntiAlias) {
331             this->drawAA(target, gp.get());
332         } else {
333             this->draw(target, gp.get());
334         }
335     }
336 
drawVertices(Target * target,const GrGeometryProcessor * gp,const GrBuffer * vb,int firstVertex,int count) const337     void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
338                       int firstVertex, int count) const {
339         GrMesh mesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines : GrPrimitiveType::kTriangles);
340         mesh.setNonIndexedNonInstanced(count);
341         mesh.setVertexData(vb, firstVertex);
342         target->draw(gp, fHelper.makePipeline(target), mesh);
343     }
344 
onCombineIfPossible(GrOp *,const GrCaps &)345     bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
346 
347     Helper fHelper;
348     GrColor                 fColor;
349     GrShape                 fShape;
350     SkMatrix                fViewMatrix;
351     SkIRect                 fDevClipBounds;
352     bool                    fAntiAlias;
353 
354     typedef GrMeshDrawOp INHERITED;
355 };
356 
357 }  // anonymous namespace
358 
onDrawPath(const DrawPathArgs & args)359 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
360     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
361                               "GrTessellatingPathRenderer::onDrawPath");
362     SkIRect clipBoundsI;
363     args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
364                                       args.fRenderTargetContext->height(),
365                                       &clipBoundsI);
366     std::unique_ptr<GrDrawOp> op = TessellatingPathOp::Make(std::move(args.fPaint),
367                                                             *args.fShape,
368                                                             *args.fViewMatrix,
369                                                             clipBoundsI,
370                                                             args.fAAType,
371                                                             args.fUserStencilSettings);
372     args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
373     return true;
374 }
375 
376 ///////////////////////////////////////////////////////////////////////////////////////////////////
377 
378 #if GR_TEST_UTILS
379 
GR_DRAW_OP_TEST_DEFINE(TesselatingPathOp)380 GR_DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
381     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
382     SkPath path = GrTest::TestPath(random);
383     SkIRect devClipBounds = SkIRect::MakeLTRB(
384         random->nextU(), random->nextU(), random->nextU(), random->nextU());
385     devClipBounds.sort();
386     static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kMSAA, GrAAType::kCoverage};
387     GrAAType aaType;
388     do {
389         aaType = kAATypes[random->nextULessThan(SK_ARRAY_COUNT(kAATypes))];
390     } while(GrAAType::kMSAA == aaType && GrFSAAType::kUnifiedMSAA != fsaaType);
391     GrStyle style;
392     do {
393         GrTest::TestStyle(random, &style);
394     } while (!style.isSimpleFill());
395     GrShape shape(path, style);
396     return TessellatingPathOp::Make(std::move(paint), shape, viewMatrix, devClipBounds, aaType,
397                                     GrGetRandomStencil(random, context));
398 }
399 
400 #endif
401