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