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/GrGeometryProcessor.h"
13#include "src/gpu/GrShaderVar.h"
14#include "src/gpu/GrTAllocator.h"
15#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
16
17class GrGLSLProgramBuilder;
18
19#ifdef SK_DEBUG
20static 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
35class GrGLSLVarying {
36public:
37 enum class Scope {
38 kVertToFrag,
39 kVertToGeo,
40 kGeoToFrag
41 };
42
43 GrGLSLVarying() = default;
44 GrGLSLVarying(GrSLType type, Scope scope = Scope::kVertToFrag)
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
59 GrSLType type() const { return fType; }
60 Scope scope() const { return fScope; }
61 bool isInVertexShader() const { return Scope::kGeoToFrag != fScope; }
62 bool isInFragmentShader() const { return Scope::kVertToGeo != fScope; }
63
64 const char* vsOut() const { SkASSERT(this->isInVertexShader()); return fVsOut; }
65 const char* gsIn() const { return fGsIn; }
66 const char* gsOut() const { return fGsOut; }
67 const char* fsIn() const { SkASSERT(this->isInFragmentShader()); return fFsIn; }
68
69private:
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
80static const int kVaryingsPerBlock = 8;
81
82class GrGLSLVaryingHandler {
83public:
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
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
143protected:
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
167private:
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