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