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