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