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        kSampleVariables_GLSLPrivateFeature,
177        kSampleMaskOverrideCoverage_GLSLPrivateFeature,
178        kLastGLSLPrivateFeature = kSampleMaskOverrideCoverage_GLSLPrivateFeature
179    };
180
181    /*
182     * A general function which enables an extension in a shader if the feature bit is not present
183     *
184     * @return true if the feature bit was not yet present, false otherwise.
185     */
186    bool addFeature(uint32_t featureBit, const char* extensionName);
187
188    enum InterfaceQualifier {
189        kIn_InterfaceQualifier,
190        kOut_InterfaceQualifier,
191        kLastInterfaceQualifier = kOut_InterfaceQualifier
192    };
193
194    /*
195     * A low level function to build default layout qualifiers.
196     *
197     *   e.g. layout(param1, param2, ...) out;
198     *
199     * GLSL allows default layout qualifiers for in, out, and uniform.
200     */
201    void addLayoutQualifier(const char* param, InterfaceQualifier);
202
203    void compileAndAppendLayoutQualifiers();
204
205    void nextStage() {
206        fShaderStrings.push_back();
207        fCompilerStrings.push_back(this->code().c_str());
208        fCompilerStringLengths.push_back((int)this->code().size());
209        fCodeIndex++;
210    }
211
212    SkString& versionDecl() { return fShaderStrings[kVersionDecl]; }
213    SkString& extensions() { return fShaderStrings[kExtensions]; }
214    SkString& definitions() { return fShaderStrings[kDefinitions]; }
215    SkString& precisionQualifier() { return fShaderStrings[kPrecisionQualifier]; }
216    SkString& layoutQualifiers() { return fShaderStrings[kLayoutQualifiers]; }
217    SkString& uniforms() { return fShaderStrings[kUniforms]; }
218    SkString& inputs() { return fShaderStrings[kInputs]; }
219    SkString& outputs() { return fShaderStrings[kOutputs]; }
220    SkString& functions() { return fShaderStrings[kFunctions]; }
221    SkString& main() { return fShaderStrings[kMain]; }
222    SkString& code() { return fShaderStrings[fCodeIndex]; }
223
224    virtual void onFinalize() = 0;
225
226    enum {
227        kVersionDecl,
228        kExtensions,
229        kDefinitions,
230        kPrecisionQualifier,
231        kLayoutQualifiers,
232        kUniforms,
233        kInputs,
234        kOutputs,
235        kFunctions,
236        kMain,
237        kCode,
238    };
239
240    GrGLSLProgramBuilder* fProgramBuilder;
241    SkSTArray<kCode, const char*, true> fCompilerStrings;
242    SkSTArray<kCode, int, true> fCompilerStringLengths;
243    SkSTArray<kCode, SkString> fShaderStrings;
244    SkString fCode;
245    SkString fFunctions;
246    SkString fExtensions;
247
248    VarArray fInputs;
249    VarArray fOutputs;
250    uint32_t fFeaturesAddedMask;
251    SkSTArray<1, SkString> fLayoutParams[kLastInterfaceQualifier + 1];
252    int fCodeIndex;
253    bool fFinalized;
254
255    friend class GrGLSLProgramBuilder;
256    friend class GrGLProgramBuilder;
257    friend class GrGLSLVaryingHandler; // to access noperspective interpolation feature.
258    friend class GrGLPathProgramBuilder; // to access fInputs.
259    friend class GrVkPipelineStateBuilder;
260};
261#endif
262