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