• 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/AALinearizingConvexPathRenderer.h"
9 
10 #include "include/core/SkString.h"
11 #include "src/core/SkGeometry.h"
12 #include "src/core/SkPathPriv.h"
13 #include "src/core/SkTraceEvent.h"
14 #include "src/gpu/BufferWriter.h"
15 #include "src/gpu/GrAuditTrail.h"
16 #include "src/gpu/GrCaps.h"
17 #include "src/gpu/GrDefaultGeoProcFactory.h"
18 #include "src/gpu/GrDrawOpTest.h"
19 #include "src/gpu/GrGeometryProcessor.h"
20 #include "src/gpu/GrOpFlushState.h"
21 #include "src/gpu/GrProcessor.h"
22 #include "src/gpu/GrProgramInfo.h"
23 #include "src/gpu/GrStyle.h"
24 #include "src/gpu/geometry/GrAAConvexTessellator.h"
25 #include "src/gpu/geometry/GrPathUtils.h"
26 #include "src/gpu/geometry/GrStyledShape.h"
27 #include "src/gpu/ops/GrMeshDrawOp.h"
28 #include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
29 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
30 
31 ///////////////////////////////////////////////////////////////////////////////
32 namespace skgpu::v1 {
33 
34 namespace {
35 
36 static const int DEFAULT_BUFFER_SIZE = 100;
37 
38 // The thicker the stroke, the harder it is to produce high-quality results using tessellation. For
39 // the time being, we simply drop back to software rendering above this stroke width.
40 static const SkScalar kMaxStrokeWidth = 20.0;
41 
42 // extract the result vertices and indices from the GrAAConvexTessellator
extract_verts(const GrAAConvexTessellator & tess,const SkMatrix * localCoordsMatrix,void * vertData,const GrVertexColor & color,uint16_t firstIndex,uint16_t * idxs)43 void extract_verts(const GrAAConvexTessellator& tess,
44                    const SkMatrix* localCoordsMatrix,
45                    void* vertData,
46                    const GrVertexColor& color,
47                    uint16_t firstIndex,
48                    uint16_t* idxs) {
49     VertexWriter verts{vertData};
50     for (int i = 0; i < tess.numPts(); ++i) {
51         SkPoint lc;
52         if (localCoordsMatrix) {
53             localCoordsMatrix->mapPoints(&lc, &tess.point(i), 1);
54         }
55         verts << tess.point(i) << color << VertexWriter::If(localCoordsMatrix, lc)
56               << tess.coverage(i);
57     }
58 
59     for (int i = 0; i < tess.numIndices(); ++i) {
60         idxs[i] = tess.index(i) + firstIndex;
61     }
62 }
63 
create_lines_only_gp(SkArenaAlloc * arena,bool tweakAlphaForCoverage,bool usesLocalCoords,bool wideColor)64 GrGeometryProcessor* create_lines_only_gp(SkArenaAlloc* arena,
65                                           bool tweakAlphaForCoverage,
66                                           bool usesLocalCoords,
67                                           bool wideColor) {
68     using namespace GrDefaultGeoProcFactory;
69 
70     Coverage::Type coverageType =
71         tweakAlphaForCoverage ? Coverage::kAttributeTweakAlpha_Type : Coverage::kAttribute_Type;
72     LocalCoords::Type localCoordsType =
73             usesLocalCoords ? LocalCoords::kHasExplicit_Type : LocalCoords::kUnused_Type;
74     Color::Type colorType =
75         wideColor ? Color::kPremulWideColorAttribute_Type : Color::kPremulGrColorAttribute_Type;
76 
77     return Make(arena, colorType, coverageType, localCoordsType, SkMatrix::I());
78 }
79 
80 class AAFlatteningConvexPathOp final : public GrMeshDrawOp {
81 private:
82     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
83 
84 public:
85     DEFINE_OP_CLASS_ID
86 
Make(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit,const GrUserStencilSettings * stencilSettings)87     static GrOp::Owner Make(GrRecordingContext* context,
88                             GrPaint&& paint,
89                             const SkMatrix& viewMatrix,
90                             const SkPath& path,
91                             SkScalar strokeWidth,
92                             SkStrokeRec::Style style,
93                             SkPaint::Join join,
94                             SkScalar miterLimit,
95                             const GrUserStencilSettings* stencilSettings) {
96         return Helper::FactoryHelper<AAFlatteningConvexPathOp>(context, std::move(paint),
97                                                                viewMatrix, path,
98                                                                strokeWidth, style, join, miterLimit,
99                                                                stencilSettings);
100     }
101 
AAFlatteningConvexPathOp(GrProcessorSet * processorSet,const SkPMColor4f & color,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit,const GrUserStencilSettings * stencilSettings)102     AAFlatteningConvexPathOp(GrProcessorSet* processorSet,
103                              const SkPMColor4f& color,
104                              const SkMatrix& viewMatrix,
105                              const SkPath& path,
106                              SkScalar strokeWidth,
107                              SkStrokeRec::Style style,
108                              SkPaint::Join join,
109                              SkScalar miterLimit,
110                              const GrUserStencilSettings* stencilSettings)
111             : INHERITED(ClassID()), fHelper(processorSet, GrAAType::kCoverage, stencilSettings) {
112         fPaths.emplace_back(
113                 PathData{viewMatrix, path, color, strokeWidth, miterLimit, style, join});
114 
115         // compute bounds
116         SkRect bounds = path.getBounds();
117         SkScalar w = strokeWidth;
118         if (w > 0) {
119             w /= 2;
120             SkScalar maxScale = viewMatrix.getMaxScale();
121             // We should not have a perspective matrix, thus we should have a valid scale.
122             SkASSERT(maxScale != -1);
123             if (SkPaint::kMiter_Join == join && w * maxScale > 1.f) {
124                 w *= miterLimit;
125             }
126             bounds.outset(w, w);
127         }
128         this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsHairline::kNo);
129     }
130 
name() const131     const char* name() const override { return "AAFlatteningConvexPathOp"; }
132 
visitProxies(const GrVisitProxyFunc & func) const133     void visitProxies(const GrVisitProxyFunc& func) const override {
134         if (fProgramInfo) {
135             fProgramInfo->visitFPProxies(func);
136         } else {
137             fHelper.visitProxies(func);
138         }
139     }
140 
fixedFunctionFlags() const141     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
142 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)143     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
144                                       GrClampType clampType) override {
145         return fHelper.finalizeProcessors(caps, clip, clampType,
146                                           GrProcessorAnalysisCoverage::kSingleChannel,
147                                           &fPaths.back().fColor, &fWideColor);
148     }
149 
150 private:
programInfo()151     GrProgramInfo* programInfo() override { return fProgramInfo; }
152 
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)153     void onCreateProgramInfo(const GrCaps* caps,
154                              SkArenaAlloc* arena,
155                              const GrSurfaceProxyView& writeView,
156                              bool usesMSAASurface,
157                              GrAppliedClip&& appliedClip,
158                              const GrDstProxyView& dstProxyView,
159                              GrXferBarrierFlags renderPassXferBarriers,
160                              GrLoadOp colorLoadOp) override {
161         GrGeometryProcessor* gp = create_lines_only_gp(arena,
162                                                        fHelper.compatibleWithCoverageAsAlpha(),
163                                                        fHelper.usesLocalCoords(),
164                                                        fWideColor);
165         if (!gp) {
166             SkDebugf("Couldn't create a GrGeometryProcessor\n");
167             return;
168         }
169 
170         fProgramInfo = fHelper.createProgramInfoWithStencil(caps, arena, writeView, usesMSAASurface,
171                                                             std::move(appliedClip), dstProxyView,
172                                                             gp, GrPrimitiveType::kTriangles,
173                                                             renderPassXferBarriers, colorLoadOp);
174     }
175 
recordDraw(GrMeshDrawTarget * target,int vertexCount,size_t vertexStride,void * vertices,int indexCount,uint16_t * indices)176     void recordDraw(GrMeshDrawTarget* target,
177                     int vertexCount, size_t vertexStride, void* vertices,
178                     int indexCount, uint16_t* indices) {
179         if (vertexCount == 0 || indexCount == 0) {
180             return;
181         }
182         sk_sp<const GrBuffer> vertexBuffer;
183         int firstVertex;
184         void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
185                                               &firstVertex);
186         if (!verts) {
187             SkDebugf("Could not allocate vertices\n");
188             return;
189         }
190         memcpy(verts, vertices, vertexCount * vertexStride);
191 
192         sk_sp<const GrBuffer> indexBuffer;
193         int firstIndex;
194         uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
195         if (!idxs) {
196             SkDebugf("Could not allocate indices\n");
197             return;
198         }
199         memcpy(idxs, indices, indexCount * sizeof(uint16_t));
200         GrSimpleMesh* mesh = target->allocMesh();
201         mesh->setIndexed(std::move(indexBuffer), indexCount, firstIndex, 0, vertexCount - 1,
202                          GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
203         fMeshes.push_back(mesh);
204     }
205 
onPrepareDraws(GrMeshDrawTarget * target)206     void onPrepareDraws(GrMeshDrawTarget* target) override {
207         if (!fProgramInfo) {
208             this->createProgramInfo(target);
209             if (!fProgramInfo) {
210                 return;
211             }
212         }
213 
214         size_t vertexStride =  fProgramInfo->geomProc().vertexStride();
215         int instanceCount = fPaths.count();
216 
217         int64_t vertexCount = 0;
218         int64_t indexCount = 0;
219         int64_t maxVertices = DEFAULT_BUFFER_SIZE;
220         int64_t maxIndices = DEFAULT_BUFFER_SIZE;
221         uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride);
222         uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t));
223         for (int i = 0; i < instanceCount; i++) {
224             const PathData& args = fPaths[i];
225             GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth,
226                                        args.fJoin, args.fMiterLimit);
227 
228             if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
229                 continue;
230             }
231 
232             int currentVertices = tess.numPts();
233             if (vertexCount + currentVertices > static_cast<int>(UINT16_MAX)) {
234                 // if we added the current instance, we would overflow the indices we can store in a
235                 // uint16_t. Draw what we've got so far and reset.
236                 this->recordDraw(target, vertexCount, vertexStride, vertices, indexCount, indices);
237                 vertexCount = 0;
238                 indexCount = 0;
239             }
240             if (vertexCount + currentVertices > maxVertices) {
241                 maxVertices = std::max(vertexCount + currentVertices, maxVertices * 2);
242                 if (maxVertices * vertexStride > SK_MaxS32) {
243                     sk_free(vertices);
244                     sk_free(indices);
245                     return;
246                 }
247                 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride);
248             }
249             int currentIndices = tess.numIndices();
250             if (indexCount + currentIndices > maxIndices) {
251                 maxIndices = std::max(indexCount + currentIndices, maxIndices * 2);
252                 if (maxIndices * sizeof(uint16_t) > SK_MaxS32) {
253                     sk_free(vertices);
254                     sk_free(indices);
255                     return;
256                 }
257                 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t));
258             }
259 
260             const SkMatrix* localCoordsMatrix = nullptr;
261             SkMatrix ivm;
262             if (fHelper.usesLocalCoords()) {
263                 if (!args.fViewMatrix.invert(&ivm)) {
264                     ivm = SkMatrix::I();
265                 }
266                 localCoordsMatrix = &ivm;
267             }
268 
269             extract_verts(tess, localCoordsMatrix, vertices + vertexStride * vertexCount,
270                           GrVertexColor(args.fColor, fWideColor), vertexCount,
271                           indices + indexCount);
272             vertexCount += currentVertices;
273             indexCount += currentIndices;
274         }
275         if (vertexCount <= SK_MaxS32 && indexCount <= SK_MaxS32) {
276             this->recordDraw(target, vertexCount, vertexStride, vertices, indexCount, indices);
277         }
278         sk_free(vertices);
279         sk_free(indices);
280     }
281 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)282     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
283         if (!fProgramInfo || fMeshes.isEmpty()) {
284             return;
285         }
286 
287         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
288         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
289         for (int i = 0; i < fMeshes.count(); ++i) {
290             flushState->drawMesh(*fMeshes[i]);
291         }
292     }
293 
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)294     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
295         AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>();
296         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
297             return CombineResult::kCannotCombine;
298         }
299 
300         fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
301         fWideColor |= that->fWideColor;
302         return CombineResult::kMerged;
303     }
304 
305 #if GR_TEST_UTILS
onDumpInfo() const306     SkString onDumpInfo() const override {
307         SkString string;
308         for (const auto& path : fPaths) {
309             string.appendf(
310                     "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, "
311                     "MiterLimit: %.2f\n",
312                     path.fColor.toBytes_RGBA(), path.fStrokeWidth, path.fStyle, path.fJoin,
313                     path.fMiterLimit);
314         }
315         string += fHelper.dumpInfo();
316         return string;
317     }
318 #endif
319 
320     struct PathData {
321         SkMatrix fViewMatrix;
322         SkPath fPath;
323         SkPMColor4f fColor;
324         SkScalar fStrokeWidth;
325         SkScalar fMiterLimit;
326         SkStrokeRec::Style fStyle;
327         SkPaint::Join fJoin;
328     };
329 
330     SkSTArray<1, PathData, true> fPaths;
331     Helper fHelper;
332     bool fWideColor;
333 
334     SkTDArray<GrSimpleMesh*> fMeshes;
335     GrProgramInfo*           fProgramInfo = nullptr;
336 
337     using INHERITED = GrMeshDrawOp;
338 };
339 
340 }  // anonymous namespace
341 
342 ///////////////////////////////////////////////////////////////////////////////////////////////////
343 
344 PathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const345 AALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
346     if (GrAAType::kCoverage != args.fAAType) {
347         return CanDrawPath::kNo;
348     }
349     if (!args.fShape->knownToBeConvex()) {
350         return CanDrawPath::kNo;
351     }
352     if (args.fShape->style().pathEffect()) {
353         return CanDrawPath::kNo;
354     }
355     if (args.fShape->inverseFilled()) {
356         return CanDrawPath::kNo;
357     }
358     if (args.fShape->bounds().width() <= 0 && args.fShape->bounds().height() <= 0) {
359         // Stroked zero length lines should draw, but this PR doesn't handle that case
360         return CanDrawPath::kNo;
361     }
362     const SkStrokeRec& stroke = args.fShape->style().strokeRec();
363 
364     if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
365         stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
366         if (!args.fViewMatrix->isSimilarity()) {
367             return CanDrawPath::kNo;
368         }
369         SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth();
370         if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) {
371             return CanDrawPath::kNo;
372         }
373         if (strokeWidth > kMaxStrokeWidth ||
374             !args.fShape->knownToBeClosed() ||
375             stroke.getJoin() == SkPaint::Join::kRound_Join) {
376             return CanDrawPath::kNo;
377         }
378         return CanDrawPath::kYes;
379     }
380     if (stroke.getStyle() != SkStrokeRec::kFill_Style) {
381         return CanDrawPath::kNo;
382     }
383     // This can almost handle perspective. It would need to use 3 component explicit local coords
384     // when there are FPs that require them. This is difficult to test because AAConvexPathRenderer
385     // takes almost all filled paths that could get here. So just avoid perspective fills.
386     if (args.fViewMatrix->hasPerspective()) {
387         return CanDrawPath::kNo;
388     }
389     return CanDrawPath::kYes;
390 }
391 
onDrawPath(const DrawPathArgs & args)392 bool AALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
393     GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
394                               "AALinearizingConvexPathRenderer::onDrawPath");
395     SkASSERT(args.fSurfaceDrawContext->numSamples() <= 1);
396     SkASSERT(!args.fShape->isEmpty());
397     SkASSERT(!args.fShape->style().pathEffect());
398 
399     SkPath path;
400     args.fShape->asPath(&path);
401     bool fill = args.fShape->style().isSimpleFill();
402     const SkStrokeRec& stroke = args.fShape->style().strokeRec();
403     SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth();
404     SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin();
405     SkScalar miterLimit = stroke.getMiter();
406 
407     GrOp::Owner op = AAFlatteningConvexPathOp::Make(
408             args.fContext, std::move(args.fPaint), *args.fViewMatrix, path, strokeWidth,
409             stroke.getStyle(), join, miterLimit, args.fUserStencilSettings);
410     args.fSurfaceDrawContext->addDrawOp(args.fClip, std::move(op));
411     return true;
412 }
413 
414 } // namespace skgpu::v1
415 
416 #if GR_TEST_UTILS
417 
GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp)418 GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
419     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
420     const SkPath& path = GrTest::TestPathConvex(random);
421 
422     SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style,
423                                      SkStrokeRec::kStroke_Style,
424                                      SkStrokeRec::kStrokeAndFill_Style };
425 
426     SkStrokeRec::Style style = styles[random->nextU() % 3];
427 
428     SkScalar strokeWidth = -1.f;
429     SkPaint::Join join = SkPaint::kMiter_Join;
430     SkScalar miterLimit = 0.5f;
431 
432     if (SkStrokeRec::kFill_Style != style) {
433         strokeWidth = random->nextRangeF(1.0f, 10.0f);
434         if (random->nextBool()) {
435             join = SkPaint::kMiter_Join;
436         } else {
437             join = SkPaint::kBevel_Join;
438         }
439         miterLimit = random->nextRangeF(0.5f, 2.0f);
440     }
441     const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
442     return skgpu::v1::AAFlatteningConvexPathOp::Make(context, std::move(paint), viewMatrix, path,
443                                                      strokeWidth, style, join, miterLimit,
444                                                      stencilSettings);
445 }
446 
447 #endif
448