xref: /aosp_15_r20/external/skia/src/gpu/ganesh/effects/GrConvexPolyEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2014 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/effects/GrConvexPolyEffect.h"
9 
10 #include "include/core/SkPath.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkString.h"
13 #include "include/private/SkColorData.h"
14 #include "include/private/base/SkAssert.h"
15 #include "include/private/base/SkFloatingPoint.h"
16 #include "include/private/gpu/ganesh/GrTypesPriv.h"
17 #include "src/base/SkRandom.h"
18 #include "src/core/SkPathEnums.h"
19 #include "src/core/SkPathPriv.h"
20 #include "src/core/SkSLTypeShared.h"
21 #include "src/gpu/KeyBuilder.h"
22 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
23 #include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
24 #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
25 
26 #include <algorithm>
27 #include <cstddef>
28 #include <cstdint>
29 #include <tuple>
30 
31 struct GrShaderCaps;
32 
33 //////////////////////////////////////////////////////////////////////////////
34 
Make(std::unique_ptr<GrFragmentProcessor> inputFP,GrClipEdgeType type,const SkPath & path)35 GrFPResult GrConvexPolyEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP,
36                                     GrClipEdgeType type, const SkPath& path) {
37     if (path.getSegmentMasks() != SkPath::kLine_SegmentMask || !path.isConvex()) {
38         return GrFPFailure(std::move(inputFP));
39     }
40 
41     SkPathFirstDirection dir = SkPathPriv::ComputeFirstDirection(path);
42     // The only way this should fail is if the clip is effectively a infinitely thin line. In that
43     // case nothing is inside the clip. It'd be nice to detect this at a higher level and either
44     // skip the draw or omit the clip element.
45     if (dir == SkPathFirstDirection::kUnknown) {
46         if (GrClipEdgeTypeIsInverseFill(type)) {
47             return GrFPSuccess(
48                     GrFragmentProcessor::ModulateRGBA(std::move(inputFP), SK_PMColor4fWHITE));
49         }
50         // This could use ConstColor instead of ModulateRGBA but it would trigger a debug print
51         // about a coverage processor not being compatible with the alpha-as-coverage optimization.
52         // We don't really care about this unlikely case so we just use ModulateRGBA to suppress
53         // the print.
54         return GrFPSuccess(
55                 GrFragmentProcessor::ModulateRGBA(std::move(inputFP), SK_PMColor4fTRANSPARENT));
56     }
57 
58     SkScalar        edges[3 * kMaxEdges];
59     SkPoint         pts[4];
60     SkPath::Verb    verb;
61     SkPath::Iter    iter(path, true);
62 
63     // SkPath considers itself convex so long as there is a convex contour within it,
64     // regardless of any degenerate contours such as a string of moveTos before it.
65     // Iterate here to consume any degenerate contours and only process the points
66     // on the actual convex contour.
67     int n = 0;
68     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
69         switch (verb) {
70             case SkPath::kMove_Verb:
71             case SkPath::kClose_Verb:
72                 break;
73             case SkPath::kLine_Verb: {
74                 if (n >= kMaxEdges) {
75                     return GrFPFailure(std::move(inputFP));
76                 }
77                 if (pts[0] != pts[1]) {
78                     SkVector v = pts[1] - pts[0];
79                     v.normalize();
80                     if (SkPathFirstDirection::kCCW == dir) {
81                         edges[3 * n] = v.fY;
82                         edges[3 * n + 1] = -v.fX;
83                     } else {
84                         edges[3 * n] = -v.fY;
85                         edges[3 * n + 1] = v.fX;
86                     }
87                     edges[3 * n + 2] = -(edges[3 * n] * pts[1].fX + edges[3 * n + 1] * pts[1].fY);
88                     ++n;
89                 }
90                 break;
91             }
92             default:
93                 // Non-linear segment so not a polygon.
94                 return GrFPFailure(std::move(inputFP));
95         }
96     }
97 
98     if (path.isInverseFillType()) {
99         type = GrInvertClipEdgeType(type);
100     }
101     return GrConvexPolyEffect::Make(std::move(inputFP), type, n, edges);
102 }
103 
~GrConvexPolyEffect()104 GrConvexPolyEffect::~GrConvexPolyEffect() {}
105 
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const106 void GrConvexPolyEffect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
107     static_assert(kGrClipEdgeTypeCnt <= 8);
108     uint32_t key = (fEdgeCount << 3) | static_cast<int>(fEdgeType);
109     b->add32(key);
110 }
111 
onMakeProgramImpl() const112 std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrConvexPolyEffect::onMakeProgramImpl() const {
113     class Impl : public ProgramImpl {
114     public:
115         void emitCode(EmitArgs& args) override {
116             const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>();
117 
118             const char *edgeArrayName;
119             fEdgeUniform = args.fUniformHandler->addUniformArray(&cpe,
120                                                                  kFragment_GrShaderFlag,
121                                                                  SkSLType::kHalf3,
122                                                                  "edgeArray",
123                                                                  cpe.fEdgeCount,
124                                                                  &edgeArrayName);
125             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
126             fragBuilder->codeAppend("float alpha = 1.0;\n"
127                                     "float edge;\n");
128             for (int i = 0; i < cpe.fEdgeCount; ++i) {
129                 fragBuilder->codeAppendf("edge = dot(float3(%s[%d]), sk_FragCoord.xy1);\n",
130                                          edgeArrayName, i);
131                 if (GrClipEdgeTypeIsAA(cpe.fEdgeType)) {
132                     fragBuilder->codeAppend("alpha *= saturate(edge);\n");
133                 } else {
134                     fragBuilder->codeAppend("alpha *= step(0.5, edge);\n");
135                 }
136             }
137 
138             if (GrClipEdgeTypeIsInverseFill(cpe.fEdgeType)) {
139                 fragBuilder->codeAppend("alpha = 1.0 - alpha;\n");
140             }
141 
142             SkString inputSample = this->invokeChild(/*childIndex=*/0, args);
143 
144             fragBuilder->codeAppendf("return %s * half(alpha);\n", inputSample.c_str());
145         }
146 
147     private:
148         void onSetData(const GrGLSLProgramDataManager& pdman,
149                        const GrFragmentProcessor& fp) override {
150             const GrConvexPolyEffect& cpe = fp.cast<GrConvexPolyEffect>();
151             size_t n = 3*cpe.fEdgeCount;
152             if (!std::equal(fPrevEdges.begin(), fPrevEdges.begin() + n, cpe.fEdges.begin())) {
153                 pdman.set3fv(fEdgeUniform, cpe.fEdgeCount, cpe.fEdges.data());
154                 std::copy_n(cpe.fEdges.begin(), n, fPrevEdges.begin());
155             }
156         }
157 
158         GrGLSLProgramDataManager::UniformHandle              fEdgeUniform;
159         std::array<float, 3 * GrConvexPolyEffect::kMaxEdges> fPrevEdges = {SK_FloatNaN};
160     };
161 
162     return std::make_unique<Impl>();
163 }
164 
GrConvexPolyEffect(std::unique_ptr<GrFragmentProcessor> inputFP,GrClipEdgeType edgeType,int n,const float edges[])165 GrConvexPolyEffect::GrConvexPolyEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
166                                        GrClipEdgeType edgeType,
167                                        int n,
168                                        const float edges[])
169         : INHERITED(kGrConvexPolyEffect_ClassID,
170                     ProcessorOptimizationFlags(inputFP.get()) &
171                             kCompatibleWithCoverageAsAlpha_OptimizationFlag)
172         , fEdgeType(edgeType)
173         , fEdgeCount(n) {
174     // Factory function should have already ensured this.
175     SkASSERT(n <= kMaxEdges);
176     std::copy_n(edges, 3*n, fEdges.begin());
177     // Outset the edges by 0.5 so that a pixel with center on an edge is 50% covered in the AA case
178     // and 100% covered in the non-AA case.
179     for (int i = 0; i < n; ++i) {
180         fEdges[3 * i + 2] += SK_ScalarHalf;
181     }
182 
183     this->registerChild(std::move(inputFP));
184 }
185 
GrConvexPolyEffect(const GrConvexPolyEffect & that)186 GrConvexPolyEffect::GrConvexPolyEffect(const GrConvexPolyEffect& that)
187         : INHERITED(that)
188         , fEdgeType(that.fEdgeType)
189         , fEdgeCount(that.fEdgeCount) {
190     std::copy_n(that.fEdges.begin(), 3*that.fEdgeCount, fEdges.begin());
191 }
192 
clone() const193 std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::clone() const {
194     return std::unique_ptr<GrFragmentProcessor>(new GrConvexPolyEffect(*this));
195 }
196 
onIsEqual(const GrFragmentProcessor & other) const197 bool GrConvexPolyEffect::onIsEqual(const GrFragmentProcessor& other) const {
198     const GrConvexPolyEffect& cpe = other.cast<GrConvexPolyEffect>();
199     int n = 3*cpe.fEdgeCount;
200     return cpe.fEdgeType == fEdgeType   &&
201            cpe.fEdgeCount == fEdgeCount &&
202            std::equal(cpe.fEdges.begin(), cpe.fEdges.begin() + n, fEdges.begin());
203 }
204 
205 //////////////////////////////////////////////////////////////////////////////
206 
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConvexPolyEffect)207 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConvexPolyEffect)
208 
209 #if defined(GPU_TEST_UTILS)
210 std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::TestCreate(GrProcessorTestData* d) {
211     int count = d->fRandom->nextULessThan(kMaxEdges) + 1;
212     SkScalar edges[kMaxEdges * 3];
213     for (int i = 0; i < 3 * count; ++i) {
214         edges[i] = d->fRandom->nextSScalar1();
215     }
216 
217     bool success;
218     std::unique_ptr<GrFragmentProcessor> fp = d->inputFP();
219     do {
220         GrClipEdgeType edgeType =
221                 static_cast<GrClipEdgeType>(d->fRandom->nextULessThan(kGrClipEdgeTypeCnt));
222         std::tie(success, fp) = GrConvexPolyEffect::Make(std::move(fp), edgeType, count, edges);
223     } while (!success);
224     return fp;
225 }
226 #endif
227