1/*
2 * Copyright 2014 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/gpu/GrDefaultGeoProcFactory.h"
9
10#include "include/core/SkRefCnt.h"
11#include "src/core/SkArenaAlloc.h"
12#include "src/gpu/GrCaps.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
15#include "src/gpu/glsl/GrGLSLUniformHandler.h"
16#include "src/gpu/glsl/GrGLSLVarying.h"
17#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
18
19/*
20 * The default Geometry Processor simply takes position and multiplies it by the uniform view
21 * matrix. It also leaves coverage untouched.  Behind the scenes, we may add per vertex color or
22 * local coords.
23 */
24
25enum GPFlag {
26    kColorAttribute_GPFlag              = 0x1,
27    kColorAttributeIsWide_GPFlag        = 0x2,
28    kLocalCoordAttribute_GPFlag         = 0x4,
29    kCoverageAttribute_GPFlag           = 0x8,
30    kCoverageAttributeTweak_GPFlag      = 0x10,
31    kCoverageAttributeUnclamped_GPFlag  = 0x20,
32};
33
34class DefaultGeoProc : public GrGeometryProcessor {
35public:
36    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
37                                     uint32_t gpTypeFlags,
38                                     const SkPMColor4f& color,
39                                     const SkMatrix& viewMatrix,
40                                     const SkMatrix& localMatrix,
41                                     bool localCoordsWillBeRead,
42                                     uint8_t coverage) {
43        return arena->make([&](void* ptr) {
44            return new (ptr) DefaultGeoProc(gpTypeFlags, color, viewMatrix, localMatrix, coverage,
45                                            localCoordsWillBeRead);
46        });
47    }
48
49    const char* name() const override { return "DefaultGeometryProcessor"; }
50
51    SkString getShaderDfxInfo() const override {
52        SkString format;
53        format.printf("ShaderDfx_DefaultGeoProc_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d",
54            fFlags, fCoverage, fLocalCoordsWillBeRead, fInLocalCoords.isInitialized(),
55            fViewMatrix.isIdentity(), fViewMatrix.isScaleTranslate(), fViewMatrix.hasPerspective(),
56            fLocalMatrix.isIdentity(), fLocalMatrix.isScaleTranslate(), fLocalMatrix.hasPerspective());
57        return format;
58    }
59
60    void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
61        uint32_t key = fFlags;
62        key |= fCoverage == 0xff      ?  0x80 : 0;
63        key |= fLocalCoordsWillBeRead ? 0x100 : 0;
64
65        bool usesLocalMatrix = fLocalCoordsWillBeRead && !fInLocalCoords.isInitialized();
66        key = ProgramImpl::AddMatrixKeys(caps,
67                                         key,
68                                         fViewMatrix,
69                                         usesLocalMatrix ? fLocalMatrix : SkMatrix::I());
70        b->add32(key);
71    }
72
73    std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
74        return std::make_unique<Impl>();
75    }
76
77private:
78    class Impl : public ProgramImpl {
79    public:
80        void setData(const GrGLSLProgramDataManager& pdman,
81                     const GrShaderCaps& shaderCaps,
82                     const GrGeometryProcessor& geomProc) override {
83            const DefaultGeoProc& dgp = geomProc.cast<DefaultGeoProc>();
84
85            SetTransform(pdman, shaderCaps, fViewMatrixUniform, dgp.fViewMatrix, &fViewMatrixPrev);
86            SetTransform(pdman,
87                         shaderCaps,
88                         fLocalMatrixUniform,
89                         dgp.fLocalMatrix,
90                         &fLocalMatrixPrev);
91
92            if (!dgp.hasVertexColor() && dgp.fColor != fColor) {
93                pdman.set4fv(fColorUniform, 1, dgp.fColor.vec());
94                fColor = dgp.fColor;
95            }
96
97            if (dgp.fCoverage != fCoverage && !dgp.hasVertexCoverage()) {
98                pdman.set1f(fCoverageUniform, GrNormalizeByteToFloat(dgp.fCoverage));
99                fCoverage = dgp.fCoverage;
100            }
101        }
102
103    private:
104        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
105            const DefaultGeoProc& gp = args.fGeomProc.cast<DefaultGeoProc>();
106            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
107            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
108            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
109            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
110
111            // emit attributes
112            varyingHandler->emitAttributes(gp);
113
114            bool tweakAlpha = SkToBool(gp.fFlags & kCoverageAttributeTweak_GPFlag);
115            bool coverageNeedsSaturate = SkToBool(gp.fFlags & kCoverageAttributeUnclamped_GPFlag);
116            SkASSERT(!tweakAlpha || gp.hasVertexCoverage());
117            SkASSERT(!tweakAlpha || !coverageNeedsSaturate);
118
119            // Setup pass through color
120            fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
121            if (gp.hasVertexColor() || tweakAlpha) {
122                GrGLSLVarying varying(kHalf4_GrSLType);
123                varyingHandler->addVarying("color", &varying);
124
125                // Start with the attribute or with uniform color
126                if (gp.hasVertexColor()) {
127                    vertBuilder->codeAppendf("half4 color = %s;", gp.fInColor.name());
128                } else {
129                    const char* colorUniformName;
130                    fColorUniform = uniformHandler->addUniform(nullptr,
131                                                               kVertex_GrShaderFlag,
132                                                               kHalf4_GrSLType,
133                                                               "Color",
134                                                               &colorUniformName);
135                    vertBuilder->codeAppendf("half4 color = %s;", colorUniformName);
136                }
137
138                // Optionally fold coverage into alpha (color).
139                if (tweakAlpha) {
140                    vertBuilder->codeAppendf("color = color * %s;", gp.fInCoverage.name());
141                }
142                vertBuilder->codeAppendf("%s = color;\n", varying.vsOut());
143                fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
144            } else {
145                this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
146                                        &fColorUniform);
147            }
148
149            // Setup position
150            WriteOutputPosition(vertBuilder,
151                                uniformHandler,
152                                *args.fShaderCaps,
153                                gpArgs,
154                                gp.fInPosition.name(),
155                                gp.fViewMatrix,
156                                &fViewMatrixUniform);
157
158            // emit transforms using either explicit local coords or positions
159            if (gp.fInLocalCoords.isInitialized()) {
160                SkASSERT(gp.fLocalMatrix.isIdentity());
161                gpArgs->fLocalCoordVar = gp.fInLocalCoords.asShaderVar();
162            } else if (gp.fLocalCoordsWillBeRead) {
163                WriteLocalCoord(vertBuilder,
164                                uniformHandler,
165                                *args.fShaderCaps,
166                                gpArgs,
167                                gp.fInPosition.asShaderVar(),
168                                gp.fLocalMatrix,
169                                &fLocalMatrixUniform);
170            }
171
172            // Setup coverage as pass through
173            if (gp.hasVertexCoverage() && !tweakAlpha) {
174                fragBuilder->codeAppendf("half alpha = 1.0;");
175                varyingHandler->addPassThroughAttribute(gp.fInCoverage.asShaderVar(), "alpha");
176                if (coverageNeedsSaturate) {
177                    fragBuilder->codeAppendf("half4 %s = half4(saturate(alpha));",
178                                             args.fOutputCoverage);
179                } else {
180                    fragBuilder->codeAppendf("half4 %s = half4(alpha);", args.fOutputCoverage);
181                }
182            } else if (gp.fCoverage == 0xff) {
183                fragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
184            } else {
185                const char* fragCoverage;
186                fCoverageUniform = uniformHandler->addUniform(nullptr,
187                                                              kFragment_GrShaderFlag,
188                                                              kHalf_GrSLType,
189                                                              "Coverage",
190                                                              &fragCoverage);
191                fragBuilder->codeAppendf("half4 %s = half4(%s);",
192                                         args.fOutputCoverage, fragCoverage);
193            }
194        }
195
196        SkMatrix    fViewMatrixPrev  = SkMatrix::InvalidMatrix();
197        SkMatrix    fLocalMatrixPrev = SkMatrix::InvalidMatrix();
198        SkPMColor4f fColor           = SK_PMColor4fILLEGAL;
199        uint8_t     fCoverage        = 0xFF;
200
201        UniformHandle fViewMatrixUniform;
202        UniformHandle fLocalMatrixUniform;
203        UniformHandle fColorUniform;
204        UniformHandle fCoverageUniform;
205    };
206
207    bool hasVertexColor() const { return fInColor.isInitialized(); }
208    bool hasVertexCoverage() const { return fInCoverage.isInitialized(); }
209
210    DefaultGeoProc(uint32_t gpTypeFlags,
211                   const SkPMColor4f& color,
212                   const SkMatrix& viewMatrix,
213                   const SkMatrix& localMatrix,
214                   uint8_t coverage,
215                   bool localCoordsWillBeRead)
216            : INHERITED(kDefaultGeoProc_ClassID)
217            , fColor(color)
218            , fViewMatrix(viewMatrix)
219            , fLocalMatrix(localMatrix)
220            , fCoverage(coverage)
221            , fFlags(gpTypeFlags)
222            , fLocalCoordsWillBeRead(localCoordsWillBeRead) {
223        fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
224        if (fFlags & kColorAttribute_GPFlag) {
225            fInColor = MakeColorAttribute("inColor",
226                                          SkToBool(fFlags & kColorAttributeIsWide_GPFlag));
227        }
228        if (fFlags & kLocalCoordAttribute_GPFlag) {
229            fInLocalCoords = {"inLocalCoord", kFloat2_GrVertexAttribType,
230                                              kFloat2_GrSLType};
231        }
232        if (fFlags & kCoverageAttribute_GPFlag) {
233            fInCoverage = {"inCoverage", kFloat_GrVertexAttribType, kHalf_GrSLType};
234        }
235        this->setVertexAttributes(&fInPosition, 4);
236    }
237
238    Attribute fInPosition;
239    Attribute fInColor;
240    Attribute fInLocalCoords;
241    Attribute fInCoverage;
242    SkPMColor4f fColor;
243    SkMatrix fViewMatrix;
244    SkMatrix fLocalMatrix;
245    uint8_t fCoverage;
246    uint32_t fFlags;
247    bool fLocalCoordsWillBeRead;
248
249    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
250
251    using INHERITED = GrGeometryProcessor;
252};
253
254GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DefaultGeoProc);
255
256#if GR_TEST_UTILS
257GrGeometryProcessor* DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
258    uint32_t flags = 0;
259    if (d->fRandom->nextBool()) {
260        flags |= kColorAttribute_GPFlag;
261    }
262    if (d->fRandom->nextBool()) {
263        flags |= kColorAttributeIsWide_GPFlag;
264    }
265    if (d->fRandom->nextBool()) {
266        flags |= kCoverageAttribute_GPFlag;
267        if (d->fRandom->nextBool()) {
268            flags |= (d->fRandom->nextBool()) ? kCoverageAttributeTweak_GPFlag
269                                              : kCoverageAttributeUnclamped_GPFlag;
270        }
271    }
272    if (d->fRandom->nextBool()) {
273        flags |= kLocalCoordAttribute_GPFlag;
274    }
275
276    GrColor color = GrTest::RandomColor(d->fRandom);
277    SkMatrix viewMtx = GrTest::TestMatrix(d->fRandom);
278    SkMatrix localMtx = GrTest::TestMatrix(d->fRandom);
279    bool readsLocalCoords = d->fRandom->nextBool();
280    uint8_t coverage = GrTest::RandomCoverage(d->fRandom);
281    return DefaultGeoProc::Make(d->allocator(),
282                                flags,
283                                SkPMColor4f::FromBytes_RGBA(color),
284                                viewMtx,
285                                localMtx,
286                                readsLocalCoords,
287                                coverage);
288}
289#endif
290
291GrGeometryProcessor* GrDefaultGeoProcFactory::Make(SkArenaAlloc* arena,
292                                                   const Color& color,
293                                                   const Coverage& coverage,
294                                                   const LocalCoords& localCoords,
295                                                   const SkMatrix& viewMatrix) {
296    uint32_t flags = 0;
297    if (Color::kPremulGrColorAttribute_Type == color.fType) {
298        flags |= kColorAttribute_GPFlag;
299    } else if (Color::kPremulWideColorAttribute_Type == color.fType) {
300        flags |= kColorAttribute_GPFlag | kColorAttributeIsWide_GPFlag;
301    }
302    if (Coverage::kAttribute_Type == coverage.fType) {
303        flags |= kCoverageAttribute_GPFlag;
304    } else if (Coverage::kAttributeTweakAlpha_Type == coverage.fType) {
305        flags |= kCoverageAttribute_GPFlag | kCoverageAttributeTweak_GPFlag;
306    } else if (Coverage::kAttributeUnclamped_Type == coverage.fType) {
307        flags |= kCoverageAttribute_GPFlag | kCoverageAttributeUnclamped_GPFlag;
308    }
309    flags |= localCoords.fType == LocalCoords::kHasExplicit_Type ? kLocalCoordAttribute_GPFlag : 0;
310
311    uint8_t inCoverage = coverage.fCoverage;
312    bool localCoordsWillBeRead = localCoords.fType != LocalCoords::kUnused_Type;
313
314    return DefaultGeoProc::Make(arena,
315                                flags,
316                                color.fColor,
317                                viewMatrix,
318                                localCoords.fMatrix ? *localCoords.fMatrix : SkMatrix::I(),
319                                localCoordsWillBeRead,
320                                inCoverage);
321}
322
323GrGeometryProcessor* GrDefaultGeoProcFactory::MakeForDeviceSpace(SkArenaAlloc* arena,
324                                                                 const Color& color,
325                                                                 const Coverage& coverage,
326                                                                 const LocalCoords& localCoords,
327                                                                 const SkMatrix& viewMatrix) {
328    SkMatrix invert = SkMatrix::I();
329    if (LocalCoords::kUnused_Type != localCoords.fType) {
330        SkASSERT(LocalCoords::kUsePosition_Type == localCoords.fType);
331        if (!viewMatrix.isIdentity() && !viewMatrix.invert(&invert)) {
332            return nullptr;
333        }
334
335        if (localCoords.hasLocalMatrix()) {
336            invert.postConcat(*localCoords.fMatrix);
337        }
338    }
339
340    LocalCoords inverted(LocalCoords::kUsePosition_Type, &invert);
341    return Make(arena, color, coverage, inverted, SkMatrix::I());
342}
343