1/*
2 * Copyright 2015 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#ifndef GrGLSLVarying_DEFINED
9#define GrGLSLVarying_DEFINED
10
11#include "GrAllocator.h"
12#include "GrGeometryProcessor.h"
13#include "GrShaderVar.h"
14#include "GrTypesPriv.h"
15#include "glsl/GrGLSLProgramDataManager.h"
16
17class GrGLSLProgramBuilder;
18
19class GrGLSLVarying {
20public:
21    enum class Scope {
22        kVertToFrag,
23        kVertToGeo,
24        kGeoToFrag
25    };
26
27    GrGLSLVarying() = default;
28    GrGLSLVarying(GrSLType type, Scope scope = Scope::kVertToFrag) : fType(type), fScope(scope) {}
29
30    void reset(GrSLType type, Scope scope = Scope::kVertToFrag) {
31        *this = GrGLSLVarying();
32        fType = type;
33        fScope = scope;
34    }
35
36    GrSLType type() const { return fType; }
37    Scope scope() const { return fScope; }
38    bool isInVertexShader() const { return Scope::kGeoToFrag != fScope; }
39    bool isInFragmentShader() const { return Scope::kVertToGeo != fScope; }
40
41    const char* vsOut() const { SkASSERT(this->isInVertexShader()); return fVsOut; }
42    const char* gsIn() const { return fGsIn; }
43    const char* gsOut() const { return fGsOut; }
44    const char* fsIn() const { SkASSERT(this->isInFragmentShader()); return fFsIn; }
45
46private:
47    GrSLType fType = kVoid_GrSLType;
48    Scope fScope = Scope::kVertToFrag;
49    const char* fVsOut = nullptr;
50    const char* fGsIn = nullptr;
51    const char* fGsOut = nullptr;
52    const char* fFsIn = nullptr;
53
54    friend class GrGLSLVaryingHandler;
55};
56
57static const int kVaryingsPerBlock = 8;
58
59class GrGLSLVaryingHandler {
60public:
61    explicit GrGLSLVaryingHandler(GrGLSLProgramBuilder* program)
62        : fVaryings(kVaryingsPerBlock)
63        , fVertexInputs(kVaryingsPerBlock)
64        , fVertexOutputs(kVaryingsPerBlock)
65        , fGeomInputs(kVaryingsPerBlock)
66        , fGeomOutputs(kVaryingsPerBlock)
67        , fFragInputs(kVaryingsPerBlock)
68        , fFragOutputs(kVaryingsPerBlock)
69        , fProgramBuilder(program)
70        , fDefaultInterpolationModifier(nullptr) {}
71
72    virtual ~GrGLSLVaryingHandler() {}
73
74    /*
75     * Notifies the varying handler that this shader will never emit geometry in perspective and
76     * therefore does not require perspective-correct interpolation. When supported, this allows
77     * varyings to use the "noperspective" keyword, which means the GPU can use cheaper math for
78     * interpolation.
79     */
80    void setNoPerspective();
81
82    /*
83     * addVarying allows fine grained control for setting up varyings between stages. Calling this
84     * function will make sure all necessary decls are setup for the client. The client however is
85     * responsible for setting up all shader code (e.g "vOut = vIn;") If you just need to take an
86     * attribute and pass it through to an output value in a fragment shader, use
87     * addPassThroughAttribute.
88     * TODO convert most uses of addVarying to addPassThroughAttribute
89     */
90    void addVarying(const char* name, GrGLSLVarying* varying) {
91        SkASSERT(GrSLTypeIsFloatType(varying->type())); // Integers must use addFlatVarying.
92        this->internalAddVarying(name, varying, false /*flat*/);
93    }
94
95    /*
96     * addFlatVarying sets up a varying whose value is constant across every fragment. The graphics
97     * pipeline will pull its value from the final vertex of the draw primitive (provoking vertex).
98     * Flat interpolation is not always supported and the user must check the caps before using.
99     * TODO: Some platforms can change the provoking vertex. Should we be resetting this knob?
100     */
101    void addFlatVarying(const char* name, GrGLSLVarying* varying) {
102        this->internalAddVarying(name, varying, true /*flat*/);
103    }
104
105    /*
106     * The GP can use these calls to pass an attribute through all shaders directly to 'output' in
107     * the fragment shader.  Though these calls affect both the vertex shader and fragment shader,
108     * they expect 'output' to be defined in the fragment shader before the call is made. If there
109     * is a geometry shader, we will simply take the value of the varying from the first vertex and
110     * that will be set as the output varying for all emitted vertices.
111     * TODO it might be nicer behavior to have a flag to declare output inside these calls
112     */
113    void addPassThroughAttribute(const GrGeometryProcessor::Attribute*, const char* output);
114    void addFlatPassThroughAttribute(const GrGeometryProcessor::Attribute*, const char* output);
115
116    void emitAttributes(const GrGeometryProcessor& gp);
117
118    // This should be called once all attributes and varyings have been added to the
119    // GrGLSLVaryingHanlder and before getting/adding any of the declarations to the shaders.
120    void finalize();
121
122    void getVertexDecls(SkString* inputDecls, SkString* outputDecls) const;
123    void getGeomDecls(SkString* inputDecls, SkString* outputDecls) const;
124    void getFragDecls(SkString* inputDecls, SkString* outputDecls) const;
125
126protected:
127    struct VaryingInfo {
128        GrSLType         fType;
129        bool             fIsFlat;
130        SkString         fVsOut;
131        SkString         fGsOut;
132        GrShaderFlags    fVisibility;
133    };
134
135    typedef GrTAllocator<VaryingInfo> VaryingList;
136    typedef GrTAllocator<GrShaderVar> VarArray;
137    typedef GrGLSLProgramDataManager::VaryingHandle VaryingHandle;
138
139    VaryingList    fVaryings;
140    VarArray       fVertexInputs;
141    VarArray       fVertexOutputs;
142    VarArray       fGeomInputs;
143    VarArray       fGeomOutputs;
144    VarArray       fFragInputs;
145    VarArray       fFragOutputs;
146
147    // This is not owned by the class
148    GrGLSLProgramBuilder* fProgramBuilder;
149
150private:
151    void internalAddVarying(const char* name, GrGLSLVarying*, bool flat);
152    void writePassThroughAttribute(const GrGeometryProcessor::Attribute*, const char* output,
153                                   const GrGLSLVarying&);
154
155    void addAttribute(const GrShaderVar& var);
156
157    virtual void onFinalize() = 0;
158
159    // helper function for get*Decls
160    void appendDecls(const VarArray& vars, SkString* out) const;
161
162    const char* fDefaultInterpolationModifier;
163
164    friend class GrGLSLProgramBuilder;
165};
166
167#endif
168