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