1/*
2 * Copyright 2016 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#include "SkSLHCodeGenerator.h"
9
10#include "SkSLParser.h"
11#include "SkSLUtil.h"
12#include "ir/SkSLEnum.h"
13#include "ir/SkSLFunctionDeclaration.h"
14#include "ir/SkSLFunctionDefinition.h"
15#include "ir/SkSLSection.h"
16#include "ir/SkSLVarDeclarations.h"
17
18namespace SkSL {
19
20HCodeGenerator::HCodeGenerator(const Context* context, const Program* program,
21                               ErrorReporter* errors, String name, OutputStream* out)
22: INHERITED(program, errors, out)
23, fContext(*context)
24, fName(std::move(name))
25, fFullName(String::printf("Gr%s", fName.c_str()))
26, fSectionAndParameterHelper(*program, *errors) {}
27
28String HCodeGenerator::ParameterType(const Context& context, const Type& type,
29                                     const Layout& layout) {
30    if (layout.fCType != "") {
31        return layout.fCType;
32    } else if (type == *context.fFloat_Type || type == *context.fHalf_Type) {
33        return "float";
34    } else if (type == *context.fFloat2_Type || type == *context.fHalf2_Type) {
35        return "SkPoint";
36    } else if (type == *context.fInt4_Type || type == *context.fShort4_Type) {
37        return "SkIRect";
38    } else if (type == *context.fFloat4_Type || type == *context.fHalf4_Type) {
39        return "SkRect";
40    } else if (type == *context.fFloat4x4_Type || type == *context.fHalf4x4_Type) {
41        return "SkMatrix44";
42    } else if (type.kind() == Type::kSampler_Kind) {
43        return "sk_sp<GrTextureProxy>";
44    } else if (type == *context.fFragmentProcessor_Type) {
45        return "std::unique_ptr<GrFragmentProcessor>";
46    }
47    return type.name();
48}
49
50String HCodeGenerator::FieldType(const Context& context, const Type& type,
51                                 const Layout& layout) {
52    if (type.kind() == Type::kSampler_Kind) {
53        return "TextureSampler";
54    } else if (type == *context.fFragmentProcessor_Type) {
55        // we don't store fragment processors in fields, they get registered via
56        // registerChildProcessor instead
57        ASSERT(false);
58        return "<error>";
59    }
60    return ParameterType(context, type, layout);
61}
62
63void HCodeGenerator::writef(const char* s, va_list va) {
64    static constexpr int BUFFER_SIZE = 1024;
65    va_list copy;
66    va_copy(copy, va);
67    char buffer[BUFFER_SIZE];
68    int length = vsnprintf(buffer, BUFFER_SIZE, s, va);
69    if (length < BUFFER_SIZE) {
70        fOut->write(buffer, length);
71    } else {
72        std::unique_ptr<char[]> heap(new char[length + 1]);
73        vsprintf(heap.get(), s, copy);
74        fOut->write(heap.get(), length);
75    }
76}
77
78void HCodeGenerator::writef(const char* s, ...) {
79    va_list va;
80    va_start(va, s);
81    this->writef(s, va);
82    va_end(va);
83}
84
85bool HCodeGenerator::writeSection(const char* name, const char* prefix) {
86    const Section* s = fSectionAndParameterHelper.getSection(name);
87    if (s) {
88        this->writef("%s%s", prefix, s->fText.c_str());
89        return true;
90    }
91    return false;
92}
93
94void HCodeGenerator::writeExtraConstructorParams(const char* separator) {
95    // super-simple parse, just assume the last token before a comma is the name of a parameter
96    // (which is true as long as there are no multi-parameter template types involved). Will replace
97    // this with something more robust if the need arises.
98    const Section* section = fSectionAndParameterHelper.getSection(CONSTRUCTOR_PARAMS_SECTION);
99    if (section) {
100        const char* s = section->fText.c_str();
101        #define BUFFER_SIZE 64
102        char lastIdentifier[BUFFER_SIZE];
103        int lastIdentifierLength = 0;
104        bool foundBreak = false;
105        while (*s) {
106            char c = *s;
107            ++s;
108            if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
109                c == '_') {
110                if (foundBreak) {
111                    lastIdentifierLength = 0;
112                    foundBreak = false;
113                }
114                ASSERT(lastIdentifierLength < BUFFER_SIZE);
115                lastIdentifier[lastIdentifierLength] = c;
116                ++lastIdentifierLength;
117            } else {
118                foundBreak = true;
119                if (c == ',') {
120                    ASSERT(lastIdentifierLength < BUFFER_SIZE);
121                    lastIdentifier[lastIdentifierLength] = 0;
122                    this->writef("%s%s", separator, lastIdentifier);
123                    separator = ", ";
124                } else if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
125                    lastIdentifierLength = 0;
126                }
127            }
128        }
129        if (lastIdentifierLength) {
130            ASSERT(lastIdentifierLength < BUFFER_SIZE);
131            lastIdentifier[lastIdentifierLength] = 0;
132            this->writef("%s%s", separator, lastIdentifier);
133        }
134    }
135}
136
137void HCodeGenerator::writeMake() {
138    const char* separator;
139    if (!this->writeSection(MAKE_SECTION)) {
140        this->writef("    static std::unique_ptr<GrFragmentProcessor> Make(");
141        separator = "";
142        for (const auto& param : fSectionAndParameterHelper.getParameters()) {
143            this->writef("%s%s %s", separator, ParameterType(fContext, param->fType,
144                                                             param->fModifiers.fLayout).c_str(),
145                         String(param->fName).c_str());
146            separator = ", ";
147        }
148        this->writeSection(CONSTRUCTOR_PARAMS_SECTION, separator);
149        this->writef(") {\n"
150                     "        return std::unique_ptr<GrFragmentProcessor>(new %s(",
151                     fFullName.c_str());
152        separator = "";
153        for (const auto& param : fSectionAndParameterHelper.getParameters()) {
154            if (param->fType == *fContext.fFragmentProcessor_Type) {
155                this->writef("%sstd::move(%s)", separator, String(param->fName).c_str());
156            } else {
157                this->writef("%s%s", separator, String(param->fName).c_str());
158            }
159            separator = ", ";
160        }
161        this->writeExtraConstructorParams(separator);
162        this->writef("));\n"
163                     "    }\n");
164    }
165}
166
167void HCodeGenerator::failOnSection(const char* section, const char* msg) {
168    std::vector<const Section*> s = fSectionAndParameterHelper.getSections(section);
169    if (s.size()) {
170        fErrors.error(s[0]->fOffset, String("@") + section + " " + msg);
171    }
172}
173
174void HCodeGenerator::writeConstructor() {
175    if (this->writeSection(CONSTRUCTOR_SECTION)) {
176        const char* msg = "may not be present when constructor is overridden";
177        this->failOnSection(CONSTRUCTOR_CODE_SECTION, msg);
178        this->failOnSection(CONSTRUCTOR_PARAMS_SECTION, msg);
179        this->failOnSection(COORD_TRANSFORM_SECTION, msg);
180        this->failOnSection(INITIALIZERS_SECTION, msg);
181        this->failOnSection(OPTIMIZATION_FLAGS_SECTION, msg);
182    }
183    this->writef("    %s(", fFullName.c_str());
184    const char* separator = "";
185    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
186        this->writef("%s%s %s", separator, ParameterType(fContext, param->fType,
187                                                         param->fModifiers.fLayout).c_str(),
188                     String(param->fName).c_str());
189        separator = ", ";
190    }
191    this->writeSection(CONSTRUCTOR_PARAMS_SECTION, separator);
192    this->writef(")\n"
193                 "    : INHERITED(k%s_ClassID", fFullName.c_str());
194    if (!this->writeSection(OPTIMIZATION_FLAGS_SECTION, ", (OptimizationFlags) ")) {
195        this->writef(", kNone_OptimizationFlags");
196    }
197    this->writef(")");
198    this->writeSection(INITIALIZERS_SECTION, "\n    , ");
199    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
200        String nameString(param->fName);
201        const char* name = nameString.c_str();
202        if (param->fType.kind() == Type::kSampler_Kind) {
203            this->writef("\n    , %s(std::move(%s)", FieldName(name).c_str(), name);
204            for (const Section* s : fSectionAndParameterHelper.getSections(
205                                                                          SAMPLER_PARAMS_SECTION)) {
206                if (s->fArgument == name) {
207                    this->writef(", %s", s->fText.c_str());
208                }
209            }
210            this->writef(")");
211        } else if (param->fType == *fContext.fFragmentProcessor_Type) {
212            // do nothing
213        } else {
214            this->writef("\n    , %s(%s)", FieldName(name).c_str(), name);
215        }
216    }
217    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
218        String field = FieldName(s->fArgument.c_str());
219        this->writef("\n    , %sCoordTransform(%s, %s.proxy())", field.c_str(), s->fText.c_str(),
220                     field.c_str());
221    }
222    this->writef(" {\n");
223    this->writeSection(CONSTRUCTOR_CODE_SECTION);
224    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
225        if (param->fType.kind() == Type::kSampler_Kind) {
226            this->writef("        this->addTextureSampler(&%s);\n",
227                         FieldName(String(param->fName).c_str()).c_str());
228        } else if (param->fType == *fContext.fFragmentProcessor_Type) {
229            this->writef("        this->registerChildProcessor(std::move(%s));",
230                         String(param->fName).c_str());
231        }
232    }
233    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
234        String field = FieldName(s->fArgument.c_str());
235        this->writef("        this->addCoordTransform(&%sCoordTransform);\n", field.c_str());
236    }
237    this->writef("    }\n");
238}
239
240void HCodeGenerator::writeFields() {
241    this->writeSection(FIELDS_SECTION);
242    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
243        if (param->fType == *fContext.fFragmentProcessor_Type) {
244            continue;
245        }
246        this->writef("    %s %s;\n", FieldType(fContext, param->fType,
247                                               param->fModifiers.fLayout).c_str(),
248                     FieldName(String(param->fName).c_str()).c_str());
249    }
250    for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
251        this->writef("    GrCoordTransform %sCoordTransform;\n",
252                     FieldName(s->fArgument.c_str()).c_str());
253    }
254}
255
256String HCodeGenerator::GetHeader(const Program& program, ErrorReporter& errors) {
257    SymbolTable types(&errors);
258    Parser parser(program.fSource->c_str(), program.fSource->length(), types, errors);
259    for (;;) {
260        Token header = parser.nextRawToken();
261        switch (header.fKind) {
262            case Token::WHITESPACE:
263                break;
264            case Token::BLOCK_COMMENT:
265                return String(program.fSource->c_str() + header.fOffset, header.fLength);
266            default:
267                return "";
268        }
269    }
270}
271
272bool HCodeGenerator::generateCode() {
273    this->writef("%s\n", GetHeader(fProgram, fErrors).c_str());
274    this->writef(kFragmentProcessorHeader, fFullName.c_str());
275    this->writef("#ifndef %s_DEFINED\n"
276                 "#define %s_DEFINED\n",
277                 fFullName.c_str(),
278                 fFullName.c_str());
279    this->writef("#include \"SkTypes.h\"\n"
280                 "#if SK_SUPPORT_GPU\n");
281    this->writeSection(HEADER_SECTION);
282    this->writef("#include \"GrFragmentProcessor.h\"\n"
283                 "#include \"GrCoordTransform.h\"\n");
284    this->writef("class %s : public GrFragmentProcessor {\n"
285                 "public:\n",
286                 fFullName.c_str());
287    for (const auto& p : fProgram.fElements) {
288        if (ProgramElement::kEnum_Kind == p->fKind && !((Enum&) *p).fBuiltin) {
289            this->writef("%s\n", p->description().c_str());
290        }
291    }
292    this->writeSection(CLASS_SECTION);
293    for (const auto& param : fSectionAndParameterHelper.getParameters()) {
294        if (param->fType.kind() == Type::kSampler_Kind ||
295            param->fType.kind() == Type::kOther_Kind) {
296            continue;
297        }
298        String nameString(param->fName);
299        const char* name = nameString.c_str();
300        this->writef("    %s %s() const { return %s; }\n",
301                     FieldType(fContext, param->fType, param->fModifiers.fLayout).c_str(), name,
302                     FieldName(name).c_str());
303    }
304    this->writeMake();
305    this->writef("    %s(const %s& src);\n"
306                 "    std::unique_ptr<GrFragmentProcessor> clone() const override;\n"
307                 "    const char* name() const override { return \"%s\"; }\n"
308                 "private:\n",
309                 fFullName.c_str(), fFullName.c_str(), fName.c_str());
310    this->writeConstructor();
311    this->writef("    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;\n"
312                 "    void onGetGLSLProcessorKey(const GrShaderCaps&,"
313                                                "GrProcessorKeyBuilder*) const override;\n"
314                 "    bool onIsEqual(const GrFragmentProcessor&) const override;\n"
315                 "    GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n");
316    this->writeFields();
317    this->writef("    typedef GrFragmentProcessor INHERITED;\n"
318                "};\n");
319    this->writeSection(HEADER_END_SECTION);
320    this->writef("#endif\n"
321                 "#endif\n");
322    return 0 == fErrors.errorCount();
323}
324
325} // namespace
326