blob: c5ddc7a76ede7a713f3f0a4b076858a71e232269 [file] [log] [blame]
Ethan Nicholas762466e2017-06-29 10:03:38 -04001/*
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 "SkSLCPPCodeGenerator.h"
9
10#include "SkSLCompiler.h"
11#include "SkSLHCodeGenerator.h"
12
13namespace SkSL {
14
15static bool needs_uniform_var(const Variable& var) {
Brian Osman1cb41712017-10-19 12:54:52 -040016 return (var.fModifiers.fFlags & Modifiers::kUniform_Flag);
Ethan Nicholas762466e2017-06-29 10:03:38 -040017}
18
19CPPCodeGenerator::CPPCodeGenerator(const Context* context, const Program* program,
20 ErrorReporter* errors, String name, OutputStream* out)
21: INHERITED(context, program, errors, out)
22, fName(std::move(name))
23, fFullName(String::printf("Gr%s", fName.c_str()))
24, fSectionAndParameterHelper(*program, *errors) {
25 fLineEnding = "\\n";
26}
27
28void CPPCodeGenerator::writef(const char* s, va_list va) {
29 static constexpr int BUFFER_SIZE = 1024;
Ethan Nicholas9fb036f2017-07-05 16:19:09 -040030 va_list copy;
31 va_copy(copy, va);
Ethan Nicholas762466e2017-06-29 10:03:38 -040032 char buffer[BUFFER_SIZE];
33 int length = vsnprintf(buffer, BUFFER_SIZE, s, va);
34 if (length < BUFFER_SIZE) {
35 fOut->write(buffer, length);
36 } else {
37 std::unique_ptr<char[]> heap(new char[length + 1]);
Ethan Nicholas9fb036f2017-07-05 16:19:09 -040038 vsprintf(heap.get(), s, copy);
Ethan Nicholas762466e2017-06-29 10:03:38 -040039 fOut->write(heap.get(), length);
40 }
41}
42
43void CPPCodeGenerator::writef(const char* s, ...) {
44 va_list va;
45 va_start(va, s);
46 this->writef(s, va);
47 va_end(va);
48}
49
50void CPPCodeGenerator::writeHeader() {
51}
52
Ethan Nicholasf7b88202017-09-18 14:10:39 -040053bool CPPCodeGenerator::usesPrecisionModifiers() const {
54 return false;
Ethan Nicholas762466e2017-06-29 10:03:38 -040055}
56
Ethan Nicholasf7b88202017-09-18 14:10:39 -040057String CPPCodeGenerator::getTypeName(const Type& type) {
58 return type.name();
Ethan Nicholas5af9ea32017-07-28 15:19:46 -040059}
Ethan Nicholasf7b88202017-09-18 14:10:39 -040060
Ethan Nicholas762466e2017-06-29 10:03:38 -040061void CPPCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
62 Precedence parentPrecedence) {
63 if (b.fOperator == Token::PERCENT) {
64 // need to use "%%" instead of "%" b/c the code will be inside of a printf
65 Precedence precedence = GetBinaryPrecedence(b.fOperator);
66 if (precedence >= parentPrecedence) {
67 this->write("(");
68 }
69 this->writeExpression(*b.fLeft, precedence);
70 this->write(" %% ");
71 this->writeExpression(*b.fRight, precedence);
72 if (precedence >= parentPrecedence) {
73 this->write(")");
74 }
75 } else {
76 INHERITED::writeBinaryExpression(b, parentPrecedence);
77 }
78}
79
80void CPPCodeGenerator::writeIndexExpression(const IndexExpression& i) {
81 const Expression& base = *i.fBase;
82 if (base.fKind == Expression::kVariableReference_Kind) {
83 int builtin = ((VariableReference&) base).fVariable.fModifiers.fLayout.fBuiltin;
84 if (SK_TRANSFORMEDCOORDS2D_BUILTIN == builtin) {
85 this->write("%s");
86 if (i.fIndex->fKind != Expression::kIntLiteral_Kind) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -070087 fErrors.error(i.fIndex->fOffset,
Ethan Nicholas762466e2017-06-29 10:03:38 -040088 "index into sk_TransformedCoords2D must be an integer literal");
89 return;
90 }
91 int64_t index = ((IntLiteral&) *i.fIndex).fValue;
92 String name = "sk_TransformedCoords2D_" + to_string(index);
93 fFormatArgs.push_back(name + ".c_str()");
94 if (fWrittenTransformedCoords.find(index) == fWrittenTransformedCoords.end()) {
Brian Osman72a37be2017-08-15 09:19:53 -040095 fExtraEmitCodeCode += " SkString " + name +
Ethan Nicholas762466e2017-06-29 10:03:38 -040096 " = fragBuilder->ensureCoords2D(args.fTransformedCoords[" +
97 to_string(index) + "]);\n";
98 fWrittenTransformedCoords.insert(index);
99 }
100 return;
101 } else if (SK_TEXTURESAMPLERS_BUILTIN == builtin) {
102 this->write("%s");
103 if (i.fIndex->fKind != Expression::kIntLiteral_Kind) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700104 fErrors.error(i.fIndex->fOffset,
Ethan Nicholas762466e2017-06-29 10:03:38 -0400105 "index into sk_TextureSamplers must be an integer literal");
106 return;
107 }
108 int64_t index = ((IntLiteral&) *i.fIndex).fValue;
109 fFormatArgs.push_back(" fragBuilder->getProgramBuilder()->samplerVariable("
110 "args.fTexSamplers[" + to_string(index) + "]).c_str()");
111 return;
112 }
113 }
114 INHERITED::writeIndexExpression(i);
115}
116
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400117static String default_value(const Type& type) {
Ethan Nicholase9d172a2017-11-20 12:12:24 -0500118 if (type.fName == "bool") {
119 return "false";
120 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400121 switch (type.kind()) {
122 case Type::kScalar_Kind: return "0";
123 case Type::kVector_Kind: return type.name() + "(0)";
124 case Type::kMatrix_Kind: return type.name() + "(1)";
125 default: ABORT("unsupported default_value type\n");
126 }
Ethan Nicholas762466e2017-06-29 10:03:38 -0400127}
128
Ethan Nicholase9d172a2017-11-20 12:12:24 -0500129static String default_value(const Variable& var) {
130 if (var.fModifiers.fLayout.fCType == "GrColor4f") {
131 return "GrColor4f::kIllegalConstructor";
132 }
133 return default_value(var.fType);
134}
135
Ethan Nicholas762466e2017-06-29 10:03:38 -0400136static bool is_private(const Variable& var) {
137 return !(var.fModifiers.fFlags & Modifiers::kUniform_Flag) &&
138 !(var.fModifiers.fFlags & Modifiers::kIn_Flag) &&
139 var.fStorage == Variable::kGlobal_Storage &&
140 var.fModifiers.fLayout.fBuiltin == -1;
141}
142
Ethan Nicholasd608c092017-10-26 09:30:08 -0400143void CPPCodeGenerator::writeRuntimeValue(const Type& type, const Layout& layout,
144 const String& cppCode) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400145 if (type.isFloat()) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400146 this->write("%f");
147 fFormatArgs.push_back(cppCode);
148 } else if (type == *fContext.fInt_Type) {
149 this->write("%d");
150 fFormatArgs.push_back(cppCode);
151 } else if (type == *fContext.fBool_Type) {
152 this->write("%s");
153 fFormatArgs.push_back("(" + cppCode + " ? \"true\" : \"false\")");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400154 } else if (type == *fContext.fFloat2_Type || type == *fContext.fHalf2_Type) {
155 this->write(type.name() + "(%f, %f)");
Ethan Nicholas762466e2017-06-29 10:03:38 -0400156 fFormatArgs.push_back(cppCode + ".fX");
157 fFormatArgs.push_back(cppCode + ".fY");
Ethan Nicholas82399462017-10-16 12:35:44 -0400158 } else if (type == *fContext.fFloat4_Type || type == *fContext.fHalf4_Type) {
159 this->write(type.name() + "(%f, %f, %f, %f)");
Ethan Nicholasd608c092017-10-26 09:30:08 -0400160 if (layout.fCType == "SkPMColor") {
161 fFormatArgs.push_back("SkGetPackedR32(" + cppCode + ") / 255.0");
162 fFormatArgs.push_back("SkGetPackedG32(" + cppCode + ") / 255.0");
163 fFormatArgs.push_back("SkGetPackedB32(" + cppCode + ") / 255.0");
164 fFormatArgs.push_back("SkGetPackedA32(" + cppCode + ") / 255.0");
Ethan Nicholase9d172a2017-11-20 12:12:24 -0500165 } else if (layout.fCType == "GrColor4f") {
166 fFormatArgs.push_back(cppCode + ".fRGBA[0]");
167 fFormatArgs.push_back(cppCode + ".fRGBA[1]");
168 fFormatArgs.push_back(cppCode + ".fRGBA[2]");
169 fFormatArgs.push_back(cppCode + ".fRGBA[3]");
Ethan Nicholasd608c092017-10-26 09:30:08 -0400170 } else {
171 fFormatArgs.push_back(cppCode + ".left()");
172 fFormatArgs.push_back(cppCode + ".top()");
173 fFormatArgs.push_back(cppCode + ".right()");
174 fFormatArgs.push_back(cppCode + ".bottom()");
175 }
Ethan Nicholasaae47c82017-11-10 15:34:03 -0500176 } else if (type.kind() == Type::kEnum_Kind) {
177 this->write("%d");
178 fFormatArgs.push_back("(int) " + cppCode);
Ethan Nicholas2d5f9b32017-12-13 14:36:14 -0500179 } else if (type == *fContext.fInt4_Type || type == *fContext.fShort4_Type) {
180 this->write(type.name() + "(%d, %d, %d, %d)");
181 fFormatArgs.push_back(cppCode + ".left()");
182 fFormatArgs.push_back(cppCode + ".top()");
183 fFormatArgs.push_back(cppCode + ".right()");
184 fFormatArgs.push_back(cppCode + ".bottom()");
Ethan Nicholas762466e2017-06-29 10:03:38 -0400185 } else {
Ethan Nicholas82399462017-10-16 12:35:44 -0400186 printf("unsupported runtime value type '%s'\n", String(type.fName).c_str());
187 ASSERT(false);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400188 }
189}
190
191void CPPCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
192 if (is_private(var)) {
Ethan Nicholasd608c092017-10-26 09:30:08 -0400193 this->writeRuntimeValue(var.fType, var.fModifiers.fLayout, var.fName);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400194 } else {
195 this->writeExpression(value, kTopLevel_Precedence);
196 }
197}
198
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400199String CPPCodeGenerator::getSamplerHandle(const Variable& var) {
200 int samplerCount = 0;
Ethan Nicholas68990be2017-07-13 09:36:52 -0400201 for (const auto param : fSectionAndParameterHelper.getParameters()) {
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400202 if (&var == param) {
203 return "args.fTexSamplers[" + to_string(samplerCount) + "]";
204 }
205 if (param->fType.kind() == Type::kSampler_Kind) {
206 ++samplerCount;
207 }
208 }
209 ABORT("should have found sampler in parameters\n");
210}
211
Ethan Nicholasdcba08e2017-08-02 10:52:54 -0400212void CPPCodeGenerator::writeIntLiteral(const IntLiteral& i) {
213 this->write(to_string((int32_t) i.fValue));
214}
215
Ethan Nicholas82399462017-10-16 12:35:44 -0400216void CPPCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
217 if (fCPPMode) {
218 ASSERT(swizzle.fComponents.size() == 1); // no support for multiple swizzle components yet
219 this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
220 switch (swizzle.fComponents[0]) {
221 case 0: this->write(".left()"); break;
222 case 1: this->write(".top()"); break;
223 case 2: this->write(".right()"); break;
224 case 3: this->write(".bottom()"); break;
225 }
226 } else {
227 INHERITED::writeSwizzle(swizzle);
228 }
229}
230
Ethan Nicholas762466e2017-06-29 10:03:38 -0400231void CPPCodeGenerator::writeVariableReference(const VariableReference& ref) {
Ethan Nicholas82399462017-10-16 12:35:44 -0400232 if (fCPPMode) {
233 this->write(ref.fVariable.fName);
234 return;
235 }
Ethan Nicholas762466e2017-06-29 10:03:38 -0400236 switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
237 case SK_INCOLOR_BUILTIN:
238 this->write("%s");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400239 fFormatArgs.push_back(String("args.fInputColor ? args.fInputColor : \"half4(1)\""));
Ethan Nicholas762466e2017-06-29 10:03:38 -0400240 break;
241 case SK_OUTCOLOR_BUILTIN:
242 this->write("%s");
243 fFormatArgs.push_back(String("args.fOutputColor"));
244 break;
245 default:
246 if (ref.fVariable.fType.kind() == Type::kSampler_Kind) {
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400247 this->write("%s");
248 fFormatArgs.push_back("fragBuilder->getProgramBuilder()->samplerVariable(" +
249 this->getSamplerHandle(ref.fVariable) + ").c_str()");
250 return;
Ethan Nicholas762466e2017-06-29 10:03:38 -0400251 }
252 if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag) {
253 this->write("%s");
254 String name = ref.fVariable.fName;
Brian Osman1cb41712017-10-19 12:54:52 -0400255 String var = String::printf("args.fUniformHandler->getUniformCStr(%sVar)",
256 HCodeGenerator::FieldName(name.c_str()).c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400257 String code;
258 if (ref.fVariable.fModifiers.fLayout.fWhen.size()) {
259 code = String::printf("%sVar.isValid() ? %s : \"%s\"",
260 HCodeGenerator::FieldName(name.c_str()).c_str(),
261 var.c_str(),
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400262 default_value(ref.fVariable.fType).c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400263 } else {
264 code = var;
265 }
266 fFormatArgs.push_back(code);
267 } else if (SectionAndParameterHelper::IsParameter(ref.fVariable)) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700268 String name(ref.fVariable.fName);
Ethan Nicholasd608c092017-10-26 09:30:08 -0400269 this->writeRuntimeValue(ref.fVariable.fType, ref.fVariable.fModifiers.fLayout,
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700270 String::printf("_outer.%s()", name.c_str()).c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400271 } else {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700272 this->write(ref.fVariable.fName);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400273 }
274 }
275}
276
Ethan Nicholas6e1cbc02017-07-14 10:12:15 -0400277void CPPCodeGenerator::writeIfStatement(const IfStatement& s) {
278 if (s.fIsStatic) {
279 this->write("@");
280 }
281 INHERITED::writeIfStatement(s);
282}
283
284void CPPCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
285 if (s.fIsStatic) {
286 this->write("@");
287 }
288 INHERITED::writeSwitchStatement(s);
289}
290
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400291void CPPCodeGenerator::writeFunctionCall(const FunctionCall& c) {
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400292 if (c.fFunction.fBuiltin && c.fFunction.fName == "process") {
293 ASSERT(c.fArguments.size() == 1);
294 ASSERT(Expression::kVariableReference_Kind == c.fArguments[0]->fKind);
295 int index = 0;
296 bool found = false;
297 for (const auto& p : fProgram.fElements) {
298 if (ProgramElement::kVar_Kind == p->fKind) {
299 const VarDeclarations* decls = (const VarDeclarations*) p.get();
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000300 for (const auto& raw : decls->fVars) {
301 VarDeclaration& decl = (VarDeclaration&) *raw;
302 if (decl.fVar == &((VariableReference&) *c.fArguments[0]).fVariable) {
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400303 found = true;
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000304 } else if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400305 ++index;
306 }
307 }
308 }
309 if (found) {
310 break;
311 }
312 }
313 ASSERT(found);
314 String childName = "_child" + to_string(index);
315 fExtraEmitCodeCode += " SkString " + childName + "(\"" + childName + "\");\n" +
316 " this->emitChild(" + to_string(index) + ", &" + childName +
317 ", args);\n";
318 this->write("%s");
319 fFormatArgs.push_back(childName + ".c_str()");
320 return;
321 }
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400322 INHERITED::writeFunctionCall(c);
323 if (c.fFunction.fBuiltin && c.fFunction.fName == "texture") {
324 this->write(".%s");
325 ASSERT(c.fArguments.size() >= 1);
326 ASSERT(c.fArguments[0]->fKind == Expression::kVariableReference_Kind);
327 String sampler = this->getSamplerHandle(((VariableReference&) *c.fArguments[0]).fVariable);
328 fFormatArgs.push_back("fragBuilder->getProgramBuilder()->samplerSwizzle(" + sampler +
329 ").c_str()");
330 }
331}
332
Ethan Nicholas762466e2017-06-29 10:03:38 -0400333void CPPCodeGenerator::writeFunction(const FunctionDefinition& f) {
334 if (f.fDeclaration.fName == "main") {
335 fFunctionHeader = "";
336 OutputStream* oldOut = fOut;
337 StringStream buffer;
338 fOut = &buffer;
339 for (const auto& s : ((Block&) *f.fBody).fStatements) {
340 this->writeStatement(*s);
341 this->writeLine();
342 }
343
344 fOut = oldOut;
345 this->write(fFunctionHeader);
346 this->write(buffer.str());
347 } else {
348 INHERITED::writeFunction(f);
349 }
350}
351
352void CPPCodeGenerator::writeSetting(const Setting& s) {
353 static constexpr const char* kPrefix = "sk_Args.";
354 if (!strncmp(s.fName.c_str(), kPrefix, strlen(kPrefix))) {
355 const char* name = s.fName.c_str() + strlen(kPrefix);
Ethan Nicholasd608c092017-10-26 09:30:08 -0400356 this->writeRuntimeValue(s.fType, Layout(), HCodeGenerator::FieldName(name).c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400357 } else {
358 this->write(s.fName.c_str());
359 }
360}
361
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400362bool CPPCodeGenerator::writeSection(const char* name, const char* prefix) {
Ethan Nicholas68990be2017-07-13 09:36:52 -0400363 const Section* s = fSectionAndParameterHelper.getSection(name);
364 if (s) {
365 this->writef("%s%s", prefix, s->fText.c_str());
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400366 return true;
Ethan Nicholas762466e2017-06-29 10:03:38 -0400367 }
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400368 return false;
Ethan Nicholas762466e2017-06-29 10:03:38 -0400369}
370
371void CPPCodeGenerator::writeProgramElement(const ProgramElement& p) {
372 if (p.fKind == ProgramElement::kSection_Kind) {
373 return;
374 }
375 if (p.fKind == ProgramElement::kVar_Kind) {
376 const VarDeclarations& decls = (const VarDeclarations&) p;
377 if (!decls.fVars.size()) {
378 return;
379 }
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000380 const Variable& var = *((VarDeclaration&) *decls.fVars[0]).fVar;
Ethan Nicholas762466e2017-06-29 10:03:38 -0400381 if (var.fModifiers.fFlags & (Modifiers::kIn_Flag | Modifiers::kUniform_Flag) ||
382 -1 != var.fModifiers.fLayout.fBuiltin) {
383 return;
384 }
385 }
386 INHERITED::writeProgramElement(p);
387}
388
389void CPPCodeGenerator::addUniform(const Variable& var) {
390 if (!needs_uniform_var(var)) {
391 return;
392 }
393 const char* precision;
394 if (var.fModifiers.fFlags & Modifiers::kHighp_Flag) {
395 precision = "kHigh_GrSLPrecision";
396 } else if (var.fModifiers.fFlags & Modifiers::kMediump_Flag) {
397 precision = "kMedium_GrSLPrecision";
398 } else if (var.fModifiers.fFlags & Modifiers::kLowp_Flag) {
399 precision = "kLow_GrSLPrecision";
400 } else {
401 precision = "kDefault_GrSLPrecision";
402 }
403 const char* type;
404 if (var.fType == *fContext.fFloat_Type) {
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400405 type = "kFloat_GrSLType";
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400406 } else if (var.fType == *fContext.fHalf_Type) {
407 type = "kHalf_GrSLType";
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400408 } else if (var.fType == *fContext.fFloat2_Type) {
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400409 type = "kFloat2_GrSLType";
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400410 } else if (var.fType == *fContext.fHalf2_Type) {
411 type = "kHalf2_GrSLType";
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400412 } else if (var.fType == *fContext.fFloat4_Type) {
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400413 type = "kFloat4_GrSLType";
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400414 } else if (var.fType == *fContext.fHalf4_Type) {
415 type = "kHalf4_GrSLType";
Brian Osman1cb41712017-10-19 12:54:52 -0400416 } else if (var.fType == *fContext.fFloat4x4_Type) {
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400417 type = "kFloat4x4_GrSLType";
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400418 } else if (var.fType == *fContext.fHalf4x4_Type) {
419 type = "kHalf4x4_GrSLType";
Ethan Nicholas762466e2017-06-29 10:03:38 -0400420 } else {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700421 ABORT("unsupported uniform type: %s %s;\n", String(var.fType.fName).c_str(),
422 String(var.fName).c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400423 }
424 if (var.fModifiers.fLayout.fWhen.size()) {
425 this->writef(" if (%s) {\n ", var.fModifiers.fLayout.fWhen.c_str());
426 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700427 String name(var.fName);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400428 this->writef(" %sVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, %s, "
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700429 "%s, \"%s\");\n", HCodeGenerator::FieldName(name.c_str()).c_str(), type, precision,
430 name.c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400431 if (var.fModifiers.fLayout.fWhen.size()) {
432 this->write(" }\n");
433 }
434}
435
436void CPPCodeGenerator::writePrivateVars() {
437 for (const auto& p : fProgram.fElements) {
438 if (ProgramElement::kVar_Kind == p->fKind) {
439 const VarDeclarations* decls = (const VarDeclarations*) p.get();
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000440 for (const auto& raw : decls->fVars) {
441 VarDeclaration& decl = (VarDeclaration&) *raw;
442 if (is_private(*decl.fVar)) {
443 if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
444 fErrors.error(decl.fOffset,
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400445 "fragmentProcessor variables must be declared 'in'");
446 return;
447 }
Ethan Nicholase9d172a2017-11-20 12:12:24 -0500448 this->writef("%s %s = %s;\n",
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000449 HCodeGenerator::FieldType(fContext, decl.fVar->fType,
450 decl.fVar->fModifiers.fLayout).c_str(),
Ethan Nicholase9d172a2017-11-20 12:12:24 -0500451 String(decl.fVar->fName).c_str(),
452 default_value(*decl.fVar).c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400453 }
454 }
455 }
456 }
457}
458
459void CPPCodeGenerator::writePrivateVarValues() {
460 for (const auto& p : fProgram.fElements) {
461 if (ProgramElement::kVar_Kind == p->fKind) {
462 const VarDeclarations* decls = (const VarDeclarations*) p.get();
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000463 for (const auto& raw : decls->fVars) {
464 VarDeclaration& decl = (VarDeclaration&) *raw;
465 if (is_private(*decl.fVar) && decl.fValue) {
466 this->writef("%s = ", String(decl.fVar->fName).c_str());
Ethan Nicholas82399462017-10-16 12:35:44 -0400467 fCPPMode = true;
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000468 this->writeExpression(*decl.fValue, kAssignment_Precedence);
Ethan Nicholas82399462017-10-16 12:35:44 -0400469 fCPPMode = false;
470 this->write(";\n");
Ethan Nicholas762466e2017-06-29 10:03:38 -0400471 }
472 }
473 }
474 }
475}
476
Ethan Nicholas82399462017-10-16 12:35:44 -0400477static bool is_accessible(const Variable& var) {
478 return Type::kSampler_Kind != var.fType.kind() &&
479 Type::kOther_Kind != var.fType.kind();
480}
481
Ethan Nicholas5b6e6272017-10-13 13:11:06 -0400482void CPPCodeGenerator::writeCodeAppend(const String& code) {
483 // codeAppendf can only handle appending 1024 bytes at a time, so we need to break the string
484 // into chunks. Unfortunately we can't tell exactly how long the string is going to end up,
485 // because printf escape sequences get replaced by strings of unknown length, but keeping the
486 // format string below 512 bytes is probably safe.
487 static constexpr size_t maxChunkSize = 512;
488 size_t start = 0;
489 size_t index = 0;
490 size_t argStart = 0;
491 size_t argCount;
492 while (index < code.size()) {
493 argCount = 0;
494 this->write(" fragBuilder->codeAppendf(\"");
495 while (index < code.size() && index < start + maxChunkSize) {
496 if ('%' == code[index]) {
497 if (index == start + maxChunkSize - 1 || index == code.size() - 1) {
498 break;
499 }
500 if (code[index + 1] != '%') {
501 ++argCount;
502 }
Ethan Nicholasef0c9fd2017-10-30 10:04:14 -0400503 } else if ('\\' == code[index] && index == start + maxChunkSize - 1) {
504 // avoid splitting an escape sequence that happens to fall across a chunk boundary
505 break;
Ethan Nicholas5b6e6272017-10-13 13:11:06 -0400506 }
507 ++index;
508 }
509 fOut->write(code.c_str() + start, index - start);
510 this->write("\"");
511 for (size_t i = argStart; i < argStart + argCount; ++i) {
512 this->writef(", %s", fFormatArgs[i].c_str());
513 }
514 this->write(");\n");
515 argStart += argCount;
516 start = index;
517 }
518}
519
Ethan Nicholas762466e2017-06-29 10:03:38 -0400520bool CPPCodeGenerator::writeEmitCode(std::vector<const Variable*>& uniforms) {
521 this->write(" void emitCode(EmitArgs& args) override {\n"
522 " GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;\n");
523 this->writef(" const %s& _outer = args.fFp.cast<%s>();\n"
524 " (void) _outer;\n",
525 fFullName.c_str(), fFullName.c_str());
Ethan Nicholas82399462017-10-16 12:35:44 -0400526 for (const auto& p : fProgram.fElements) {
527 if (ProgramElement::kVar_Kind == p->fKind) {
528 const VarDeclarations* decls = (const VarDeclarations*) p.get();
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000529 for (const auto& raw : decls->fVars) {
530 VarDeclaration& decl = (VarDeclaration&) *raw;
531 String nameString(decl.fVar->fName);
Ethan Nicholas82399462017-10-16 12:35:44 -0400532 const char* name = nameString.c_str();
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000533 if (SectionAndParameterHelper::IsParameter(*decl.fVar) &&
534 is_accessible(*decl.fVar)) {
Ethan Nicholas82399462017-10-16 12:35:44 -0400535 this->writef(" auto %s = _outer.%s();\n"
536 " (void) %s;\n",
537 name, name, name);
538 }
539 }
540 }
541 }
Ethan Nicholas762466e2017-06-29 10:03:38 -0400542 this->writePrivateVarValues();
543 for (const auto u : uniforms) {
544 this->addUniform(*u);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400545 }
546 this->writeSection(EMIT_CODE_SECTION);
547 OutputStream* old = fOut;
548 StringStream mainBuffer;
549 fOut = &mainBuffer;
550 bool result = INHERITED::generateCode();
551 fOut = old;
Ethan Nicholas5b6e6272017-10-13 13:11:06 -0400552 this->writef("%s", fExtraEmitCodeCode.c_str());
553 this->writeCodeAppend(mainBuffer.str());
554 this->write(" }\n");
Ethan Nicholas762466e2017-06-29 10:03:38 -0400555 return result;
556}
557
558void CPPCodeGenerator::writeSetData(std::vector<const Variable*>& uniforms) {
559 const char* fullName = fFullName.c_str();
Ethan Nicholas68990be2017-07-13 09:36:52 -0400560 const Section* section = fSectionAndParameterHelper.getSection(SET_DATA_SECTION);
561 const char* pdman = section ? section->fArgument.c_str() : "pdman";
Ethan Nicholas762466e2017-06-29 10:03:38 -0400562 this->writef(" void onSetData(const GrGLSLProgramDataManager& %s, "
563 "const GrFragmentProcessor& _proc) override {\n",
564 pdman);
565 bool wroteProcessor = false;
566 for (const auto u : uniforms) {
567 if (u->fModifiers.fFlags & Modifiers::kIn_Flag) {
568 if (!wroteProcessor) {
569 this->writef(" const %s& _outer = _proc.cast<%s>();\n", fullName, fullName);
570 wroteProcessor = true;
571 this->writef(" {\n");
572 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700573 String nameString(u->fName);
574 const char* name = nameString.c_str();
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400575 if (u->fType == *fContext.fFloat4_Type || u->fType == *fContext.fHalf4_Type) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400576 this->writef(" const SkRect %sValue = _outer.%s();\n"
Ethan Nicholasee338732017-07-17 15:13:45 -0400577 " %s.set4fv(%sVar, 1, (float*) &%sValue);\n",
Ethan Nicholas762466e2017-06-29 10:03:38 -0400578 name, name, pdman, HCodeGenerator::FieldName(name).c_str(), name);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400579 } else if (u->fType == *fContext.fFloat4x4_Type ||
580 u->fType == *fContext.fHalf4x4_Type) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400581 this->writef(" float %sValue[16];\n"
582 " _outer.%s().asColMajorf(%sValue);\n"
583 " %s.setMatrix4f(%sVar, %sValue);\n",
584 name, name, name, pdman, HCodeGenerator::FieldName(name).c_str(),
585 name);
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400586 } else if (u->fType == *fContext.fFragmentProcessor_Type) {
587 // do nothing
Ethan Nicholas762466e2017-06-29 10:03:38 -0400588 } else {
589 this->writef(" %s.set1f(%sVar, _outer.%s());\n",
590 pdman, HCodeGenerator::FieldName(name).c_str(), name);
591 }
592 }
593 }
594 if (wroteProcessor) {
595 this->writef(" }\n");
596 }
Ethan Nicholas68990be2017-07-13 09:36:52 -0400597 if (section) {
Ethan Nicholas2d5f9b32017-12-13 14:36:14 -0500598 int samplerIndex = 0;
Ethan Nicholas762466e2017-06-29 10:03:38 -0400599 for (const auto& p : fProgram.fElements) {
600 if (ProgramElement::kVar_Kind == p->fKind) {
601 const VarDeclarations* decls = (const VarDeclarations*) p.get();
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000602 for (const auto& raw : decls->fVars) {
603 VarDeclaration& decl = (VarDeclaration&) *raw;
604 String nameString(decl.fVar->fName);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700605 const char* name = nameString.c_str();
Ethan Nicholas2d5f9b32017-12-13 14:36:14 -0500606 if (decl.fVar->fType.kind() == Type::kSampler_Kind) {
607 this->writef(" GrSurfaceProxy& %sProxy = "
608 "*_outer.textureSampler(%d).proxy();\n",
609 name, samplerIndex);
610 this->writef(" GrTexture& %s = *%sProxy.priv().peekTexture();\n",
611 name, name);
612 this->writef(" (void) %s;\n", name);
613 ++samplerIndex;
614 } else if (needs_uniform_var(*decl.fVar)) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400615 this->writef(" UniformHandle& %s = %sVar;\n"
616 " (void) %s;\n",
617 name, HCodeGenerator::FieldName(name).c_str(), name);
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000618 } else if (SectionAndParameterHelper::IsParameter(*decl.fVar) &&
619 decl.fVar->fType != *fContext.fFragmentProcessor_Type) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400620 if (!wroteProcessor) {
621 this->writef(" const %s& _outer = _proc.cast<%s>();\n", fullName,
622 fullName);
623 wroteProcessor = true;
624 }
625 this->writef(" auto %s = _outer.%s();\n"
626 " (void) %s;\n",
627 name, name, name);
628 }
629 }
630 }
631 }
632 this->writeSection(SET_DATA_SECTION);
633 }
634 this->write(" }\n");
635}
636
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400637void CPPCodeGenerator::writeClone() {
638 if (!this->writeSection(CLONE_SECTION)) {
639 if (fSectionAndParameterHelper.getSection(FIELDS_SECTION)) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700640 fErrors.error(0, "fragment processors with custom @fields must also have a custom"
641 "@clone");
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400642 }
643 this->writef("%s::%s(const %s& src)\n"
Ethan Nicholasabff9562017-10-09 10:54:08 -0400644 ": INHERITED(k%s_ClassID, src.optimizationFlags())", fFullName.c_str(),
645 fFullName.c_str(), fFullName.c_str(), fFullName.c_str());
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400646 for (const auto& param : fSectionAndParameterHelper.getParameters()) {
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400647 if (param->fType == *fContext.fFragmentProcessor_Type) {
648 continue;
649 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700650 String fieldName = HCodeGenerator::FieldName(String(param->fName).c_str());
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400651 this->writef("\n, %s(src.%s)",
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400652 fieldName.c_str(),
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400653 fieldName.c_str());
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400654 }
655 for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
656 String fieldName = HCodeGenerator::FieldName(s->fArgument.c_str());
657 this->writef("\n, %sCoordTransform(src.%sCoordTransform)", fieldName.c_str(),
658 fieldName.c_str());
659 }
Ethan Nicholasabff9562017-10-09 10:54:08 -0400660 this->writef(" {\n");
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400661 int childCount = 0;
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400662 for (const auto& param : fSectionAndParameterHelper.getParameters()) {
663 if (param->fType.kind() == Type::kSampler_Kind) {
664 this->writef(" this->addTextureSampler(&%s);\n",
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700665 HCodeGenerator::FieldName(String(param->fName).c_str()).c_str());
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400666 } else if (param->fType == *fContext.fFragmentProcessor_Type) {
667 this->writef(" this->registerChildProcessor(src.childProcessor(%d).clone());"
668 "\n", childCount++);
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400669 }
670 }
671 for (const Section* s : fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION)) {
672 String field = HCodeGenerator::FieldName(s->fArgument.c_str());
673 this->writef(" this->addCoordTransform(&%sCoordTransform);\n", field.c_str());
674 }
675 this->write("}\n");
Brian Salomonaff329b2017-08-11 09:40:37 -0400676 this->writef("std::unique_ptr<GrFragmentProcessor> %s::clone() const {\n",
677 fFullName.c_str());
678 this->writef(" return std::unique_ptr<GrFragmentProcessor>(new %s(*this));\n",
679 fFullName.c_str());
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400680 this->write("}\n");
681 }
682}
683
Ethan Nicholas762466e2017-06-29 10:03:38 -0400684void CPPCodeGenerator::writeTest() {
Ethan Nicholas68990be2017-07-13 09:36:52 -0400685 const Section* test = fSectionAndParameterHelper.getSection(TEST_CODE_SECTION);
686 if (test) {
Brian Salomonaff329b2017-08-11 09:40:37 -0400687 this->writef(
688 "GR_DEFINE_FRAGMENT_PROCESSOR_TEST(%s);\n"
689 "#if GR_TEST_UTILS\n"
690 "std::unique_ptr<GrFragmentProcessor> %s::TestCreate(GrProcessorTestData* %s) {\n",
691 fFullName.c_str(),
692 fFullName.c_str(),
693 test->fArgument.c_str());
Ethan Nicholas68990be2017-07-13 09:36:52 -0400694 this->writeSection(TEST_CODE_SECTION);
695 this->write("}\n"
696 "#endif\n");
Ethan Nicholas762466e2017-06-29 10:03:38 -0400697 }
Ethan Nicholas762466e2017-06-29 10:03:38 -0400698}
699
700void CPPCodeGenerator::writeGetKey() {
701 this->writef("void %s::onGetGLSLProcessorKey(const GrShaderCaps& caps, "
702 "GrProcessorKeyBuilder* b) const {\n",
703 fFullName.c_str());
Ethan Nicholas68990be2017-07-13 09:36:52 -0400704 for (const auto& param : fSectionAndParameterHelper.getParameters()) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700705 String nameString(param->fName);
706 const char* name = nameString.c_str();
Ethan Nicholas762466e2017-06-29 10:03:38 -0400707 if (param->fModifiers.fLayout.fKey != Layout::kNo_Key &&
708 (param->fModifiers.fFlags & Modifiers::kUniform_Flag)) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700709 fErrors.error(param->fOffset,
Ethan Nicholas762466e2017-06-29 10:03:38 -0400710 "layout(key) may not be specified on uniforms");
711 }
712 switch (param->fModifiers.fLayout.fKey) {
713 case Layout::kKey_Key:
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400714 if (param->fType == *fContext.fFloat4x4_Type) {
715 ABORT("no automatic key handling for float4x4\n");
716 } else if (param->fType == *fContext.fFloat2_Type) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400717 this->writef(" b->add32(%s.fX);\n",
718 HCodeGenerator::FieldName(name).c_str());
719 this->writef(" b->add32(%s.fY);\n",
720 HCodeGenerator::FieldName(name).c_str());
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400721 } else if (param->fType == *fContext.fFloat4_Type) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400722 this->writef(" b->add32(%s.x());\n",
723 HCodeGenerator::FieldName(name).c_str());
724 this->writef(" b->add32(%s.y());\n",
725 HCodeGenerator::FieldName(name).c_str());
726 this->writef(" b->add32(%s.width());\n",
727 HCodeGenerator::FieldName(name).c_str());
728 this->writef(" b->add32(%s.height());\n",
729 HCodeGenerator::FieldName(name).c_str());
730 } else {
Ethan Nicholasaae47c82017-11-10 15:34:03 -0500731 this->writef(" b->add32((int32_t) %s);\n",
Ethan Nicholas762466e2017-06-29 10:03:38 -0400732 HCodeGenerator::FieldName(name).c_str());
733 }
734 break;
735 case Layout::kIdentity_Key:
736 if (param->fType.kind() != Type::kMatrix_Kind) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700737 fErrors.error(param->fOffset,
Ethan Nicholas762466e2017-06-29 10:03:38 -0400738 "layout(key=identity) requires matrix type");
739 }
740 this->writef(" b->add32(%s.isIdentity() ? 1 : 0);\n",
741 HCodeGenerator::FieldName(name).c_str());
742 break;
743 case Layout::kNo_Key:
744 break;
745 }
746 }
747 this->write("}\n");
748}
749
750bool CPPCodeGenerator::generateCode() {
751 std::vector<const Variable*> uniforms;
752 for (const auto& p : fProgram.fElements) {
753 if (ProgramElement::kVar_Kind == p->fKind) {
754 const VarDeclarations* decls = (const VarDeclarations*) p.get();
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000755 for (const auto& raw : decls->fVars) {
756 VarDeclaration& decl = (VarDeclaration&) *raw;
757 if ((decl.fVar->fModifiers.fFlags & Modifiers::kUniform_Flag) &&
758 decl.fVar->fType.kind() != Type::kSampler_Kind) {
759 uniforms.push_back(decl.fVar);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400760 }
761 }
762 }
763 }
764 const char* baseName = fName.c_str();
765 const char* fullName = fFullName.c_str();
766 this->writef(kFragmentProcessorHeader, fullName);
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400767 this->writef("#include \"%s.h\"\n"
768 "#if SK_SUPPORT_GPU\n", fullName);
Ethan Nicholas9fb036f2017-07-05 16:19:09 -0400769 this->writeSection(CPP_SECTION);
Brian Osman1cb41712017-10-19 12:54:52 -0400770 this->writef("#include \"glsl/GrGLSLFragmentProcessor.h\"\n"
Ethan Nicholas762466e2017-06-29 10:03:38 -0400771 "#include \"glsl/GrGLSLFragmentShaderBuilder.h\"\n"
772 "#include \"glsl/GrGLSLProgramBuilder.h\"\n"
Ethan Nicholas2d5f9b32017-12-13 14:36:14 -0500773 "#include \"GrTexture.h\"\n"
Ethan Nicholas762466e2017-06-29 10:03:38 -0400774 "#include \"SkSLCPP.h\"\n"
775 "#include \"SkSLUtil.h\"\n"
776 "class GrGLSL%s : public GrGLSLFragmentProcessor {\n"
777 "public:\n"
778 " GrGLSL%s() {}\n",
Ethan Nicholas9fb036f2017-07-05 16:19:09 -0400779 baseName, baseName);
Ethan Nicholas762466e2017-06-29 10:03:38 -0400780 bool result = this->writeEmitCode(uniforms);
781 this->write("private:\n");
782 this->writeSetData(uniforms);
783 this->writePrivateVars();
784 for (const auto& u : uniforms) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400785 if (needs_uniform_var(*u) && !(u->fModifiers.fFlags & Modifiers::kIn_Flag)) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700786 this->writef(" UniformHandle %sVar;\n",
787 HCodeGenerator::FieldName(String(u->fName).c_str()).c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400788 }
789 }
Ethan Nicholas68990be2017-07-13 09:36:52 -0400790 for (const auto& param : fSectionAndParameterHelper.getParameters()) {
Ethan Nicholas762466e2017-06-29 10:03:38 -0400791 if (needs_uniform_var(*param)) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700792 this->writef(" UniformHandle %sVar;\n",
793 HCodeGenerator::FieldName(String(param->fName).c_str()).c_str());
Ethan Nicholas762466e2017-06-29 10:03:38 -0400794 }
795 }
Ethan Nicholas762466e2017-06-29 10:03:38 -0400796 this->writef("};\n"
797 "GrGLSLFragmentProcessor* %s::onCreateGLSLInstance() const {\n"
798 " return new GrGLSL%s();\n"
799 "}\n",
800 fullName, baseName);
801 this->writeGetKey();
802 this->writef("bool %s::onIsEqual(const GrFragmentProcessor& other) const {\n"
803 " const %s& that = other.cast<%s>();\n"
804 " (void) that;\n",
805 fullName, fullName, fullName);
Ethan Nicholas68990be2017-07-13 09:36:52 -0400806 for (const auto& param : fSectionAndParameterHelper.getParameters()) {
Ethan Nicholasc9472af2017-10-10 16:30:21 -0400807 if (param->fType == *fContext.fFragmentProcessor_Type) {
808 continue;
809 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -0700810 String nameString(param->fName);
811 const char* name = nameString.c_str();
Ethan Nicholas762466e2017-06-29 10:03:38 -0400812 this->writef(" if (%s != that.%s) return false;\n",
813 HCodeGenerator::FieldName(name).c_str(),
814 HCodeGenerator::FieldName(name).c_str());
815 }
816 this->write(" return true;\n"
817 "}\n");
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400818 this->writeClone();
Ethan Nicholas762466e2017-06-29 10:03:38 -0400819 this->writeTest();
Ethan Nicholas9fb036f2017-07-05 16:19:09 -0400820 this->writeSection(CPP_END_SECTION);
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400821 this->write("#endif\n");
Ethan Nicholas762466e2017-06-29 10:03:38 -0400822 result &= 0 == fErrors.errorCount();
823 return result;
824}
825
826} // namespace