• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 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/core/SkDistanceFieldGen.h"
9 #include "src/gpu/GrCaps.h"
10 #include "src/gpu/GrShaderCaps.h"
11 #include "src/gpu/GrTexture.h"
12 #include "src/gpu/effects/GrAtlasedShaderHelpers.h"
13 #include "src/gpu/effects/GrDistanceFieldGeoProc.h"
14 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
15 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
16 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
17 #include "src/gpu/glsl/GrGLSLVarying.h"
18 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
19 
20 // Assuming a radius of a little less than the diagonal of the fragment
21 #define SK_DistanceFieldAAFactor     "0.65"
22 
23 class GrDistanceFieldA8TextGeoProc::Impl : public ProgramImpl {
24 public:
setData(const GrGLSLProgramDataManager & pdman,const GrShaderCaps & shaderCaps,const GrGeometryProcessor & geomProc)25     void setData(const GrGLSLProgramDataManager& pdman,
26                  const GrShaderCaps& shaderCaps,
27                  const GrGeometryProcessor& geomProc) override {
28         const GrDistanceFieldA8TextGeoProc& dfa8gp = geomProc.cast<GrDistanceFieldA8TextGeoProc>();
29 
30 #ifdef SK_GAMMA_APPLY_TO_A8
31         float distanceAdjust = dfa8gp.fDistanceAdjust;
32         if (distanceAdjust != fDistanceAdjust) {
33             fDistanceAdjust = distanceAdjust;
34             pdman.set1f(fDistanceAdjustUni, distanceAdjust);
35         }
36 #endif
37 
38         const SkISize& atlasDimensions = dfa8gp.fAtlasDimensions;
39         SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight));
40 
41         if (fAtlasDimensions != atlasDimensions) {
42             pdman.set2f(fAtlasDimensionsInvUniform,
43                         1.0f / atlasDimensions.fWidth,
44                         1.0f / atlasDimensions.fHeight);
45             fAtlasDimensions = atlasDimensions;
46         }
47         SetTransform(pdman, shaderCaps, fLocalMatrixUniform, dfa8gp.fLocalMatrix, &fLocalMatrix);
48     }
49 
50 private:
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)51     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
52         const GrDistanceFieldA8TextGeoProc& dfTexEffect =
53                 args.fGeomProc.cast<GrDistanceFieldA8TextGeoProc>();
54         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
55 
56         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
57         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
58         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
59 
60         // emit attributes
61         varyingHandler->emitAttributes(dfTexEffect);
62 
63         const char* atlasDimensionsInvName;
64         fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr,
65                                                                 kVertex_GrShaderFlag,
66                                                                 kFloat2_GrSLType,
67                                                                 "AtlasDimensionsInv",
68                                                                 &atlasDimensionsInvName);
69 #ifdef SK_GAMMA_APPLY_TO_A8
70         // adjust based on gamma
71         const char* distanceAdjustUniName = nullptr;
72         // width, height, 1/(3*width)
73         fDistanceAdjustUni = uniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
74                                                         kHalf_GrSLType, "DistanceAdjust",
75                                                         &distanceAdjustUniName);
76 #endif
77 
78         // Setup pass through color
79         fragBuilder->codeAppendf("half4 %s;\n", args.fOutputColor);
80         varyingHandler->addPassThroughAttribute(dfTexEffect.fInColor.asShaderVar(),
81                                                 args.fOutputColor);
82 
83         // Setup position
84         gpArgs->fPositionVar = dfTexEffect.fInPosition.asShaderVar();
85         WriteLocalCoord(vertBuilder,
86                         uniformHandler,
87                         *args.fShaderCaps,
88                         gpArgs,
89                         gpArgs->fPositionVar,
90                         dfTexEffect.fLocalMatrix,
91                         &fLocalMatrixUniform);
92 
93         // add varyings
94         GrGLSLVarying uv, texIdx, st;
95         append_index_uv_varyings(args,
96                                  dfTexEffect.numTextureSamplers(),
97                                  dfTexEffect.fInTextureCoords.name(),
98                                  atlasDimensionsInvName,
99                                  &uv,
100                                  &texIdx,
101                                  &st);
102 
103         bool isUniformScale = (dfTexEffect.fFlags & kUniformScale_DistanceFieldEffectMask) ==
104                                                     kUniformScale_DistanceFieldEffectMask;
105         bool isSimilarity   = SkToBool(dfTexEffect.fFlags & kSimilarity_DistanceFieldEffectFlag  );
106         bool isGammaCorrect = SkToBool(dfTexEffect.fFlags & kGammaCorrect_DistanceFieldEffectFlag);
107         bool isAliased      = SkToBool(dfTexEffect.fFlags & kAliased_DistanceFieldEffectFlag     );
108 
109         // Use highp to work around aliasing issues
110         fragBuilder->codeAppendf("float2 uv = %s;\n", uv.fsIn());
111         fragBuilder->codeAppend("half4 texColor;");
112         append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
113                                    texIdx, "uv", "texColor");
114 
115         fragBuilder->codeAppend("half distance = "
116                       SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");");
117 #ifdef SK_GAMMA_APPLY_TO_A8
118         // adjust width based on gamma
119         fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
120 #endif
121 
122         fragBuilder->codeAppend("half afwidth;");
123         if (isUniformScale) {
124             // For uniform scale, we adjust for the effect of the transformation on the distance
125             // by using the length of the gradient of the t coordinate in the y direction.
126             // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space.
127 
128             // this gives us a smooth step across approximately one fragment
129             if (args.fShaderCaps->avoidDfDxForGradientsWhenPossible()) {
130                 fragBuilder->codeAppendf(
131                         "afwidth = abs(" SK_DistanceFieldAAFactor "*half(dFdy(%s.y)));", st.fsIn());
132             } else {
133                 fragBuilder->codeAppendf(
134                         "afwidth = abs(" SK_DistanceFieldAAFactor "*half(dFdx(%s.x)));", st.fsIn());
135             }
136         } else if (isSimilarity) {
137             // For similarity transform, we adjust the effect of the transformation on the distance
138             // by using the length of the gradient of the texture coordinates. We use st coordinates
139             // to ensure we're mapping 1:1 from texel space to pixel space.
140             // We use the y gradient because there is a bug in the Mali 400 in the x direction.
141 
142             // this gives us a smooth step across approximately one fragment
143             if (args.fShaderCaps->avoidDfDxForGradientsWhenPossible()) {
144                 fragBuilder->codeAppendf("half st_grad_len = length(half2(dFdy(%s)));", st.fsIn());
145             } else {
146                 fragBuilder->codeAppendf("half st_grad_len = length(half2(dFdx(%s)));", st.fsIn());
147             }
148             fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);");
149         } else {
150             // For general transforms, to determine the amount of correction we multiply a unit
151             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
152             // (which is the inverse transform for this fragment) and take the length of the result.
153             fragBuilder->codeAppend("half2 dist_grad = half2(float2(dFdx(distance), "
154                                                                    "dFdy(distance)));");
155             // the length of the gradient may be 0, so we need to check for this
156             // this also compensates for the Adreno, which likes to drop tiles on division by 0
157             fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);");
158             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
159             fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);");
160             fragBuilder->codeAppend("} else {");
161             fragBuilder->codeAppend("dist_grad = dist_grad*half(inversesqrt(dg_len2));");
162             fragBuilder->codeAppend("}");
163 
164             fragBuilder->codeAppendf("half2 Jdx = half2(dFdx(%s));", st.fsIn());
165             fragBuilder->codeAppendf("half2 Jdy = half2(dFdy(%s));", st.fsIn());
166             fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
167             fragBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
168 
169             // this gives us a smooth step across approximately one fragment
170             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
171         }
172 
173         if (isAliased) {
174             fragBuilder->codeAppend("half val = distance > 0 ? 1.0 : 0.0;");
175         } else if (isGammaCorrect) {
176             // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
177             // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want
178             // distance mapped linearly to coverage, so use a linear step:
179             fragBuilder->codeAppend(
180                 "half val = saturate((distance + afwidth) / (2.0 * afwidth));");
181         } else {
182             fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);");
183         }
184 
185         fragBuilder->codeAppendf("half4 %s = half4(val);", args.fOutputCoverage);
186     }
187 
188 private:
189 #ifdef SK_GAMMA_APPLY_TO_A8
190     float    fDistanceAdjust  = -1.f;
191 #endif
192     SkISize  fAtlasDimensions = {-1, -1};
193     SkMatrix fLocalMatrix     = SkMatrix::InvalidMatrix();
194 
195     UniformHandle fDistanceAdjustUni;
196     UniformHandle fAtlasDimensionsInvUniform;
197     UniformHandle fLocalMatrixUniform;
198 
199     using INHERITED = ProgramImpl;
200 };
201 
202 ///////////////////////////////////////////////////////////////////////////////
203 
GrDistanceFieldA8TextGeoProc(const GrShaderCaps & caps,const GrSurfaceProxyView * views,int numViews,GrSamplerState params,float distanceAdjust,uint32_t flags,const SkMatrix & localMatrix)204 GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(const GrShaderCaps& caps,
205                                                            const GrSurfaceProxyView* views,
206                                                            int numViews,
207                                                            GrSamplerState params,
208 #ifdef SK_GAMMA_APPLY_TO_A8
209                                                            float distanceAdjust,
210 #endif
211                                                            uint32_t flags,
212                                                            const SkMatrix& localMatrix)
213         : INHERITED(kGrDistanceFieldA8TextGeoProc_ClassID)
214         , fLocalMatrix(localMatrix)
215         , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
216 #ifdef SK_GAMMA_APPLY_TO_A8
217         , fDistanceAdjust(distanceAdjust)
218 #endif
219 {
220     SkASSERT(numViews <= kMaxTextures);
221     SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
222 
223     if (flags & kPerspective_DistanceFieldEffectFlag) {
224         fInPosition = {"inPosition", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
225     } else {
226         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
227     }
228     fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType };
229     fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
230                         caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType};
231     this->setVertexAttributes(&fInPosition, 3);
232 
233     if (numViews) {
234         fAtlasDimensions = views[0].proxy()->dimensions();
235     }
236     for (int i = 0; i < numViews; ++i) {
237         const GrSurfaceProxy* proxy = views[i].proxy();
238         SkASSERT(proxy);
239         SkASSERT(proxy->dimensions() == fAtlasDimensions);
240         fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
241     }
242     this->setTextureSamplerCnt(numViews);
243 }
244 
addNewViews(const GrSurfaceProxyView * views,int numViews,GrSamplerState params)245 void GrDistanceFieldA8TextGeoProc::addNewViews(const GrSurfaceProxyView* views,
246                                                int numViews,
247                                                GrSamplerState params) {
248     SkASSERT(numViews <= kMaxTextures);
249     // Just to make sure we don't try to add too many proxies
250     numViews = std::min(numViews, kMaxTextures);
251 
252     if (!fTextureSamplers[0].isInitialized()) {
253         fAtlasDimensions = views[0].proxy()->dimensions();
254     }
255 
256     for (int i = 0; i < numViews; ++i) {
257         const GrSurfaceProxy* proxy = views[i].proxy();
258         SkASSERT(proxy);
259         SkASSERT(proxy->dimensions() == fAtlasDimensions);
260         if (!fTextureSamplers[i].isInitialized()) {
261             fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
262         }
263     }
264     this->setTextureSamplerCnt(numViews);
265 }
266 
addToKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const267 void GrDistanceFieldA8TextGeoProc::addToKey(const GrShaderCaps& caps,
268                                             GrProcessorKeyBuilder* b) const {
269     uint32_t key = 0;
270     key |= fFlags;
271     key |= ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix) << 16;
272     b->add32(key);
273     b->add32(this->numTextureSamplers());
274 }
275 
makeProgramImpl(const GrShaderCaps &) const276 std::unique_ptr<GrGeometryProcessor::ProgramImpl> GrDistanceFieldA8TextGeoProc::makeProgramImpl(
277         const GrShaderCaps&) const {
278     return std::make_unique<Impl>();
279 }
280 
281 ///////////////////////////////////////////////////////////////////////////////
282 
283 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc);
284 
285 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)286 GrGeometryProcessor* GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) {
287     auto [view, ct, at] = d->randomAlphaOnlyView();
288 
289     GrSamplerState::WrapMode wrapModes[2];
290     GrTest::TestWrapModes(d->fRandom, wrapModes);
291     GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
292                                                    ? GrSamplerState::Filter::kLinear
293                                                    : GrSamplerState::Filter::kNearest);
294 
295     uint32_t flags = 0;
296     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
297     if (flags & kSimilarity_DistanceFieldEffectFlag) {
298         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
299     }
300     SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
301 #ifdef SK_GAMMA_APPLY_TO_A8
302     float lum = d->fRandom->nextF();
303 #endif
304     return GrDistanceFieldA8TextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(),
305                                               &view, 1,
306                                               samplerState,
307 #ifdef SK_GAMMA_APPLY_TO_A8
308                                               lum,
309 #endif
310                                               flags, localMatrix);
311 }
312 #endif
313 
314 ///////////////////////////////////////////////////////////////////////////////
315 
316 class GrDistanceFieldPathGeoProc::Impl : public ProgramImpl {
317 public:
setData(const GrGLSLProgramDataManager & pdman,const GrShaderCaps & shaderCaps,const GrGeometryProcessor & geomProc)318     void setData(const GrGLSLProgramDataManager& pdman,
319                  const GrShaderCaps& shaderCaps,
320                  const GrGeometryProcessor& geomProc) override {
321         const GrDistanceFieldPathGeoProc& dfpgp = geomProc.cast<GrDistanceFieldPathGeoProc>();
322 
323         // We always set the matrix uniform; it's either used to transform from local to device
324         // for the output position, or from device to local for the local coord variable.
325         SetTransform(pdman, shaderCaps, fMatrixUniform, dfpgp.fMatrix, &fMatrix);
326 
327         const SkISize& atlasDimensions = dfpgp.fAtlasDimensions;
328         SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight));
329         if (fAtlasDimensions != atlasDimensions) {
330             pdman.set2f(fAtlasDimensionsInvUniform,
331                         1.0f / atlasDimensions.fWidth,
332                         1.0f / atlasDimensions.fHeight);
333             fAtlasDimensions = atlasDimensions;
334         }
335     }
336 
337 private:
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)338     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
339         const GrDistanceFieldPathGeoProc& dfPathEffect =
340                 args.fGeomProc.cast<GrDistanceFieldPathGeoProc>();
341 
342         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
343 
344         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
345         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
346         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
347 
348         // emit attributes
349         varyingHandler->emitAttributes(dfPathEffect);
350 
351         const char* atlasDimensionsInvName;
352         fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr,
353                                                                 kVertex_GrShaderFlag,
354                                                                 kFloat2_GrSLType,
355                                                                 "AtlasDimensionsInv",
356                                                                 &atlasDimensionsInvName);
357 
358         GrGLSLVarying uv, texIdx, st;
359         append_index_uv_varyings(args,
360                                  dfPathEffect.numTextureSamplers(),
361                                  dfPathEffect.fInTextureCoords.name(),
362                                  atlasDimensionsInvName,
363                                  &uv,
364                                  &texIdx,
365                                  &st);
366 
367         // setup pass through color
368         fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
369         varyingHandler->addPassThroughAttribute(dfPathEffect.fInColor.asShaderVar(),
370                                                 args.fOutputColor);
371 
372         if (dfPathEffect.fMatrix.hasPerspective()) {
373             // Setup position (output position is transformed, local coords are pass through)
374             WriteOutputPosition(vertBuilder,
375                                 uniformHandler,
376                                 *args.fShaderCaps,
377                                 gpArgs,
378                                 dfPathEffect.fInPosition.name(),
379                                 dfPathEffect.fMatrix,
380                                 &fMatrixUniform);
381             gpArgs->fLocalCoordVar = dfPathEffect.fInPosition.asShaderVar();
382         } else {
383             // Setup position (output position is pass through, local coords are transformed)
384             WriteOutputPosition(vertBuilder, gpArgs, dfPathEffect.fInPosition.name());
385             WriteLocalCoord(vertBuilder,
386                             uniformHandler,
387                             *args.fShaderCaps,
388                             gpArgs,
389                             dfPathEffect.fInPosition.asShaderVar(),
390                             dfPathEffect.fMatrix,
391                             &fMatrixUniform);
392         }
393 
394         // Use highp to work around aliasing issues
395         fragBuilder->codeAppendf("float2 uv = %s;", uv.fsIn());
396         fragBuilder->codeAppend("half4 texColor;");
397         append_multitexture_lookup(args, dfPathEffect.numTextureSamplers(), texIdx, "uv",
398                                    "texColor");
399 
400         fragBuilder->codeAppend("half distance = "
401             SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");");
402 
403         fragBuilder->codeAppend("half afwidth;");
404         bool isUniformScale = (dfPathEffect.fFlags & kUniformScale_DistanceFieldEffectMask) ==
405                                                      kUniformScale_DistanceFieldEffectMask;
406         bool isSimilarity   = SkToBool(dfPathEffect.fFlags & kSimilarity_DistanceFieldEffectFlag  );
407         bool isGammaCorrect = SkToBool(dfPathEffect.fFlags & kGammaCorrect_DistanceFieldEffectFlag);
408         if (isUniformScale) {
409             // For uniform scale, we adjust for the effect of the transformation on the distance
410             // by using the length of the gradient of the t coordinate in the y direction.
411             // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space.
412 
413             // this gives us a smooth step across approximately one fragment
414             if (args.fShaderCaps->avoidDfDxForGradientsWhenPossible()) {
415                 fragBuilder->codeAppendf(
416                         "afwidth = abs(" SK_DistanceFieldAAFactor "*half(dFdy(%s.y)));", st.fsIn());
417             } else {
418                 fragBuilder->codeAppendf(
419                         "afwidth = abs(" SK_DistanceFieldAAFactor "*half(dFdx(%s.x)));", st.fsIn());
420             }
421         } else if (isSimilarity) {
422             // For similarity transform, we adjust the effect of the transformation on the distance
423             // by using the length of the gradient of the texture coordinates. We use st coordinates
424             // to ensure we're mapping 1:1 from texel space to pixel space.
425 
426             // this gives us a smooth step across approximately one fragment
427             if (args.fShaderCaps->avoidDfDxForGradientsWhenPossible()) {
428                 fragBuilder->codeAppendf("half st_grad_len = half(length(dFdy(%s)));", st.fsIn());
429             } else {
430                 fragBuilder->codeAppendf("half st_grad_len = half(length(dFdx(%s)));", st.fsIn());
431             }
432             fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);");
433         } else {
434             // For general transforms, to determine the amount of correction we multiply a unit
435             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
436             // (which is the inverse transform for this fragment) and take the length of the result.
437             fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance), "
438                                                             "dFdy(distance));");
439             // the length of the gradient may be 0, so we need to check for this
440             // this also compensates for the Adreno, which likes to drop tiles on division by 0
441             fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);");
442             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
443             fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);");
444             fragBuilder->codeAppend("} else {");
445             fragBuilder->codeAppend("dist_grad = dist_grad*half(inversesqrt(dg_len2));");
446             fragBuilder->codeAppend("}");
447 
448             fragBuilder->codeAppendf("half2 Jdx = half2(dFdx(%s));", st.fsIn());
449             fragBuilder->codeAppendf("half2 Jdy = half2(dFdy(%s));", st.fsIn());
450             fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
451             fragBuilder->codeAppend("                   dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
452 
453             // this gives us a smooth step across approximately one fragment
454             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
455         }
456         // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
457         // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
458         // mapped linearly to coverage, so use a linear step:
459         if (isGammaCorrect) {
460             fragBuilder->codeAppend(
461                 "half val = saturate((distance + afwidth) / (2.0 * afwidth));");
462         } else {
463             fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);");
464         }
465 
466         fragBuilder->codeAppendf("half4 %s = half4(val);", args.fOutputCoverage);
467     }
468 
469     SkMatrix      fMatrix;        // view matrix if perspective, local matrix otherwise
470     UniformHandle fMatrixUniform;
471 
472     SkISize       fAtlasDimensions;
473     UniformHandle fAtlasDimensionsInvUniform;
474 
475     using INHERITED = ProgramImpl;
476 };
477 
478 ///////////////////////////////////////////////////////////////////////////////
479 
GrDistanceFieldPathGeoProc(const GrShaderCaps & caps,const SkMatrix & matrix,bool wideColor,const GrSurfaceProxyView * views,int numViews,GrSamplerState params,uint32_t flags)480 GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(const GrShaderCaps& caps,
481                                                        const SkMatrix& matrix,
482                                                        bool wideColor,
483                                                        const GrSurfaceProxyView* views,
484                                                        int numViews,
485                                                        GrSamplerState params,
486                                                        uint32_t flags)
487         : INHERITED(kGrDistanceFieldPathGeoProc_ClassID)
488         , fMatrix(matrix)
489         , fFlags(flags & kNonLCD_DistanceFieldEffectMask) {
490     SkASSERT(numViews <= kMaxTextures);
491     SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
492 
493     fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
494     fInColor = MakeColorAttribute("inColor", wideColor);
495     fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
496                         caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType};
497     this->setVertexAttributes(&fInPosition, 3);
498 
499     if (numViews) {
500         fAtlasDimensions = views[0].proxy()->dimensions();
501     }
502 
503     for (int i = 0; i < numViews; ++i) {
504         const GrSurfaceProxy* proxy = views[i].proxy();
505         SkASSERT(proxy);
506         SkASSERT(proxy->dimensions() == fAtlasDimensions);
507         fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
508     }
509     this->setTextureSamplerCnt(numViews);
510 }
511 
addNewViews(const GrSurfaceProxyView * views,int numViews,GrSamplerState params)512 void GrDistanceFieldPathGeoProc::addNewViews(const GrSurfaceProxyView* views,
513                                              int numViews,
514                                              GrSamplerState params) {
515     SkASSERT(numViews <= kMaxTextures);
516     // Just to make sure we don't try to add too many proxies
517     numViews = std::min(numViews, kMaxTextures);
518 
519     if (!fTextureSamplers[0].isInitialized()) {
520         fAtlasDimensions = views[0].proxy()->dimensions();
521     }
522 
523     for (int i = 0; i < numViews; ++i) {
524         const GrSurfaceProxy* proxy = views[i].proxy();
525         SkASSERT(proxy);
526         SkASSERT(proxy->dimensions() == fAtlasDimensions);
527         if (!fTextureSamplers[i].isInitialized()) {
528             fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
529         }
530     }
531     this->setTextureSamplerCnt(numViews);
532 }
533 
addToKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const534 void GrDistanceFieldPathGeoProc::addToKey(const GrShaderCaps& caps,
535                                           GrProcessorKeyBuilder* b) const {
536     uint32_t key = fFlags;
537     key |= ProgramImpl::ComputeMatrixKey(caps, fMatrix) << 16;
538     key |= fMatrix.hasPerspective() << (16 + ProgramImpl::kMatrixKeyBits);
539     b->add32(key);
540     b->add32(this->numTextureSamplers());
541 }
542 
makeProgramImpl(const GrShaderCaps &) const543 std::unique_ptr<GrGeometryProcessor::ProgramImpl> GrDistanceFieldPathGeoProc::makeProgramImpl(
544         const GrShaderCaps&) const {
545     return std::make_unique<Impl>();
546 }
547 
548 ///////////////////////////////////////////////////////////////////////////////
549 
550 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc);
551 
552 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)553 GrGeometryProcessor* GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) {
554     auto [view, ct, at] = d->randomAlphaOnlyView();
555 
556     GrSamplerState::WrapMode wrapModes[2];
557     GrTest::TestWrapModes(d->fRandom, wrapModes);
558     GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
559                                                    ? GrSamplerState::Filter::kLinear
560                                                    : GrSamplerState::Filter::kNearest);
561 
562     uint32_t flags = 0;
563     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
564     if (flags & kSimilarity_DistanceFieldEffectFlag) {
565         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
566     }
567     SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
568     bool wideColor = d->fRandom->nextBool();
569     return GrDistanceFieldPathGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(),
570                                             localMatrix,
571                                             wideColor,
572                                             &view, 1,
573                                             samplerState,
574                                             flags);
575 }
576 #endif
577 
578 ///////////////////////////////////////////////////////////////////////////////
579 
580 class GrDistanceFieldLCDTextGeoProc::Impl : public ProgramImpl {
581 public:
setData(const GrGLSLProgramDataManager & pdman,const GrShaderCaps & shaderCaps,const GrGeometryProcessor & geomProc)582     void setData(const GrGLSLProgramDataManager& pdman,
583                  const GrShaderCaps& shaderCaps,
584                  const GrGeometryProcessor& geomProc) override {
585         SkASSERT(fDistanceAdjustUni.isValid());
586 
587         const GrDistanceFieldLCDTextGeoProc& dflcd = geomProc.cast<GrDistanceFieldLCDTextGeoProc>();
588         GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dflcd.fDistanceAdjust;
589         if (wa != fDistanceAdjust) {
590             pdman.set3f(fDistanceAdjustUni, wa.fR, wa.fG, wa.fB);
591             fDistanceAdjust = wa;
592         }
593 
594         const SkISize& atlasDimensions = dflcd.fAtlasDimensions;
595         SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight));
596         if (fAtlasDimensions != atlasDimensions) {
597             pdman.set2f(fAtlasDimensionsInvUniform,
598                         1.0f / atlasDimensions.fWidth,
599                         1.0f / atlasDimensions.fHeight);
600             fAtlasDimensions = atlasDimensions;
601         }
602         SetTransform(pdman, shaderCaps, fLocalMatrixUniform, dflcd.fLocalMatrix, &fLocalMatrix);
603     }
604 
605 private:
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)606     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
607         const GrDistanceFieldLCDTextGeoProc& dfTexEffect =
608                 args.fGeomProc.cast<GrDistanceFieldLCDTextGeoProc>();
609 
610         GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
611         GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
612         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
613 
614         // emit attributes
615         varyingHandler->emitAttributes(dfTexEffect);
616 
617         const char* atlasDimensionsInvName;
618         fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr,
619                                                                 kVertex_GrShaderFlag,
620                                                                 kFloat2_GrSLType,
621                                                                 "AtlasDimensionsInv",
622                                                                 &atlasDimensionsInvName);
623 
624         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
625 
626         // setup pass through color
627         fragBuilder->codeAppendf("half4 %s;\n", args.fOutputColor);
628         varyingHandler->addPassThroughAttribute(dfTexEffect.fInColor.asShaderVar(),
629                                                 args.fOutputColor);
630 
631         // Setup position
632         gpArgs->fPositionVar = dfTexEffect.fInPosition.asShaderVar();
633         WriteLocalCoord(vertBuilder,
634                         uniformHandler,
635                         *args.fShaderCaps,
636                         gpArgs,
637                         dfTexEffect.fInPosition.asShaderVar(),
638                         dfTexEffect.fLocalMatrix,
639                         &fLocalMatrixUniform);
640 
641         // set up varyings
642         GrGLSLVarying uv, texIdx, st;
643         append_index_uv_varyings(args,
644                                  dfTexEffect.numTextureSamplers(),
645                                  dfTexEffect.fInTextureCoords.name(),
646                                  atlasDimensionsInvName,
647                                  &uv,
648                                  &texIdx,
649                                  &st);
650 
651         GrGLSLVarying delta(kFloat_GrSLType);
652         varyingHandler->addVarying("Delta", &delta);
653         if (dfTexEffect.fFlags & kBGR_DistanceFieldEffectFlag) {
654             vertBuilder->codeAppendf("%s = -%s.x/3.0;", delta.vsOut(), atlasDimensionsInvName);
655         } else {
656             vertBuilder->codeAppendf("%s = %s.x/3.0;", delta.vsOut(), atlasDimensionsInvName);
657         }
658 
659         // add frag shader code
660         bool isUniformScale = (dfTexEffect.fFlags & kUniformScale_DistanceFieldEffectMask) ==
661                                                     kUniformScale_DistanceFieldEffectMask;
662         bool isSimilarity   = SkToBool(dfTexEffect.fFlags & kSimilarity_DistanceFieldEffectFlag  );
663         bool isGammaCorrect = SkToBool(dfTexEffect.fFlags & kGammaCorrect_DistanceFieldEffectFlag);
664 
665         // create LCD offset adjusted by inverse of transform
666         // Use highp to work around aliasing issues
667         fragBuilder->codeAppendf("float2 uv = %s;\n", uv.fsIn());
668 
669         if (isUniformScale) {
670             if (args.fShaderCaps->avoidDfDxForGradientsWhenPossible()) {
671                 fragBuilder->codeAppendf("half st_grad_len = half(abs(dFdy(%s.y)));", st.fsIn());
672             } else {
673                 fragBuilder->codeAppendf("half st_grad_len = half(abs(dFdx(%s.x)));", st.fsIn());
674             }
675             fragBuilder->codeAppendf("half2 offset = half2(half(st_grad_len*%s), 0.0);",
676                                      delta.fsIn());
677         } else if (isSimilarity) {
678             // For a similarity matrix with rotation, the gradient will not be aligned
679             // with the texel coordinate axes, so we need to calculate it.
680             if (args.fShaderCaps->avoidDfDxForGradientsWhenPossible()) {
681                 // We use dFdy instead and rotate -90 degrees to get the gradient in the x
682                 // direction.
683                 fragBuilder->codeAppendf("half2 st_grad = half2(dFdy(%s));", st.fsIn());
684                 fragBuilder->codeAppendf("half2 offset = half2(%s*float2(st_grad.y, -st_grad.x));",
685                                          delta.fsIn());
686             } else {
687                 fragBuilder->codeAppendf("half2 st_grad = half2(dFdx(%s));", st.fsIn());
688                 fragBuilder->codeAppendf("half2 offset = half(%s)*st_grad;", delta.fsIn());
689             }
690             fragBuilder->codeAppend("half st_grad_len = length(st_grad);");
691         } else {
692             fragBuilder->codeAppendf("half2 st = half2(%s);\n", st.fsIn());
693 
694             fragBuilder->codeAppend("half2 Jdx = half2(dFdx(st));");
695             fragBuilder->codeAppend("half2 Jdy = half2(dFdy(st));");
696             fragBuilder->codeAppendf("half2 offset = half2(half(%s))*Jdx;", delta.fsIn());
697         }
698 
699         // sample the texture by index
700         fragBuilder->codeAppend("half4 texColor;");
701         append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
702                                    texIdx, "uv", "texColor");
703 
704         // green is distance to uv center
705         fragBuilder->codeAppend("half3 distance;");
706         fragBuilder->codeAppend("distance.y = texColor.r;");
707         // red is distance to left offset
708         fragBuilder->codeAppend("half2 uv_adjusted = half2(uv) - offset;");
709         append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
710                                    texIdx, "uv_adjusted", "texColor");
711         fragBuilder->codeAppend("distance.x = texColor.r;");
712         // blue is distance to right offset
713         fragBuilder->codeAppend("uv_adjusted = half2(uv) + offset;");
714         append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(),
715                                    texIdx, "uv_adjusted", "texColor");
716         fragBuilder->codeAppend("distance.z = texColor.r;");
717 
718         fragBuilder->codeAppend("distance = "
719            "half3(" SK_DistanceFieldMultiplier ")*(distance - half3(" SK_DistanceFieldThreshold"));");
720 
721         // adjust width based on gamma
722         const char* distanceAdjustUniName = nullptr;
723         fDistanceAdjustUni = uniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
724                                                         kHalf3_GrSLType, "DistanceAdjust",
725                                                         &distanceAdjustUniName);
726         fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
727 
728         // To be strictly correct, we should compute the anti-aliasing factor separately
729         // for each color component. However, this is only important when using perspective
730         // transformations, and even then using a single factor seems like a reasonable
731         // trade-off between quality and speed.
732         fragBuilder->codeAppend("half afwidth;");
733         if (isSimilarity) {
734             // For similarity transform (uniform scale-only is a subset of this), we adjust for the
735             // effect of the transformation on the distance by using the length of the gradient of
736             // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel
737             // space to pixel space.
738 
739             // this gives us a smooth step across approximately one fragment
740             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;");
741         } else {
742             // For general transforms, to determine the amount of correction we multiply a unit
743             // vector pointing along the SDF gradient direction by the Jacobian of the st coords
744             // (which is the inverse transform for this fragment) and take the length of the result.
745             fragBuilder->codeAppend("half2 dist_grad = half2(half(dFdx(distance.r)), "
746                                                             "half(dFdy(distance.r)));");
747             // the length of the gradient may be 0, so we need to check for this
748             // this also compensates for the Adreno, which likes to drop tiles on division by 0
749             fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);");
750             fragBuilder->codeAppend("if (dg_len2 < 0.0001) {");
751             fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);");
752             fragBuilder->codeAppend("} else {");
753             fragBuilder->codeAppend("dist_grad = dist_grad*half(inversesqrt(dg_len2));");
754             fragBuilder->codeAppend("}");
755             fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
756             fragBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
757 
758             // this gives us a smooth step across approximately one fragment
759             fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
760         }
761 
762         // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
763         // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
764         // mapped linearly to coverage, so use a linear step:
765         if (isGammaCorrect) {
766             fragBuilder->codeAppendf("half4 %s = "
767                     "half4(saturate((distance + half3(afwidth)) / half3(2.0 * afwidth)), 1.0);",
768                     args.fOutputCoverage);
769         } else {
770             fragBuilder->codeAppendf(
771                     "half4 %s = half4(smoothstep(half3(-afwidth), half3(afwidth), distance), 1.0);",
772                     args.fOutputCoverage);
773         }
774     }
775 
776 private:
777     DistanceAdjust fDistanceAdjust  = DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
778     SkISize        fAtlasDimensions = {-1, -1};
779     SkMatrix       fLocalMatrix     = SkMatrix::InvalidMatrix();
780 
781     UniformHandle fDistanceAdjustUni;
782     UniformHandle fAtlasDimensionsInvUniform;
783     UniformHandle fLocalMatrixUniform;
784 };
785 
786 ///////////////////////////////////////////////////////////////////////////////
787 
GrDistanceFieldLCDTextGeoProc(const GrShaderCaps & caps,const GrSurfaceProxyView * views,int numViews,GrSamplerState params,DistanceAdjust distanceAdjust,uint32_t flags,const SkMatrix & localMatrix)788 GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(const GrShaderCaps& caps,
789                                                              const GrSurfaceProxyView* views,
790                                                              int numViews,
791                                                              GrSamplerState params,
792                                                              DistanceAdjust distanceAdjust,
793                                                              uint32_t flags,
794                                                              const SkMatrix& localMatrix)
795         : INHERITED(kGrDistanceFieldLCDTextGeoProc_ClassID)
796         , fLocalMatrix(localMatrix)
797         , fDistanceAdjust(distanceAdjust)
798         , fFlags(flags & kLCD_DistanceFieldEffectMask) {
799     SkASSERT(numViews <= kMaxTextures);
800     SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
801 
802     if (fFlags & kPerspective_DistanceFieldEffectFlag) {
803         fInPosition = {"inPosition", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
804     } else {
805         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
806     }
807     fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
808     fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
809                         caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType};
810     this->setVertexAttributes(&fInPosition, 3);
811 
812     if (numViews) {
813         fAtlasDimensions = views[0].proxy()->dimensions();
814     }
815 
816     for (int i = 0; i < numViews; ++i) {
817         const GrSurfaceProxy* proxy = views[i].proxy();
818         SkASSERT(proxy);
819         SkASSERT(proxy->dimensions() == fAtlasDimensions);
820         fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
821     }
822     this->setTextureSamplerCnt(numViews);
823 }
824 
addNewViews(const GrSurfaceProxyView * views,int numViews,GrSamplerState params)825 void GrDistanceFieldLCDTextGeoProc::addNewViews(const GrSurfaceProxyView* views,
826                                                 int numViews,
827                                                 GrSamplerState params) {
828     SkASSERT(numViews <= kMaxTextures);
829     // Just to make sure we don't try to add too many proxies
830     numViews = std::min(numViews, kMaxTextures);
831 
832     if (!fTextureSamplers[0].isInitialized()) {
833         fAtlasDimensions = views[0].proxy()->dimensions();
834     }
835 
836     for (int i = 0; i < numViews; ++i) {
837         const GrSurfaceProxy* proxy = views[i].proxy();
838         SkASSERT(proxy);
839         SkASSERT(proxy->dimensions() == fAtlasDimensions);
840         if (!fTextureSamplers[i].isInitialized()) {
841             fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
842         }
843     }
844     this->setTextureSamplerCnt(numViews);
845 }
846 
addToKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const847 void GrDistanceFieldLCDTextGeoProc::addToKey(const GrShaderCaps& caps,
848                                              GrProcessorKeyBuilder* b) const {
849     uint32_t key = 0;
850     key |= ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix);
851     key |= fFlags << 16;
852     b->add32(key);
853     b->add32(this->numTextureSamplers());
854 }
855 
makeProgramImpl(const GrShaderCaps &) const856 std::unique_ptr<GrGeometryProcessor::ProgramImpl> GrDistanceFieldLCDTextGeoProc::makeProgramImpl(
857         const GrShaderCaps&) const {
858     return std::make_unique<Impl>();
859 }
860 
861 ///////////////////////////////////////////////////////////////////////////////
862 
863 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc);
864 
865 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)866 GrGeometryProcessor* GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) {
867     auto [view, ct, at] = d->randomView();
868 
869     GrSamplerState::WrapMode wrapModes[2];
870     GrTest::TestWrapModes(d->fRandom, wrapModes);
871     GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
872                                                    ? GrSamplerState::Filter::kLinear
873                                                    : GrSamplerState::Filter::kNearest);
874     DistanceAdjust wa = { 0.0f, 0.1f, -0.1f };
875     uint32_t flags = kUseLCD_DistanceFieldEffectFlag;
876     flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
877     if (flags & kSimilarity_DistanceFieldEffectFlag) {
878         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
879     }
880     flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
881     SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
882 
883     return GrDistanceFieldLCDTextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(), &view,
884                                                1, samplerState, wa, flags, localMatrix);
885 }
886 #endif
887