1/*
2 * Copyright 2014 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 GrGLSLShaderBuilder_DEFINED
9#define GrGLSLShaderBuilder_DEFINED
10
11#include "GrAllocator.h"
12#include "GrShaderVar.h"
13#include "glsl/GrGLSLUniformHandler.h"
14#include "SkTDArray.h"
15
16#include <stdarg.h>
17
18class GrGLSLColorSpaceXformHelper;
19
20/**
21  base class for all shaders builders
22*/
23class GrGLSLShaderBuilder {
24public:
25    GrGLSLShaderBuilder(GrGLSLProgramBuilder* program);
26    virtual ~GrGLSLShaderBuilder() {}
27
28    using SamplerHandle      = GrGLSLUniformHandler::SamplerHandle;
29    using TexelBufferHandle  = GrGLSLUniformHandler::TexelBufferHandle;
30
31    /** Appends a 2D texture sample with projection if necessary. coordType must either be Vec2f or
32        Vec3f. The latter is interpreted as projective texture coords. The vec length and swizzle
33        order of the result depends on the GrProcessor::TextureSampler associated with the
34        SamplerHandle.
35        */
36    void appendTextureLookup(SkString* out,
37                             SamplerHandle,
38                             const char* coordName,
39                             GrSLType coordType = kHalf2_GrSLType) const;
40
41    /** Version of above that appends the result to the shader code instead.*/
42    void appendTextureLookup(SamplerHandle,
43                             const char* coordName,
44                             GrSLType coordType = kHalf2_GrSLType,
45                             GrGLSLColorSpaceXformHelper* colorXformHelper = nullptr);
46
47
48    /** Does the work of appendTextureLookup and modulates the result by modulation. The result is
49        always a half4. modulation and the swizzle specified by SamplerHandle must both be
50        half4 or half. If modulation is "" or nullptr it this function acts as though
51        appendTextureLookup were called. */
52    void appendTextureLookupAndModulate(const char* modulation,
53                                        SamplerHandle,
54                                        const char* coordName,
55                                        GrSLType coordType = kHalf2_GrSLType,
56                                        GrGLSLColorSpaceXformHelper* colorXformHelper = nullptr);
57
58    /** Adds a helper function to facilitate color gamut transformation, and produces code that
59        returns the srcColor transformed into a new gamut (via multiplication by the xform from
60        colorXformHelper). Premultiplied sources are also handled correctly (colorXformHelper
61        determines if the source is premultipled or not). */
62    void appendColorGamutXform(SkString* out, const char* srcColor,
63                               GrGLSLColorSpaceXformHelper* colorXformHelper);
64
65    /** Version of above that appends the result to the shader code instead. */
66    void appendColorGamutXform(const char* srcColor, GrGLSLColorSpaceXformHelper* colorXformHelper);
67
68    /** Fetches an unfiltered texel from a sampler at integer coordinates. coordExpr must match the
69        dimensionality of the sampler and must be within the sampler's range. coordExpr is emitted
70        exactly once, so expressions like "idx++" are acceptable. */
71    void appendTexelFetch(SkString* out, TexelBufferHandle, const char* coordExpr) const;
72
73    /** Version of above that appends the result to the shader code instead.*/
74    void appendTexelFetch(TexelBufferHandle, const char* coordExpr);
75
76    /**
77    * Adds a constant declaration to the top of the shader.
78    */
79    void defineConstant(const char* type, const char* name, const char* value) {
80        this->definitions().appendf("const %s %s = %s;\n", type, name, value);
81    }
82
83    void defineConstant(const char* name, int value) {
84        this->definitions().appendf("const int %s = %i;\n", name, value);
85    }
86
87    void defineConstant(const char* name, float value) {
88        this->definitions().appendf("const float %s = %f;\n", name, value);
89    }
90
91    void defineConstantf(const char* type, const char* name, const char* fmt, ...) {
92       this->definitions().appendf("const %s %s = ", type, name);
93       va_list args;
94       va_start(args, fmt);
95       this->definitions().appendVAList(fmt, args);
96       va_end(args);
97       this->definitions().append(";\n");
98    }
99
100    void declareGlobal(const GrShaderVar&);
101
102    /**
103    * Called by GrGLSLProcessors to add code to one of the shaders.
104    */
105    void codeAppendf(const char format[], ...) SK_PRINTF_LIKE(2, 3) {
106       va_list args;
107       va_start(args, format);
108       this->code().appendVAList(format, args);
109       va_end(args);
110    }
111
112    void codeAppend(const char* str) { this->code().append(str); }
113
114    void codePrependf(const char format[], ...) SK_PRINTF_LIKE(2, 3) {
115       va_list args;
116       va_start(args, format);
117       this->code().prependVAList(format, args);
118       va_end(args);
119    }
120
121    /**
122     * Appends a variable declaration to one of the shaders
123     */
124    void declAppend(const GrShaderVar& var);
125
126    /** Emits a helper function outside of main() in the fragment shader. */
127    void emitFunction(GrSLType returnType,
128                      const char* name,
129                      int argCnt,
130                      const GrShaderVar* args,
131                      const char* body,
132                      SkString* outName);
133
134    /*
135     * Combines the various parts of the shader to create a single finalized shader string.
136     */
137    void finalize(uint32_t visibility);
138
139    /*
140     * Get parent builder for adding uniforms
141     */
142    GrGLSLProgramBuilder* getProgramBuilder() { return fProgramBuilder; }
143
144    /**
145     * Helper for begining and ending a block in the shader code.
146     */
147    class ShaderBlock {
148    public:
149        ShaderBlock(GrGLSLShaderBuilder* builder) : fBuilder(builder) {
150            SkASSERT(builder);
151            fBuilder->codeAppend("{");
152        }
153
154        ~ShaderBlock() {
155            fBuilder->codeAppend("}");
156        }
157    private:
158        GrGLSLShaderBuilder* fBuilder;
159    };
160
161protected:
162    typedef GrTAllocator<GrShaderVar> VarArray;
163    void appendDecls(const VarArray& vars, SkString* out) const;
164
165    /**
166     * Features that should only be enabled internally by the builders.
167     */
168    enum GLSLPrivateFeature {
169        kFragCoordConventions_GLSLPrivateFeature,
170        kBlendEquationAdvanced_GLSLPrivateFeature,
171        kBlendFuncExtended_GLSLPrivateFeature,
172        kExternalTexture_GLSLPrivateFeature,
173        kTexelBuffer_GLSLPrivateFeature,
174        kFramebufferFetch_GLSLPrivateFeature,
175        kNoPerspectiveInterpolation_GLSLPrivateFeature,
176        kLastGLSLPrivateFeature = kNoPerspectiveInterpolation_GLSLPrivateFeature
177    };
178
179    /*
180     * A general function which enables an extension in a shader if the feature bit is not present
181     *
182     * @return true if the feature bit was not yet present, false otherwise.
183     */
184    bool addFeature(uint32_t featureBit, const char* extensionName);
185
186    enum InterfaceQualifier {
187        kIn_InterfaceQualifier,
188        kOut_InterfaceQualifier,
189        kLastInterfaceQualifier = kOut_InterfaceQualifier
190    };
191
192    /*
193     * A low level function to build default layout qualifiers.
194     *
195     *   e.g. layout(param1, param2, ...) out;
196     *
197     * GLSL allows default layout qualifiers for in, out, and uniform.
198     */
199    void addLayoutQualifier(const char* param, InterfaceQualifier);
200
201    void compileAndAppendLayoutQualifiers();
202
203    void nextStage() {
204        fShaderStrings.push_back();
205        fCompilerStrings.push_back(this->code().c_str());
206        fCompilerStringLengths.push_back((int)this->code().size());
207        fCodeIndex++;
208    }
209
210    SkString& versionDecl() { return fShaderStrings[kVersionDecl]; }
211    SkString& extensions() { return fShaderStrings[kExtensions]; }
212    SkString& definitions() { return fShaderStrings[kDefinitions]; }
213    SkString& precisionQualifier() { return fShaderStrings[kPrecisionQualifier]; }
214    SkString& layoutQualifiers() { return fShaderStrings[kLayoutQualifiers]; }
215    SkString& uniforms() { return fShaderStrings[kUniforms]; }
216    SkString& inputs() { return fShaderStrings[kInputs]; }
217    SkString& outputs() { return fShaderStrings[kOutputs]; }
218    SkString& functions() { return fShaderStrings[kFunctions]; }
219    SkString& main() { return fShaderStrings[kMain]; }
220    SkString& code() { return fShaderStrings[fCodeIndex]; }
221
222    virtual void onFinalize() = 0;
223
224    enum {
225        kVersionDecl,
226        kExtensions,
227        kDefinitions,
228        kPrecisionQualifier,
229        kLayoutQualifiers,
230        kUniforms,
231        kInputs,
232        kOutputs,
233        kFunctions,
234        kMain,
235        kCode,
236    };
237
238    GrGLSLProgramBuilder* fProgramBuilder;
239    SkSTArray<kCode, const char*, true> fCompilerStrings;
240    SkSTArray<kCode, int, true> fCompilerStringLengths;
241    SkSTArray<kCode, SkString> fShaderStrings;
242    SkString fCode;
243    SkString fFunctions;
244    SkString fExtensions;
245
246    VarArray fInputs;
247    VarArray fOutputs;
248    uint32_t fFeaturesAddedMask;
249    SkSTArray<1, SkString> fLayoutParams[kLastInterfaceQualifier + 1];
250    int fCodeIndex;
251    bool fFinalized;
252
253    friend class GrGLSLProgramBuilder;
254    friend class GrGLProgramBuilder;
255    friend class GrGLSLVaryingHandler; // to access noperspective interpolation feature.
256    friend class GrGLPathProgramBuilder; // to access fInputs.
257    friend class GrVkPipelineStateBuilder;
258};
259#endif
260