| /* |
| * Copyright 2015 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef GrGLSLVarying_DEFINED |
| #define GrGLSLVarying_DEFINED |
| |
| #include "include/private/GrTypesPriv.h" |
| #include "src/gpu/GrGeometryProcessor.h" |
| #include "src/gpu/GrShaderVar.h" |
| #include "src/gpu/GrTBlockList.h" |
| #include "src/gpu/glsl/GrGLSLProgramDataManager.h" |
| |
| class GrGLSLProgramBuilder; |
| |
| #ifdef SK_DEBUG |
| static bool is_matrix(GrSLType type) { |
| switch (type) { |
| case kFloat2x2_GrSLType: |
| case kFloat3x3_GrSLType: |
| case kFloat4x4_GrSLType: |
| case kHalf2x2_GrSLType: |
| case kHalf3x3_GrSLType: |
| case kHalf4x4_GrSLType: |
| return true; |
| default: |
| return false; |
| } |
| } |
| #endif |
| |
| class GrGLSLVarying { |
| public: |
| enum class Scope { |
| kVertToFrag, |
| kVertToGeo, |
| kGeoToFrag |
| }; |
| |
| GrGLSLVarying() = default; |
| GrGLSLVarying(GrSLType type, Scope scope = Scope::kVertToFrag) |
| : fType(type) |
| , fScope(scope) { |
| // Metal doesn't support varying matrices, so we disallow them everywhere for consistency |
| SkASSERT(!is_matrix(type)); |
| } |
| |
| void reset(GrSLType type, Scope scope = Scope::kVertToFrag) { |
| // Metal doesn't support varying matrices, so we disallow them everywhere for consistency |
| SkASSERT(!is_matrix(type)); |
| *this = GrGLSLVarying(); |
| fType = type; |
| fScope = scope; |
| } |
| |
| GrSLType type() const { return fType; } |
| Scope scope() const { return fScope; } |
| bool isInVertexShader() const { return Scope::kGeoToFrag != fScope; } |
| bool isInFragmentShader() const { return Scope::kVertToGeo != fScope; } |
| |
| const char* vsOut() const { SkASSERT(this->isInVertexShader()); return fVsOut; } |
| const char* gsIn() const { return fGsIn; } |
| const char* gsOut() const { return fGsOut; } |
| const char* fsIn() const { SkASSERT(this->isInFragmentShader()); return fFsIn; } |
| |
| private: |
| GrSLType fType = kVoid_GrSLType; |
| Scope fScope = Scope::kVertToFrag; |
| const char* fVsOut = nullptr; |
| const char* fGsIn = nullptr; |
| const char* fGsOut = nullptr; |
| const char* fFsIn = nullptr; |
| |
| friend class GrGLSLVaryingHandler; |
| }; |
| |
| static const int kVaryingsPerBlock = 8; |
| |
| class GrGLSLVaryingHandler { |
| public: |
| explicit GrGLSLVaryingHandler(GrGLSLProgramBuilder* program) |
| : fVaryings(kVaryingsPerBlock) |
| , fVertexInputs(kVaryingsPerBlock) |
| , fVertexOutputs(kVaryingsPerBlock) |
| , fGeomInputs(kVaryingsPerBlock) |
| , fGeomOutputs(kVaryingsPerBlock) |
| , fFragInputs(kVaryingsPerBlock) |
| , fFragOutputs(kVaryingsPerBlock) |
| , fProgramBuilder(program) |
| , fDefaultInterpolationModifier(nullptr) {} |
| |
| virtual ~GrGLSLVaryingHandler() {} |
| |
| /* |
| * Notifies the varying handler that this shader will never emit geometry in perspective and |
| * therefore does not require perspective-correct interpolation. When supported, this allows |
| * varyings to use the "noperspective" keyword, which means the GPU can use cheaper math for |
| * interpolation. |
| */ |
| void setNoPerspective(); |
| |
| enum class Interpolation { |
| kInterpolated, |
| kCanBeFlat, // Use "flat" if it will be faster. |
| kMustBeFlat // Use "flat" even if it is known to be slow. |
| }; |
| |
| /* |
| * addVarying allows fine grained control for setting up varyings between stages. Calling this |
| * function will make sure all necessary decls are setup for the client. The client however is |
| * responsible for setting up all shader code (e.g "vOut = vIn;") If you just need to take an |
| * attribute and pass it through to an output value in a fragment shader, use |
| * addPassThroughAttribute. |
| * TODO convert most uses of addVarying to addPassThroughAttribute |
| */ |
| void addVarying(const char* name, GrGLSLVarying* varying, |
| Interpolation = Interpolation::kInterpolated); |
| |
| /* |
| * The GP can use these calls to pass an attribute through all shaders directly to 'output' in |
| * the fragment shader. Though these calls affect both the vertex shader and fragment shader, |
| * they expect 'output' to be defined in the fragment shader before the call is made. If there |
| * is a geometry shader, we will simply take the value of the varying from the first vertex and |
| * that will be set as the output varying for all emitted vertices. |
| * TODO it might be nicer behavior to have a flag to declare output inside these calls |
| */ |
| void addPassThroughAttribute(const GrGeometryProcessor::Attribute&, const char* output, |
| Interpolation = Interpolation::kInterpolated); |
| |
| void emitAttributes(const GrGeometryProcessor&); |
| |
| // This should be called once all attributes and varyings have been added to the |
| // GrGLSLVaryingHanlder and before getting/adding any of the declarations to the shaders. |
| void finalize(); |
| |
| void getVertexDecls(SkString* inputDecls, SkString* outputDecls) const; |
| void getGeomDecls(SkString* inputDecls, SkString* outputDecls) const; |
| void getFragDecls(SkString* inputDecls, SkString* outputDecls) const; |
| |
| protected: |
| struct VaryingInfo { |
| GrSLType fType; |
| bool fIsFlat; |
| SkString fVsOut; |
| SkString fGsOut; |
| GrShaderFlags fVisibility; |
| }; |
| |
| typedef GrTBlockList<VaryingInfo> VaryingList; |
| typedef GrTBlockList<GrShaderVar> VarArray; |
| |
| VaryingList fVaryings; |
| VarArray fVertexInputs; |
| VarArray fVertexOutputs; |
| VarArray fGeomInputs; |
| VarArray fGeomOutputs; |
| VarArray fFragInputs; |
| VarArray fFragOutputs; |
| |
| // This is not owned by the class |
| GrGLSLProgramBuilder* fProgramBuilder; |
| |
| private: |
| void addAttribute(const GrShaderVar& var); |
| |
| virtual void onFinalize() = 0; |
| |
| // helper function for get*Decls |
| void appendDecls(const VarArray& vars, SkString* out) const; |
| |
| const char* fDefaultInterpolationModifier; |
| |
| friend class GrGLSLProgramBuilder; |
| }; |
| |
| #endif |