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