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