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