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