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 "GrDefaultGeoProcFactory.h"
9
10#include "SkRefCnt.h"
11#include "glsl/GrGLSLColorSpaceXformHelper.h"
12#include "glsl/GrGLSLFragmentShaderBuilder.h"
13#include "glsl/GrGLSLGeometryProcessor.h"
14#include "glsl/GrGLSLVertexGeoBuilder.h"
15#include "glsl/GrGLSLVarying.h"
16#include "glsl/GrGLSLUniformHandler.h"
17#include "glsl/GrGLSLUtil.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    kColorAttributeIsSkColor_GPFlag = 0x2,
28    kLocalCoordAttribute_GPFlag     = 0x4,
29    kCoverageAttribute_GPFlag       = 0x8,
30
31    kLinearizeColorAttribute_GPFlag = 0x10,
32};
33
34class DefaultGeoProc : public GrGeometryProcessor {
35public:
36    static sk_sp<GrGeometryProcessor> Make(uint32_t gpTypeFlags,
37                                           GrColor color,
38                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
39                                           const SkMatrix& viewMatrix,
40                                           const SkMatrix& localMatrix,
41                                           bool localCoordsWillBeRead,
42                                           uint8_t coverage) {
43        return sk_sp<GrGeometryProcessor>(new DefaultGeoProc(
44                gpTypeFlags, color, std::move(colorSpaceXform), viewMatrix, localMatrix, coverage,
45                localCoordsWillBeRead));
46    }
47
48    const char* name() const override { return "DefaultGeometryProcessor"; }
49
50    const Attribute* inPosition() const { return fInPosition; }
51    const Attribute* inColor() const { return fInColor; }
52    const Attribute* inLocalCoords() const { return fInLocalCoords; }
53    const Attribute* inCoverage() const { return fInCoverage; }
54    GrColor color() const { return fColor; }
55    bool hasVertexColor() const { return SkToBool(fInColor); }
56    const SkMatrix& viewMatrix() const { return fViewMatrix; }
57    const SkMatrix& localMatrix() const { return fLocalMatrix; }
58    bool localCoordsWillBeRead() const { return fLocalCoordsWillBeRead; }
59    uint8_t coverage() const { return fCoverage; }
60    bool hasVertexCoverage() const { return SkToBool(fInCoverage); }
61    bool linearizeColor() const {
62        // Linearization should only happen with SkColor
63        bool linearize = SkToBool(fFlags & kLinearizeColorAttribute_GPFlag);
64        SkASSERT(!linearize || (fFlags & kColorAttributeIsSkColor_GPFlag));
65        return linearize;
66    }
67
68    class GLSLProcessor : public GrGLSLGeometryProcessor {
69    public:
70        GLSLProcessor()
71            : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL), fCoverage(0xff) {}
72
73        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
74            const DefaultGeoProc& gp = args.fGP.cast<DefaultGeoProc>();
75            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
76            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
77            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
78            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
79
80            // emit attributes
81            varyingHandler->emitAttributes(gp);
82
83            // Setup pass through color
84            if (gp.hasVertexColor()) {
85                GrGLSLVarying varying(kHalf4_GrSLType);
86                varyingHandler->addVarying("color", &varying);
87
88                // There are several optional steps to process the color. Start with the attribute:
89                vertBuilder->codeAppendf("half4 color = %s;", gp.inColor()->fName);
90
91                // Linearize
92                if (gp.linearizeColor()) {
93                    SkString srgbFuncName;
94                    static const GrShaderVar gSrgbArgs[] = {
95                        GrShaderVar("x", kHalf_GrSLType),
96                    };
97                    vertBuilder->emitFunction(kHalf_GrSLType,
98                                              "srgb_to_linear",
99                                              SK_ARRAY_COUNT(gSrgbArgs),
100                                              gSrgbArgs,
101                                              "return (x <= 0.04045) ? (x / 12.92) "
102                                              ": pow((x + 0.055) / 1.055, 2.4);",
103                                              &srgbFuncName);
104                    vertBuilder->codeAppendf("color = half4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);",
105                                             srgbFuncName.c_str(), gp.inColor()->fName,
106                                             srgbFuncName.c_str(), gp.inColor()->fName,
107                                             srgbFuncName.c_str(), gp.inColor()->fName,
108                                             gp.inColor()->fName);
109                }
110
111                // For SkColor, do a red/blue swap and premul
112                if (gp.fFlags & kColorAttributeIsSkColor_GPFlag) {
113                    vertBuilder->codeAppend("color = half4(color.a * color.bgr, color.a);");
114                }
115
116                // Do color-correction to destination gamut
117                if (gp.linearizeColor()) {
118                    fColorSpaceHelper.emitCode(uniformHandler, gp.fColorSpaceXform.get(),
119                                               kVertex_GrShaderFlag);
120                    if (fColorSpaceHelper.isValid()) {
121                        SkString xformedColor;
122                        vertBuilder->appendColorGamutXform(&xformedColor, "color",
123                                                           &fColorSpaceHelper);
124                        vertBuilder->codeAppendf("color = %s;", xformedColor.c_str());
125                    }
126                }
127                vertBuilder->codeAppendf("%s = color;\n", varying.vsOut());
128                fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
129            } else {
130                this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
131                                        &fColorUniform);
132            }
133
134            // Setup position
135            this->writeOutputPosition(vertBuilder,
136                                      uniformHandler,
137                                      gpArgs,
138                                      gp.inPosition()->fName,
139                                      gp.viewMatrix(),
140                                      &fViewMatrixUniform);
141
142            if (gp.hasExplicitLocalCoords()) {
143                // emit transforms with explicit local coords
144                this->emitTransforms(vertBuilder,
145                                     varyingHandler,
146                                     uniformHandler,
147                                     gp.inLocalCoords()->asShaderVar(),
148                                     gp.localMatrix(),
149                                     args.fFPCoordTransformHandler);
150            } else {
151                // emit transforms with position
152                this->emitTransforms(vertBuilder,
153                                     varyingHandler,
154                                     uniformHandler,
155                                     gp.inPosition()->asShaderVar(),
156                                     gp.localMatrix(),
157                                     args.fFPCoordTransformHandler);
158            }
159
160            // Setup coverage as pass through
161            if (gp.hasVertexCoverage()) {
162                fragBuilder->codeAppendf("half alpha = 1.0;");
163                varyingHandler->addPassThroughAttribute(gp.inCoverage(), "alpha");
164                fragBuilder->codeAppendf("%s = half4(alpha);", args.fOutputCoverage);
165            } else if (gp.coverage() == 0xff) {
166                fragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
167            } else {
168                const char* fragCoverage;
169                fCoverageUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
170                                                              kHalf_GrSLType,
171                                                              "Coverage",
172                                                              &fragCoverage);
173                fragBuilder->codeAppendf("%s = half4(%s);", args.fOutputCoverage, fragCoverage);
174            }
175        }
176
177        static inline void GenKey(const GrGeometryProcessor& gp,
178                                  const GrShaderCaps&,
179                                  GrProcessorKeyBuilder* b) {
180            const DefaultGeoProc& def = gp.cast<DefaultGeoProc>();
181            uint32_t key = def.fFlags;
182            key |= (def.coverage() == 0xff) ? 0x10 : 0;
183            key |= (def.localCoordsWillBeRead() && def.localMatrix().hasPerspective()) ? 0x20 : 0x0;
184            key |= ComputePosKey(def.viewMatrix()) << 20;
185            b->add32(key);
186            if (def.linearizeColor()) {
187                b->add32(GrColorSpaceXform::XformKey(def.fColorSpaceXform.get()));
188            }
189        }
190
191        void setData(const GrGLSLProgramDataManager& pdman,
192                     const GrPrimitiveProcessor& gp,
193                     FPCoordTransformIter&& transformIter) override {
194            const DefaultGeoProc& dgp = gp.cast<DefaultGeoProc>();
195
196            if (!dgp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dgp.viewMatrix())) {
197                fViewMatrix = dgp.viewMatrix();
198                float viewMatrix[3 * 3];
199                GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
200                pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
201            }
202
203            if (dgp.color() != fColor && !dgp.hasVertexColor()) {
204                float c[4];
205                GrColorToRGBAFloat(dgp.color(), c);
206                pdman.set4fv(fColorUniform, 1, c);
207                fColor = dgp.color();
208            }
209
210            if (dgp.coverage() != fCoverage && !dgp.hasVertexCoverage()) {
211                pdman.set1f(fCoverageUniform, GrNormalizeByteToFloat(dgp.coverage()));
212                fCoverage = dgp.coverage();
213            }
214            this->setTransformDataHelper(dgp.fLocalMatrix, pdman, &transformIter);
215
216            if (dgp.linearizeColor() && dgp.fColorSpaceXform) {
217                fColorSpaceHelper.setData(pdman, dgp.fColorSpaceXform.get());
218            }
219        }
220
221    private:
222        SkMatrix fViewMatrix;
223        GrColor fColor;
224        uint8_t fCoverage;
225        UniformHandle fViewMatrixUniform;
226        UniformHandle fColorUniform;
227        UniformHandle fCoverageUniform;
228        GrGLSLColorSpaceXformHelper fColorSpaceHelper;
229
230        typedef GrGLSLGeometryProcessor INHERITED;
231    };
232
233    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
234        GLSLProcessor::GenKey(*this, caps, b);
235    }
236
237    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
238        return new GLSLProcessor();
239    }
240
241private:
242    DefaultGeoProc(uint32_t gpTypeFlags,
243                   GrColor color,
244                   sk_sp<GrColorSpaceXform> colorSpaceXform,
245                   const SkMatrix& viewMatrix,
246                   const SkMatrix& localMatrix,
247                   uint8_t coverage,
248                   bool localCoordsWillBeRead)
249            : INHERITED(kDefaultGeoProc_ClassID)
250            , fColor(color)
251            , fViewMatrix(viewMatrix)
252            , fLocalMatrix(localMatrix)
253            , fCoverage(coverage)
254            , fFlags(gpTypeFlags)
255            , fLocalCoordsWillBeRead(localCoordsWillBeRead)
256            , fColorSpaceXform(std::move(colorSpaceXform)) {
257        fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
258        if (fFlags & kColorAttribute_GPFlag) {
259            fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
260        }
261        if (fFlags & kLocalCoordAttribute_GPFlag) {
262            fInLocalCoords = &this->addVertexAttrib("inLocalCoord", kFloat2_GrVertexAttribType);
263            this->setHasExplicitLocalCoords();
264        }
265        if (fFlags & kCoverageAttribute_GPFlag) {
266            fInCoverage = &this->addVertexAttrib("inCoverage", kHalf_GrVertexAttribType);
267        }
268    }
269
270    const Attribute* fInPosition = nullptr;
271    const Attribute* fInColor = nullptr;
272    const Attribute* fInLocalCoords = nullptr;
273    const Attribute* fInCoverage = nullptr;
274    GrColor fColor;
275    SkMatrix fViewMatrix;
276    SkMatrix fLocalMatrix;
277    uint8_t fCoverage;
278    uint32_t fFlags;
279    bool fLocalCoordsWillBeRead;
280    sk_sp<GrColorSpaceXform> fColorSpaceXform;
281
282    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
283
284    typedef GrGeometryProcessor INHERITED;
285};
286
287GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DefaultGeoProc);
288
289#if GR_TEST_UTILS
290sk_sp<GrGeometryProcessor> DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
291    uint32_t flags = 0;
292    if (d->fRandom->nextBool()) {
293        flags |= kColorAttribute_GPFlag;
294    }
295    if (d->fRandom->nextBool()) {
296        flags |= kColorAttributeIsSkColor_GPFlag;
297    }
298    if (d->fRandom->nextBool()) {
299        flags |= kCoverageAttribute_GPFlag;
300    }
301    if (d->fRandom->nextBool()) {
302        flags |= kLocalCoordAttribute_GPFlag;
303    }
304
305    return DefaultGeoProc::Make(flags,
306                                GrRandomColor(d->fRandom),
307                                GrTest::TestColorXform(d->fRandom),
308                                GrTest::TestMatrix(d->fRandom),
309                                GrTest::TestMatrix(d->fRandom),
310                                d->fRandom->nextBool(),
311                                GrRandomCoverage(d->fRandom));
312}
313#endif
314
315sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::Make(const Color& color,
316                                                         const Coverage& coverage,
317                                                         const LocalCoords& localCoords,
318                                                         const SkMatrix& viewMatrix) {
319    uint32_t flags = 0;
320    if (Color::kPremulGrColorAttribute_Type == color.fType) {
321        flags |= kColorAttribute_GPFlag;
322    } else if (Color::kUnpremulSkColorAttribute_Type == color.fType) {
323        flags |= kColorAttribute_GPFlag | kColorAttributeIsSkColor_GPFlag;
324    }
325    if (color.fLinearize) {
326        // It only makes sense to linearize SkColors (which are always sRGB). GrColor values should
327        // have been linearized and gamut-converted during paint conversion
328        SkASSERT(Color::kUnpremulSkColorAttribute_Type == color.fType);
329        flags |= kLinearizeColorAttribute_GPFlag;
330    }
331    flags |= coverage.fType == Coverage::kAttribute_Type ? kCoverageAttribute_GPFlag : 0;
332    flags |= localCoords.fType == LocalCoords::kHasExplicit_Type ? kLocalCoordAttribute_GPFlag : 0;
333
334    uint8_t inCoverage = coverage.fCoverage;
335    bool localCoordsWillBeRead = localCoords.fType != LocalCoords::kUnused_Type;
336
337    GrColor inColor = color.fColor;
338    return DefaultGeoProc::Make(flags,
339                                inColor,
340                                color.fColorSpaceXform,
341                                viewMatrix,
342                                localCoords.fMatrix ? *localCoords.fMatrix : SkMatrix::I(),
343                                localCoordsWillBeRead,
344                                inCoverage);
345}
346
347sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeForDeviceSpace(
348                                                                     const Color& color,
349                                                                     const Coverage& coverage,
350                                                                     const LocalCoords& localCoords,
351                                                                     const SkMatrix& viewMatrix) {
352    SkMatrix invert = SkMatrix::I();
353    if (LocalCoords::kUnused_Type != localCoords.fType) {
354        SkASSERT(LocalCoords::kUsePosition_Type == localCoords.fType);
355        if (!viewMatrix.isIdentity() && !viewMatrix.invert(&invert)) {
356            return nullptr;
357        }
358
359        if (localCoords.hasLocalMatrix()) {
360            invert.preConcat(*localCoords.fMatrix);
361        }
362    }
363
364    LocalCoords inverted(LocalCoords::kUsePosition_Type, &invert);
365    return Make(color, coverage, inverted, SkMatrix::I());
366}
367