Line data Source code
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 "GrPipelineBuilder.h"
18 : #include "GrResourceCache.h"
19 : #include "GrResourceProvider.h"
20 : #include "GrTessellator.h"
21 : #include "SkGeometry.h"
22 :
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:
42 : explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
43 : private:
44 : GrUniqueKeyInvalidatedMessage fMsg;
45 :
46 : void onChange() override {
47 : SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
48 : }
49 : };
50 :
51 0 : bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
52 0 : if (!vertexBuffer) {
53 0 : return false;
54 : }
55 0 : const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
56 0 : SkASSERT(data);
57 0 : const TessInfo* info = static_cast<const TessInfo*>(data->data());
58 0 : if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
59 0 : *actualCount = info->fCount;
60 0 : return true;
61 : }
62 0 : return false;
63 : }
64 :
65 0 : class StaticVertexAllocator : public GrTessellator::VertexAllocator {
66 : public:
67 0 : StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB)
68 0 : : VertexAllocator(stride)
69 : , fResourceProvider(resourceProvider)
70 : , fCanMapVB(canMapVB)
71 0 : , fVertices(nullptr) {
72 0 : }
73 0 : void* lock(int vertexCount) override {
74 0 : size_t size = vertexCount * stride();
75 0 : fVertexBuffer.reset(fResourceProvider->createBuffer(
76 0 : size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0));
77 0 : if (!fVertexBuffer.get()) {
78 0 : return nullptr;
79 : }
80 0 : if (fCanMapVB) {
81 0 : fVertices = fVertexBuffer->map();
82 : } else {
83 0 : fVertices = sk_malloc_throw(vertexCount * stride());
84 : }
85 0 : return fVertices;
86 : }
87 0 : void unlock(int actualCount) override {
88 0 : if (fCanMapVB) {
89 0 : fVertexBuffer->unmap();
90 : } else {
91 0 : fVertexBuffer->updateData(fVertices, actualCount * stride());
92 0 : sk_free(fVertices);
93 : }
94 0 : fVertices = nullptr;
95 0 : }
96 0 : GrBuffer* vertexBuffer() { return fVertexBuffer.get(); }
97 : private:
98 : sk_sp<GrBuffer> fVertexBuffer;
99 : GrResourceProvider* fResourceProvider;
100 : bool fCanMapVB;
101 : void* fVertices;
102 : };
103 :
104 0 : class DynamicVertexAllocator : public GrTessellator::VertexAllocator {
105 : public:
106 0 : DynamicVertexAllocator(size_t stride, GrLegacyMeshDrawOp::Target* target)
107 0 : : VertexAllocator(stride)
108 : , fTarget(target)
109 : , fVertexBuffer(nullptr)
110 0 : , fVertices(nullptr) {}
111 0 : void* lock(int vertexCount) override {
112 0 : fVertexCount = vertexCount;
113 0 : fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex);
114 0 : return fVertices;
115 : }
116 0 : void unlock(int actualCount) override {
117 0 : fTarget->putBackVertices(fVertexCount - actualCount, stride());
118 0 : fVertices = nullptr;
119 0 : }
120 0 : const GrBuffer* vertexBuffer() const { return fVertexBuffer; }
121 0 : int firstVertex() const { return fFirstVertex; }
122 : private:
123 : GrLegacyMeshDrawOp::Target* fTarget;
124 : const GrBuffer* fVertexBuffer;
125 : int fVertexCount;
126 : int fFirstVertex;
127 : void* fVertices;
128 : };
129 :
130 : } // namespace
131 :
132 0 : GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
133 0 : }
134 :
135 0 : 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 0 : if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
144 0 : return false;
145 : }
146 0 : if (GrAAType::kCoverage == args.fAAType) {
147 : #ifdef SK_DISABLE_SCREENSPACE_TESS_AA_PATH_RENDERER
148 0 : return false;
149 : #else
150 : SkPath path;
151 : args.fShape->asPath(&path);
152 : if (path.countVerbs() > 10) {
153 : return false;
154 : }
155 : #endif
156 0 : } else if (!args.fShape->hasUnstyledKey()) {
157 0 : return false;
158 : }
159 0 : return true;
160 : }
161 :
162 0 : class TessellatingPathOp final : public GrLegacyMeshDrawOp {
163 : public:
164 0 : DEFINE_OP_CLASS_ID
165 :
166 0 : static std::unique_ptr<GrLegacyMeshDrawOp> Make(const GrColor& color,
167 : const GrShape& shape,
168 : const SkMatrix& viewMatrix,
169 : SkIRect devClipBounds,
170 : bool antiAlias) {
171 : return std::unique_ptr<GrLegacyMeshDrawOp>(
172 0 : new TessellatingPathOp(color, shape, viewMatrix, devClipBounds, antiAlias));
173 : }
174 :
175 0 : const char* name() const override { return "TessellatingPathOp"; }
176 :
177 0 : SkString dumpInfo() const override {
178 0 : SkString string;
179 0 : string.appendf("Color 0x%08x, aa: %d\n", fColor, fAntiAlias);
180 0 : string.append(DumpPipelineInfo(*this->pipeline()));
181 0 : string.append(INHERITED::dumpInfo());
182 0 : return string;
183 : }
184 :
185 : private:
186 0 : void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
187 : GrProcessorAnalysisCoverage* coverage) const override {
188 0 : color->setToConstant(fColor);
189 0 : *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
190 0 : }
191 :
192 0 : void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
193 0 : optimizations.getOverrideColorIfSet(&fColor);
194 0 : fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
195 0 : fNeedsLocalCoords = optimizations.readsLocalCoords();
196 0 : }
197 :
198 0 : SkPath getPath() const {
199 0 : SkASSERT(!fShape.style().applies());
200 0 : SkPath path;
201 0 : fShape.asPath(&path);
202 0 : return path;
203 : }
204 :
205 0 : void draw(Target* target, const GrGeometryProcessor* gp) const {
206 0 : SkASSERT(!fAntiAlias);
207 0 : GrResourceProvider* rp = target->resourceProvider();
208 0 : bool inverseFill = fShape.inverseFilled();
209 : // construct a cache key from the path's genID and the view matrix
210 0 : static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
211 0 : GrUniqueKey key;
212 : static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t);
213 0 : int shapeKeyDataCnt = fShape.unstyledKeySize();
214 0 : SkASSERT(shapeKeyDataCnt >= 0);
215 0 : GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
216 0 : fShape.writeUnstyledKey(&builder[0]);
217 : // For inverse fills, the tessellation is dependent on clip bounds.
218 0 : if (inverseFill) {
219 0 : memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds));
220 : } else {
221 0 : memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds));
222 : }
223 0 : builder.finish();
224 0 : sk_sp<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
225 : int actualCount;
226 0 : SkScalar tol = GrPathUtils::kDefaultTolerance;
227 0 : tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
228 0 : if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
229 0 : this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
230 0 : return;
231 : }
232 :
233 0 : SkRect clipBounds = SkRect::Make(fDevClipBounds);
234 :
235 : SkMatrix vmi;
236 0 : if (!fViewMatrix.invert(&vmi)) {
237 0 : return;
238 : }
239 0 : vmi.mapRect(&clipBounds);
240 : bool isLinear;
241 0 : bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
242 0 : StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB);
243 0 : int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator,
244 0 : false, GrColor(), false, &isLinear);
245 0 : if (count == 0) {
246 0 : return;
247 : }
248 0 : this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
249 : TessInfo info;
250 0 : info.fTolerance = isLinear ? 0 : tol;
251 0 : info.fCount = count;
252 0 : key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
253 0 : rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
254 : }
255 :
256 0 : void drawAA(Target* target, const GrGeometryProcessor* gp) const {
257 0 : SkASSERT(fAntiAlias);
258 0 : SkPath path = getPath();
259 0 : if (path.isEmpty()) {
260 0 : return;
261 : }
262 0 : SkRect clipBounds = SkRect::Make(fDevClipBounds);
263 0 : path.transform(fViewMatrix);
264 0 : SkScalar tol = GrPathUtils::kDefaultTolerance;
265 : bool isLinear;
266 0 : DynamicVertexAllocator allocator(gp->getVertexStride(), target);
267 0 : int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator,
268 0 : true, fColor, fCanTweakAlphaForCoverage,
269 0 : &isLinear);
270 0 : if (count == 0) {
271 0 : return;
272 : }
273 0 : drawVertices(target, gp, allocator.vertexBuffer(), allocator.firstVertex(), count);
274 : }
275 :
276 0 : void onPrepareDraws(Target* target) const override {
277 0 : sk_sp<GrGeometryProcessor> gp;
278 : {
279 : using namespace GrDefaultGeoProcFactory;
280 :
281 0 : Color color(fColor);
282 0 : LocalCoords::Type localCoordsType = fNeedsLocalCoords
283 0 : ? LocalCoords::kUsePosition_Type
284 0 : : LocalCoords::kUnused_Type;
285 : Coverage::Type coverageType;
286 0 : if (fAntiAlias) {
287 0 : color = Color(Color::kPremulGrColorAttribute_Type);
288 0 : if (fCanTweakAlphaForCoverage) {
289 0 : coverageType = Coverage::kSolid_Type;
290 : } else {
291 0 : coverageType = Coverage::kAttribute_Type;
292 : }
293 : } else {
294 0 : coverageType = Coverage::kSolid_Type;
295 : }
296 0 : if (fAntiAlias) {
297 0 : gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverageType,
298 0 : localCoordsType, fViewMatrix);
299 : } else {
300 0 : gp = GrDefaultGeoProcFactory::Make(color, coverageType, localCoordsType,
301 0 : fViewMatrix);
302 : }
303 : }
304 0 : if (!gp.get()) {
305 0 : return;
306 : }
307 0 : if (fAntiAlias) {
308 0 : this->drawAA(target, gp.get());
309 : } else {
310 0 : this->draw(target, gp.get());
311 : }
312 : }
313 :
314 0 : void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
315 : int firstVertex, int count) const {
316 : GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
317 0 : : kTriangles_GrPrimitiveType;
318 0 : GrMesh mesh;
319 0 : mesh.init(primitiveType, vb, firstVertex, count);
320 0 : target->draw(gp, this->pipeline(), mesh);
321 0 : }
322 :
323 0 : bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
324 :
325 0 : TessellatingPathOp(const GrColor& color,
326 : const GrShape& shape,
327 : const SkMatrix& viewMatrix,
328 : const SkIRect& devClipBounds,
329 : bool antiAlias)
330 0 : : INHERITED(ClassID())
331 0 : , fColor(color)
332 : , fShape(shape)
333 : , fViewMatrix(viewMatrix)
334 : , fDevClipBounds(devClipBounds)
335 0 : , fAntiAlias(antiAlias) {
336 : SkRect devBounds;
337 0 : viewMatrix.mapRect(&devBounds, shape.bounds());
338 0 : if (shape.inverseFilled()) {
339 : // Because the clip bounds are used to add a contour for inverse fills, they must also
340 : // include the path bounds.
341 0 : devBounds.join(SkRect::Make(fDevClipBounds));
342 : }
343 0 : this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo);
344 0 : }
345 :
346 : GrColor fColor;
347 : GrShape fShape;
348 : SkMatrix fViewMatrix;
349 : SkIRect fDevClipBounds;
350 : bool fAntiAlias;
351 : bool fCanTweakAlphaForCoverage;
352 : bool fNeedsLocalCoords;
353 :
354 : typedef GrLegacyMeshDrawOp INHERITED;
355 : };
356 :
357 0 : bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
358 0 : GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
359 : "GrTessellatingPathRenderer::onDrawPath");
360 : SkIRect clipBoundsI;
361 0 : args.fClip->getConservativeBounds(args.fRenderTargetContext->width(),
362 0 : args.fRenderTargetContext->height(),
363 0 : &clipBoundsI);
364 : std::unique_ptr<GrLegacyMeshDrawOp> op =
365 0 : TessellatingPathOp::Make(args.fPaint.getColor(),
366 0 : *args.fShape,
367 0 : *args.fViewMatrix,
368 : clipBoundsI,
369 0 : GrAAType::kCoverage == args.fAAType);
370 0 : GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
371 0 : pipelineBuilder.setUserStencil(args.fUserStencilSettings);
372 0 : args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
373 0 : std::move(op));
374 0 : return true;
375 : }
376 :
377 : ///////////////////////////////////////////////////////////////////////////////////////////////////
378 :
379 : #if GR_TEST_UTILS
380 :
381 0 : DRAW_OP_TEST_DEFINE(TesselatingPathOp) {
382 0 : GrColor color = GrRandomColor(random);
383 0 : SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
384 0 : SkPath path = GrTest::TestPath(random);
385 : SkIRect devClipBounds = SkIRect::MakeLTRB(
386 0 : random->nextU(), random->nextU(), random->nextU(), random->nextU());
387 0 : devClipBounds.sort();
388 0 : bool antiAlias = random->nextBool();
389 0 : GrStyle style;
390 0 : do {
391 0 : GrTest::TestStyle(random, &style);
392 0 : } while (!style.isSimpleFill());
393 0 : GrShape shape(path, style);
394 0 : return TessellatingPathOp::Make(color, shape, viewMatrix, devClipBounds, antiAlias);
395 : }
396 :
397 : #endif
|