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