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