• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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/effects/GrTextureEffect.h"
9 
10 #include "include/gpu/GrTexture.h"
11 #include "src/gpu/GrTexturePriv.h"
12 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
13 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
14 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
15 #include "src/sksl/SkSLCPP.h"
16 #include "src/sksl/SkSLUtil.h"
17 
18 using Mode = GrSamplerState::WrapMode;
19 using Filter = GrSamplerState::Filter;
20 
Sampling(const GrSurfaceProxy & proxy,GrSamplerState sampler,const SkRect & subset,const SkRect * domain,const GrCaps & caps)21 GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
22                                     GrSamplerState sampler,
23                                     const SkRect& subset,
24                                     const SkRect* domain,
25                                     const GrCaps& caps) {
26     struct Span {
27         float fA = 0.f, fB = 0.f;
28 
29         Span makeInset(float o) const {
30             Span r = {fA + o, fB - o};
31             if (r.fA > r.fB) {
32                 r.fA = r.fB = (r.fA + r.fB) / 2;
33             }
34             return r;
35         }
36 
37         bool contains(Span r) const { return fA <= r.fA && fB >= r.fB; }
38     };
39     struct Result1D {
40         ShaderMode fShaderMode;
41         Span fShaderSubset;
42         Span fShaderClamp;
43         Mode fHWMode;
44     };
45 
46     auto type = proxy.asTextureProxy()->textureType();
47     auto filter = sampler.filter();
48 
49     auto resolve = [type, &caps, filter](int size, Mode mode, Span subset, Span domain) {
50         Result1D r;
51         bool canDoHW = (mode != Mode::kClampToBorder || caps.clampToBorderSupport()) &&
52                        (mode == Mode::kClamp || caps.npotTextureTileSupport() || SkIsPow2(size)) &&
53                        (mode == Mode::kClamp || mode == Mode::kClampToBorder ||
54                         type == GrTextureType::k2D);
55         if (canDoHW && size > 0 && subset.fA <= 0 && subset.fB >= size) {
56             r.fShaderMode = ShaderMode::kNone;
57             r.fHWMode = mode;
58             r.fShaderSubset = r.fShaderClamp = {0, 0};
59             return r;
60         }
61 
62         r.fShaderSubset = subset;
63         bool domainIsSafe = false;
64         if (filter == Filter::kNearest) {
65             Span isubset{sk_float_floor(subset.fA), sk_float_ceil(subset.fB)};
66             if (domain.fA > isubset.fA && domain.fB < isubset.fB) {
67                 domainIsSafe = true;
68             }
69             // This inset prevents sampling neighboring texels that could occur when
70             // texture coords fall exactly at texel boundaries (depending on precision
71             // and GPU-specific snapping at the boundary).
72             r.fShaderClamp = isubset.makeInset(0.5f);
73         } else {
74             r.fShaderClamp = subset.makeInset(0.5f);
75             if (r.fShaderClamp.contains(domain)) {
76                 domainIsSafe = true;
77             }
78         }
79         if (domainIsSafe) {
80             // The domain of coords that will be used won't access texels outside of the subset.
81             // So the wrap mode effectively doesn't matter. We use kClamp since it is always
82             // supported.
83             r.fShaderMode = ShaderMode::kNone;
84             r.fHWMode = Mode::kClamp;
85             r.fShaderSubset = r.fShaderClamp = {0, 0};
86             return r;
87         }
88         r.fShaderMode = static_cast<ShaderMode>(mode);
89         r.fHWMode = Mode::kClamp;
90         return r;
91     };
92 
93     SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
94 
95     Span subsetX{subset.fLeft, subset.fRight};
96     auto domainX = domain ? Span{domain->fLeft, domain->fRight}
97                           : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
98     auto x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX);
99 
100     Span subsetY{subset.fTop, subset.fBottom};
101     auto domainY = domain ? Span{domain->fTop, domain->fBottom}
102                           : Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
103     auto y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY);
104 
105     fHWSampler = {x.fHWMode, y.fHWMode, filter};
106     fShaderModes[0] = x.fShaderMode;
107     fShaderModes[1] = y.fShaderMode;
108     fShaderSubset = {x.fShaderSubset.fA, y.fShaderSubset.fA,
109                      x.fShaderSubset.fB, y.fShaderSubset.fB};
110     fShaderClamp = {x.fShaderClamp.fA, y.fShaderClamp.fA,
111                     x.fShaderClamp.fB, y.fShaderClamp.fB};
112 }
113 
usesDecal() const114 bool GrTextureEffect::Sampling::usesDecal() const {
115     return fShaderModes[0] == ShaderMode::kDecal || fShaderModes[1] == ShaderMode::kDecal ||
116            fHWSampler.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
117            fHWSampler.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder;
118 }
119 
Make(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,Filter filter)120 std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
121                                                            SkAlphaType alphaType,
122                                                            const SkMatrix& matrix,
123                                                            Filter filter) {
124     return std::unique_ptr<GrFragmentProcessor>(
125             new GrTextureEffect(std::move(view), alphaType, matrix, Sampling(filter)));
126 }
127 
Make(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,GrSamplerState sampler,const GrCaps & caps)128 std::unique_ptr<GrFragmentProcessor> GrTextureEffect::Make(GrSurfaceProxyView view,
129                                                            SkAlphaType alphaType,
130                                                            const SkMatrix& matrix,
131                                                            GrSamplerState sampler,
132                                                            const GrCaps& caps) {
133     Sampling sampling(*view.proxy(), sampler, SkRect::Make(view.proxy()->dimensions()), nullptr,
134                       caps);
135     return std::unique_ptr<GrFragmentProcessor>(
136             new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
137 }
138 
MakeSubset(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,GrSamplerState sampler,const SkRect & subset,const GrCaps & caps)139 std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
140                                                                  SkAlphaType alphaType,
141                                                                  const SkMatrix& matrix,
142                                                                  GrSamplerState sampler,
143                                                                  const SkRect& subset,
144                                                                  const GrCaps& caps) {
145     Sampling sampling(*view.proxy(), sampler, subset, nullptr, caps);
146     return std::unique_ptr<GrFragmentProcessor>(
147             new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
148 }
149 
MakeSubset(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,GrSamplerState sampler,const SkRect & subset,const SkRect & domain,const GrCaps & caps)150 std::unique_ptr<GrFragmentProcessor> GrTextureEffect::MakeSubset(GrSurfaceProxyView view,
151                                                                  SkAlphaType alphaType,
152                                                                  const SkMatrix& matrix,
153                                                                  GrSamplerState sampler,
154                                                                  const SkRect& subset,
155                                                                  const SkRect& domain,
156                                                                  const GrCaps& caps) {
157     Sampling sampling(*view.proxy(), sampler, subset, &domain, caps);
158     return std::unique_ptr<GrFragmentProcessor>(
159             new GrTextureEffect(std::move(view), alphaType, matrix, sampling));
160 }
161 
GetFilterLogic(ShaderMode mode,GrSamplerState::Filter filter)162 GrTextureEffect::FilterLogic GrTextureEffect::GetFilterLogic(ShaderMode mode,
163                                                              GrSamplerState::Filter filter) {
164     switch (mode) {
165         case ShaderMode::kMirrorRepeat:
166         case ShaderMode::kNone:
167         case ShaderMode::kClamp:
168             return FilterLogic::kNone;
169         case ShaderMode::kRepeat:
170             switch (filter) {
171                 case GrSamplerState::Filter::kNearest:
172                     return FilterLogic::kNone;
173                 case GrSamplerState::Filter::kBilerp:
174                     return FilterLogic::kRepeatBilerp;
175                 case GrSamplerState::Filter::kMipMap:
176                     return FilterLogic::kRepeatMipMap;
177             }
178             SkUNREACHABLE;
179         case ShaderMode::kDecal:
180             return filter > GrSamplerState::Filter::kNearest ? FilterLogic::kDecalFilter
181                                                              : FilterLogic::kDecalNearest;
182     }
183     SkUNREACHABLE;
184 }
185 
onCreateGLSLInstance() const186 GrGLSLFragmentProcessor* GrTextureEffect::onCreateGLSLInstance() const {
187     class Impl : public GrGLSLFragmentProcessor {
188         UniformHandle fSubsetUni;
189         UniformHandle fClampUni;
190         UniformHandle fNormUni;
191 
192     public:
193         void emitCode(EmitArgs& args) override {
194             auto te = args.fFp.cast<GrTextureEffect>();
195             const char* coords;
196             if (args.fFp.isSampledWithExplicitCoords()) {
197                 coords = "_coords";
198             } else {
199                 coords = args.fTransformedCoords[0].fVaryingPoint.c_str();
200             }
201             auto* fb = args.fFragBuilder;
202             if (te.fShaderModes[0] == ShaderMode::kNone &&
203                 te.fShaderModes[1] == ShaderMode::kNone) {
204                 fb->codeAppendf("%s = ", args.fOutputColor);
205                 fb->appendTextureLookupAndBlend(args.fInputColor, SkBlendMode::kModulate,
206                                                 args.fTexSamplers[0], coords);
207                 fb->codeAppendf(";");
208             } else {
209                 // Here is the basic flow of the various ShaderModes are implemented in a series of
210                 // steps. Not all the steps apply to all the modes. We try to emit only the steps
211                 // that are necessary for the given x/y shader modes.
212                 //
213                 // 0) Start with interpolated coordinates (unnormalize if doing anything
214                 //    complicated).
215                 // 1) Map the coordinates into the subset range [Repeat and MirrorRepeat], or pass
216                 //    through output of 0).
217                 // 2) Clamp the coordinates to a 0.5 inset of the subset rect [Clamp, Repeat, and
218                 //    MirrorRepeat always or Decal only when filtering] or pass through output of
219                 //    1). The clamp rect collapses to a line or point it if the subset rect is less
220                 //    than one pixel wide/tall.
221                 // 3) Look up texture with output of 2) [All]
222                 // 3) Use the difference between 1) and 2) to apply filtering at edge [Repeat or
223                 //    Decal]. In the Repeat case this requires extra texture lookups on the other
224                 //    side of the subset (up to 3 more reads). Or if Decal and not filtering
225                 //    do a hard less than/greater than test with the subset rect.
226 
227                 // Convert possible projective texture coordinates into non-homogeneous half2.
228                 fb->codeAppendf(
229                         "float2 inCoord = %s;",
230                         fb->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint).c_str());
231 
232                 const auto& m = te.fShaderModes;
233                 const auto* texture = te.fSampler.proxy()->peekTexture();
234                 bool normCoords = texture->texturePriv().textureType() != GrTextureType::kRectangle;
235                 auto filter = te.fSampler.samplerState().filter();
236                 FilterLogic filterLogic[2] = {GetFilterLogic(m[0], filter),
237                                               GetFilterLogic(m[1], filter)};
238 
239                 auto modeUsesSubset = [](ShaderMode m) {
240                     return m == ShaderMode::kRepeat || m == ShaderMode::kMirrorRepeat ||
241                            m == ShaderMode::kDecal;
242                 };
243 
244                 auto modeUsesClamp = [filter](ShaderMode m) {
245                     return m != ShaderMode::kNone &&
246                            (m != ShaderMode::kDecal || filter > Filter::kNearest);
247                 };
248 
249                 bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
250                 bool useClamp [2] = {modeUsesClamp (m[0]), modeUsesClamp (m[1])};
251 
252                 const char* subsetName = nullptr;
253                 if (useSubset[0] || useSubset[1]) {
254                     fSubsetUni = args.fUniformHandler->addUniform(
255                             kFragment_GrShaderFlag, kFloat4_GrSLType, "subset", &subsetName);
256                 }
257 
258                 const char* clampName = nullptr;
259                 if (useClamp[0] || useClamp[1]) {
260                     fClampUni = args.fUniformHandler->addUniform(
261                             kFragment_GrShaderFlag, kFloat4_GrSLType, "clamp", &clampName);
262                 }
263 
264                 // To keep things a little simpler, when we have filtering logic in the shader we
265                 // operate on unnormalized texture coordinates. We add a uniform that stores
266                 // {w, h, 1/w, 1/h} in a float4.
267                 const char* norm = nullptr;
268                 if (normCoords && (filterLogic[0] != FilterLogic::kNone ||
269                                    filterLogic[1] != FilterLogic::kNone)) {
270                     // TODO: Detect support for textureSize() or polyfill textureSize() in SkSL and
271                     // always use?
272                     fNormUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
273                                                                 kFloat4_GrSLType, "norm", &norm);
274                     // TODO: Remove the normalization from the CoordTransform to skip unnormalizing
275                     // step here.
276                     fb->codeAppendf("inCoord *= %s.xy;", norm);
277                 }
278 
279                 // Generates a string to read at a coordinate, normalizing coords if necessary.
280                 auto read = [&](const char* coord) {
281                     SkString result;
282                     SkString normCoord;
283                     if (norm) {
284                         normCoord.printf("(%s) * %s.zw", coord, norm);
285                     } else {
286                         normCoord = coord;
287                     }
288                     fb->appendTextureLookup(&result, args.fTexSamplers[0], normCoord.c_str());
289                     return result;
290                 };
291 
292                 // Implements coord wrapping for kRepeat and kMirrorRepeat
293                 auto subsetCoord = [&](ShaderMode mode,
294                                        const char* coordSwizzle,
295                                        const char* subsetStartSwizzle,
296                                        const char* subsetStopSwizzle,
297                                        const char* extraCoord,
298                                        const char* coordWeight) {
299                     switch (mode) {
300                         // These modes either don't use the subset rect or don't need to map the
301                         // coords to be within the subset.
302                         case ShaderMode::kNone:
303                         case ShaderMode::kDecal:
304                         case ShaderMode::kClamp:
305                             fb->codeAppendf("subsetCoord.%s = inCoord.%s;", coordSwizzle,
306                                             coordSwizzle);
307                             break;
308                         case ShaderMode::kRepeat:
309                             if (filter == Filter::kMipMap) {
310                                 // The approach here is to generate two sets of texture coords that
311                                 // are both "moving" at the same speed (if not direction) as
312                                 // inCoords. We accomplish that by using two out of phase mirror
313                                 // repeat coords. We will always sample using both coords but the
314                                 // read from the upward sloping one is selected using a weight
315                                 // that transitions from one set to the other near the reflection
316                                 // point. Like the coords, the weight is a saw-tooth function,
317                                 // phase-shifted, vertically translated, and then clamped to 0..1.
318                                 // TODO: Skip this and use textureGrad() when available.
319                                 SkASSERT(extraCoord);
320                                 SkASSERT(coordWeight);
321                                 fb->codeAppend("{");
322                                 fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName,
323                                                 subsetStopSwizzle, subsetName, subsetStartSwizzle);
324                                 fb->codeAppendf("float w2 = 2 * w;");
325                                 fb->codeAppendf("float d = inCoord.%s - %s.%s;", coordSwizzle,
326                                                 subsetName, subsetStartSwizzle);
327                                 fb->codeAppend("float m = mod(d, w2);");
328                                 fb->codeAppend("float o = mix(m, w2 - m, step(w, m));");
329                                 fb->codeAppendf("subsetCoord.%s = o + %s.%s;", coordSwizzle,
330                                                 subsetName, subsetStartSwizzle);
331                                 fb->codeAppendf("%s = w - o + %s.%s;", extraCoord, subsetName,
332                                                 subsetStartSwizzle);
333                                 // coordWeight is used as the third param of mix() to blend between a
334                                 // sample taken using subsetCoord and a sample at extraCoord.
335                                 fb->codeAppend("float hw = w/2;");
336                                 fb->codeAppend("float n = mod(d - hw, w2);");
337                                 fb->codeAppendf(
338                                         "%s = saturate(half(mix(n, w2 - n, step(w, n)) - hw + "
339                                         "0.5));",
340                                         coordWeight);
341                                 fb->codeAppend("}");
342                             } else {
343                                 fb->codeAppendf(
344                                         "subsetCoord.%s = mod(inCoord.%s - %s.%s, %s.%s - %s.%s) + "
345                                         "%s.%s;",
346                                         coordSwizzle, coordSwizzle, subsetName, subsetStartSwizzle,
347                                         subsetName, subsetStopSwizzle, subsetName,
348                                         subsetStartSwizzle, subsetName, subsetStartSwizzle);
349                             }
350                             break;
351                         case ShaderMode::kMirrorRepeat: {
352                             fb->codeAppend("{");
353                             fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName,
354                                             subsetStopSwizzle, subsetName, subsetStartSwizzle);
355                             fb->codeAppendf("float w2 = 2 * w;");
356                             fb->codeAppendf("float m = mod(inCoord.%s - %s.%s, w2);", coordSwizzle,
357                                             subsetName, subsetStartSwizzle);
358                             fb->codeAppendf("subsetCoord.%s = mix(m, w2 - m, step(w, m)) + %s.%s;",
359                                             coordSwizzle, subsetName, subsetStartSwizzle);
360                             fb->codeAppend("}");
361                             break;
362                         }
363                     }
364                 };
365 
366                 auto clampCoord = [&](bool clamp,
367                                       const char* coordSwizzle,
368                                       const char* clampStartSwizzle,
369                                       const char* clampStopSwizzle) {
370                     if (clamp) {
371                         fb->codeAppendf("clampedCoord.%s = clamp(subsetCoord.%s, %s.%s, %s.%s);",
372                                         coordSwizzle, coordSwizzle, clampName, clampStartSwizzle,
373                                         clampName, clampStopSwizzle);
374                     } else {
375                         fb->codeAppendf("clampedCoord.%s = subsetCoord.%s;", coordSwizzle,
376                                         coordSwizzle);
377                     }
378                 };
379 
380                 // Insert vars for extra coords and blending weights for kRepeatMipMap.
381                 const char* extraRepeatCoordX  = nullptr;
382                 const char* repeatCoordWeightX = nullptr;
383                 const char* extraRepeatCoordY  = nullptr;
384                 const char* repeatCoordWeightY = nullptr;
385                 if (filterLogic[0] == FilterLogic::kRepeatMipMap) {
386                     fb->codeAppend("float extraRepeatCoordX; half repeatCoordWeightX;");
387                     extraRepeatCoordX   = "extraRepeatCoordX";
388                     repeatCoordWeightX  = "repeatCoordWeightX";
389                 }
390                 if (filterLogic[1] == FilterLogic::kRepeatMipMap) {
391                     fb->codeAppend("float extraRepeatCoordY; half repeatCoordWeightY;");
392                     extraRepeatCoordY   = "extraRepeatCoordY";
393                     repeatCoordWeightY  = "repeatCoordWeightY";
394                 }
395 
396                 // Apply subset rect and clamp rect to coords.
397                 fb->codeAppend("float2 subsetCoord;");
398                 subsetCoord(te.fShaderModes[0], "x", "x", "z", extraRepeatCoordX,
399                             repeatCoordWeightX);
400                 subsetCoord(te.fShaderModes[1], "y", "y", "w", extraRepeatCoordY,
401                             repeatCoordWeightY);
402                 fb->codeAppend("float2 clampedCoord;");
403                 clampCoord(useClamp[0], "x", "x", "z");
404                 clampCoord(useClamp[1], "y", "y", "w");
405 
406                 // Additional clamping for the extra coords for kRepeatMipMap.
407                 if (filterLogic[0] == FilterLogic::kRepeatMipMap) {
408                     fb->codeAppendf("extraRepeatCoordX = clamp(extraRepeatCoordX, %s.x, %s.z);",
409                                     clampName, clampName);
410                 }
411                 if (filterLogic[1] == FilterLogic::kRepeatMipMap) {
412                     fb->codeAppendf("extraRepeatCoordY = clamp(extraRepeatCoordY, %s.y, %s.w);",
413                                     clampName, clampName);
414                 }
415 
416                 // Do the 2 or 4 texture reads for kRepeatMipMap and then apply the weight(s)
417                 // to blend between them. If neither direction is kRepeatMipMap do a single
418                 // read at clampedCoord.
419                 if (filterLogic[0] == FilterLogic::kRepeatMipMap &&
420                     filterLogic[1] == FilterLogic::kRepeatMipMap) {
421                     fb->codeAppendf(
422                             "half4 textureColor ="
423                             "   mix(mix(%s, %s, repeatCoordWeightX),"
424                             "       mix(%s, %s, repeatCoordWeightX),"
425                             "       repeatCoordWeightY);",
426                             read("clampedCoord").c_str(),
427                             read("float2(extraRepeatCoordX, clampedCoord.y)").c_str(),
428                             read("float2(clampedCoord.x, extraRepeatCoordY)").c_str(),
429                             read("float2(extraRepeatCoordX, extraRepeatCoordY)").c_str());
430 
431                 } else if (filterLogic[0] == FilterLogic::kRepeatMipMap) {
432                     fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightX);",
433                                     read("clampedCoord").c_str(),
434                                     read("float2(extraRepeatCoordX, clampedCoord.y)").c_str());
435                 } else if (filterLogic[1] == FilterLogic::kRepeatMipMap) {
436                     fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightY);",
437                                     read("clampedCoord").c_str(),
438                                     read("float2(clampedCoord.x, extraRepeatCoordY)").c_str());
439                 } else {
440                     fb->codeAppendf("half4 textureColor = %s;", read("clampedCoord").c_str());
441                 }
442 
443                 // Strings for extra texture reads used only in kRepeatBilerp
444                 SkString repeatBilerpReadX;
445                 SkString repeatBilerpReadY;
446 
447                 // Calculate the amount the coord moved for clamping. This will be used
448                 // to implement shader-based filtering for kDecal and kRepeat.
449 
450                 if (filterLogic[0] == FilterLogic::kRepeatBilerp ||
451                     filterLogic[0] == FilterLogic::kDecalFilter) {
452                     fb->codeAppend("half errX = half(subsetCoord.x - clampedCoord.x);");
453                     fb->codeAppendf("float repeatCoordX = errX > 0 ? %s.x : %s.z;", clampName,
454                                     clampName);
455                     repeatBilerpReadX = read("float2(repeatCoordX, clampedCoord.y)");
456                 }
457                 if (filterLogic[1] == FilterLogic::kRepeatBilerp ||
458                     filterLogic[1] == FilterLogic::kDecalFilter) {
459                     fb->codeAppend("half errY = half(subsetCoord.y - clampedCoord.y);");
460                     fb->codeAppendf("float repeatCoordY = errY > 0 ? %s.y : %s.w;", clampName,
461                                     clampName);
462                     repeatBilerpReadY = read("float2(clampedCoord.x, repeatCoordY)");
463                 }
464 
465                 // Add logic for kRepeatBilerp. Do 1 or 3 more texture reads depending
466                 // on whether both modes are kRepeat and whether we're near a single subset edge
467                 // or a corner. Then blend the multiple reads using the err values calculated
468                 // above.
469                 const char* ifStr = "if";
470                 if (filterLogic[0] == FilterLogic::kRepeatBilerp &&
471                     filterLogic[1] == FilterLogic::kRepeatBilerp) {
472                     auto repeatBilerpReadXY = read("float2(repeatCoordX, repeatCoordY)");
473                     fb->codeAppendf(
474                             "if (errX != 0 && errY != 0) {"
475                             "    textureColor = mix(mix(textureColor, %s, errX),"
476                             "                       mix(%s, %s, errX),"
477                             "                       errY);"
478                             "}",
479                             repeatBilerpReadX.c_str(), repeatBilerpReadY.c_str(),
480                             repeatBilerpReadXY.c_str());
481                     ifStr = "else if";
482                 }
483                 if (filterLogic[0] == FilterLogic::kRepeatBilerp) {
484                     fb->codeAppendf(
485                             "%s (errX != 0) {"
486                             "    textureColor = mix(textureColor, %s, abs(errX));"
487                             "}",
488                             ifStr, repeatBilerpReadX.c_str());
489                 }
490                 if (filterLogic[1] == FilterLogic::kRepeatBilerp) {
491                     fb->codeAppendf(
492                             "%s (errY != 0) {"
493                             "    textureColor = mix(textureColor, %s, abs(errY));"
494                             "}",
495                             ifStr, repeatBilerpReadY.c_str());
496                 }
497 
498                 // Do soft edge shader filtering against transparent black for kDecalFilter using
499                 // the err values calculated above.
500                 if (filterLogic[0] == FilterLogic::kDecalFilter) {
501                     fb->codeAppendf(
502                             "textureColor = mix(textureColor, half4(0), min(abs(errX), 1));");
503                 }
504                 if (filterLogic[1] == FilterLogic::kDecalFilter) {
505                     fb->codeAppendf(
506                             "textureColor = mix(textureColor, half4(0), min(abs(errY), 1));");
507                 }
508 
509                 // Do hard-edge shader transition to transparent black for kDecalNearest at the
510                 // subset boundaries.
511                 if (filterLogic[0] == FilterLogic::kDecalNearest) {
512                     fb->codeAppendf(
513                             "if (inCoord.x < %s.x || inCoord.x > %s.z) {"
514                             "    textureColor = half4(0);"
515                             "}",
516                             subsetName, subsetName);
517                 }
518                 if (filterLogic[1] == FilterLogic::kDecalNearest) {
519                     fb->codeAppendf(
520                             "if (inCoord.y < %s.y || inCoord.y > %s.w) {"
521                             "    textureColor = half4(0);"
522                             "}",
523                             subsetName, subsetName);
524                 }
525                 fb->codeAppendf("%s = %s * textureColor;", args.fOutputColor, args.fInputColor);
526             }
527         }
528 
529     protected:
530         void onSetData(const GrGLSLProgramDataManager& pdm,
531                        const GrFragmentProcessor& fp) override {
532             const auto& te = fp.cast<GrTextureEffect>();
533 
534             const float w = te.fSampler.peekTexture()->width();
535             const float h = te.fSampler.peekTexture()->height();
536             const auto& s = te.fSubset;
537             const auto& c = te.fClamp;
538 
539             auto type = te.fSampler.peekTexture()->texturePriv().textureType();
540 
541             float norm[4] = {w, h, 1.f/w, 1.f/h};
542 
543             if (fNormUni.isValid()) {
544                 pdm.set4fv(fNormUni, 1, norm);
545                 SkASSERT(type != GrTextureType::kRectangle);
546             }
547 
548             auto pushRect = [&](float rect[4], UniformHandle uni) {
549                 if (te.fSampler.view().origin() == kBottomLeft_GrSurfaceOrigin) {
550                     rect[1] = h - rect[1];
551                     rect[3] = h - rect[3];
552                     std::swap(rect[1], rect[3]);
553                 }
554                 if (!fNormUni.isValid() && type != GrTextureType::kRectangle) {
555                     rect[0] *= norm[2];
556                     rect[2] *= norm[2];
557                     rect[1] *= norm[3];
558                     rect[3] *= norm[3];
559                 }
560                 pdm.set4fv(uni, 1, rect);
561             };
562 
563             if (fSubsetUni.isValid()) {
564                 float subset[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
565                 pushRect(subset, fSubsetUni);
566             }
567             if (fClampUni.isValid()) {
568                 float subset[] = {c.fLeft, c.fTop, c.fRight, c.fBottom};
569                 pushRect(subset, fClampUni);
570             }
571         }
572     };
573     return new Impl;
574 }
575 
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const576 void GrTextureEffect::onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const {
577     auto m0 = static_cast<uint32_t>(fShaderModes[0]);
578     auto m1 = static_cast<uint32_t>(fShaderModes[1]);
579     auto filter = fSampler.samplerState().filter();
580     auto l0 = static_cast<uint32_t>(GetFilterLogic(fShaderModes[0], filter));
581     auto l1 = static_cast<uint32_t>(GetFilterLogic(fShaderModes[1], filter));
582     b->add32((l0 << 24) | (l1 << 16) | (m0 << 8) | m1);
583 }
584 
onIsEqual(const GrFragmentProcessor & other) const585 bool GrTextureEffect::onIsEqual(const GrFragmentProcessor& other) const {
586     auto that = other.cast<GrTextureEffect>();
587     return fShaderModes[0] == that.fShaderModes[0] && fShaderModes[1] == that.fShaderModes[1] &&
588            fSubset == that.fSubset;
589 }
590 
GrTextureEffect(GrSurfaceProxyView view,SkAlphaType alphaType,const SkMatrix & matrix,const Sampling & sampling)591 GrTextureEffect::GrTextureEffect(GrSurfaceProxyView view, SkAlphaType alphaType,
592                                  const SkMatrix& matrix, const Sampling& sampling)
593         : GrFragmentProcessor(kGrTextureEffect_ClassID,
594                               ModulateForSamplerOptFlags(alphaType, sampling.usesDecal()))
595         , fCoordTransform(matrix, view.proxy(), view.origin())
596         , fSampler(std::move(view), sampling.fHWSampler)
597         , fSubset(sampling.fShaderSubset)
598         , fClamp(sampling.fShaderClamp)
599         , fShaderModes{sampling.fShaderModes[0], sampling.fShaderModes[1]} {
600     // We always compare the range even when it isn't used so assert we have canonical don't care
601     // values.
602     SkASSERT(fShaderModes[0] != ShaderMode::kNone || (fSubset.fLeft == 0 && fSubset.fRight == 0));
603     SkASSERT(fShaderModes[1] != ShaderMode::kNone || (fSubset.fTop == 0 && fSubset.fBottom == 0));
604     this->setTextureSamplerCnt(1);
605     this->addCoordTransform(&fCoordTransform);
606 }
607 
GrTextureEffect(const GrTextureEffect & src)608 GrTextureEffect::GrTextureEffect(const GrTextureEffect& src)
609         : INHERITED(kGrTextureEffect_ClassID, src.optimizationFlags())
610         , fCoordTransform(src.fCoordTransform)
611         , fSampler(src.fSampler)
612         , fSubset(src.fSubset)
613         , fClamp(src.fClamp)
614         , fShaderModes{src.fShaderModes[0], src.fShaderModes[1]} {
615     this->setTextureSamplerCnt(1);
616     this->addCoordTransform(&fCoordTransform);
617 }
618 
clone() const619 std::unique_ptr<GrFragmentProcessor> GrTextureEffect::clone() const {
620     return std::unique_ptr<GrFragmentProcessor>(new GrTextureEffect(*this));
621 }
622 
onTextureSampler(int) const623 const GrFragmentProcessor::TextureSampler& GrTextureEffect::onTextureSampler(int) const {
624     return fSampler;
625 }
626 
627 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureEffect);
628 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * testData)629 std::unique_ptr<GrFragmentProcessor> GrTextureEffect::TestCreate(GrProcessorTestData* testData) {
630     auto [view, ct, at] = testData->randomView();
631     Mode wrapModes[2];
632     GrTest::TestWrapModes(testData->fRandom, wrapModes);
633 
634     Filter filter;
635     if (view.asTextureProxy()->mipMapped() == GrMipMapped::kYes) {
636         switch (testData->fRandom->nextULessThan(3)) {
637             case 0:
638                 filter = Filter::kNearest;
639                 break;
640             case 1:
641                 filter = Filter::kBilerp;
642                 break;
643             default:
644                 filter = Filter::kMipMap;
645                 break;
646         }
647     } else {
648         filter = testData->fRandom->nextBool() ? Filter::kBilerp : Filter::kNearest;
649     }
650     GrSamplerState params(wrapModes, filter);
651 
652     const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
653     return GrTextureEffect::Make(std::move(view), at, matrix, params, *testData->caps());
654 }
655 #endif
656