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