1 // Copyright 2018 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/amberscript/parser.h"
16 
17 #include <algorithm>
18 #include <cassert>
19 #include <limits>
20 #include <map>
21 #include <string>
22 #include <utility>
23 #include <vector>
24 
25 #include "src/image.h"
26 #include "src/make_unique.h"
27 #include "src/sampler.h"
28 #include "src/shader_data.h"
29 #include "src/tokenizer.h"
30 #include "src/type_parser.h"
31 
32 namespace amber {
33 namespace amberscript {
34 namespace {
35 
IsComparator(const std::string & in)36 bool IsComparator(const std::string& in) {
37   return in == "EQ" || in == "NE" || in == "GT" || in == "LT" || in == "GE" ||
38          in == "LE";
39 }
40 
ToComparator(const std::string & in)41 ProbeSSBOCommand::Comparator ToComparator(const std::string& in) {
42   if (in == "EQ")
43     return ProbeSSBOCommand::Comparator::kEqual;
44   if (in == "NE")
45     return ProbeSSBOCommand::Comparator::kNotEqual;
46   if (in == "GT")
47     return ProbeSSBOCommand::Comparator::kGreater;
48   if (in == "LT")
49     return ProbeSSBOCommand::Comparator::kLess;
50   if (in == "GE")
51     return ProbeSSBOCommand::Comparator::kGreaterOrEqual;
52 
53   assert(in == "LE");
54   return ProbeSSBOCommand::Comparator::kLessOrEqual;
55 }
56 
ToType(const std::string & str_in)57 std::unique_ptr<type::Type> ToType(const std::string& str_in) {
58   std::string str = str_in;
59 
60   bool is_array = false;
61   if (str.length() > 2 && str[str.length() - 2] == '[' &&
62       str[str.length() - 1] == ']') {
63     is_array = true;
64     str = str.substr(0, str.length() - 2);
65   }
66 
67   TypeParser parser;
68   std::unique_ptr<type::Type> type;
69   if (str == "int8") {
70     type = parser.Parse("R8_SINT");
71   } else if (str == "int16") {
72     type = parser.Parse("R16_SINT");
73   } else if (str == "int32") {
74     type = parser.Parse("R32_SINT");
75   } else if (str == "int64") {
76     type = parser.Parse("R64_SINT");
77   } else if (str == "uint8") {
78     type = parser.Parse("R8_UINT");
79   } else if (str == "uint16") {
80     type = parser.Parse("R16_UINT");
81   } else if (str == "uint32") {
82     type = parser.Parse("R32_UINT");
83   } else if (str == "uint64") {
84     type = parser.Parse("R64_UINT");
85   } else if (str == "float16") {
86     type = parser.Parse("R16_SFLOAT");
87   } else if (str == "float") {
88     type = parser.Parse("R32_SFLOAT");
89   } else if (str == "double") {
90     type = parser.Parse("R64_SFLOAT");
91   } else if (str.length() > 7 && str.substr(0, 3) == "vec") {
92     if (str[4] != '<' || str[str.length() - 1] != '>')
93       return nullptr;
94 
95     int component_count = str[3] - '0';
96     if (component_count < 2 || component_count > 4)
97       return nullptr;
98 
99     type = ToType(str.substr(5, str.length() - 6));
100     if (!type)
101       return nullptr;
102 
103     if (!type->IsNumber() || type->IsArray() || type->IsVec() ||
104         type->IsMatrix()) {
105       return nullptr;
106     }
107 
108     type->SetRowCount(static_cast<uint32_t>(component_count));
109   } else if (str.length() > 9 && str.substr(0, 3) == "mat") {
110     if (str[4] != 'x' || str[6] != '<' || str[str.length() - 1] != '>')
111       return nullptr;
112 
113     int column_count = str[3] - '0';
114     if (column_count < 2 || column_count > 4)
115       return nullptr;
116 
117     int row_count = str[5] - '0';
118     if (row_count < 2 || row_count > 4)
119       return nullptr;
120 
121     type = ToType(str.substr(7, str.length() - 8));
122     if (!type)
123       return nullptr;
124     if (!type->IsNumber() || type->IsArray() || type->IsVec() ||
125         type->IsMatrix()) {
126       return nullptr;
127     }
128 
129     type->SetRowCount(static_cast<uint32_t>(row_count));
130     type->SetColumnCount(static_cast<uint32_t>(column_count));
131   }
132 
133   if (!type)
134     return nullptr;
135   if (is_array)
136     type->SetIsRuntimeArray();
137 
138   return type;
139 }
140 
StrToAddressMode(std::string str)141 AddressMode StrToAddressMode(std::string str) {
142   if (str == "repeat")
143     return AddressMode::kRepeat;
144   if (str == "mirrored_repeat")
145     return AddressMode::kMirroredRepeat;
146   if (str == "clamp_to_edge")
147     return AddressMode::kClampToEdge;
148   if (str == "clamp_to_border")
149     return AddressMode::kClampToBorder;
150   if (str == "mirror_clamp_to_edge")
151     return AddressMode::kMirrorClampToEdge;
152 
153   return AddressMode::kUnknown;
154 }
155 
StrToCompareOp(const std::string & str)156 CompareOp StrToCompareOp(const std::string& str) {
157   if (str == "never")
158     return CompareOp::kNever;
159   if (str == "less")
160     return CompareOp::kLess;
161   if (str == "equal")
162     return CompareOp::kEqual;
163   if (str == "less_or_equal")
164     return CompareOp::kLessOrEqual;
165   if (str == "greater")
166     return CompareOp::kGreater;
167   if (str == "not_equal")
168     return CompareOp::kNotEqual;
169   if (str == "greater_or_equal")
170     return CompareOp::kGreaterOrEqual;
171   if (str == "always")
172     return CompareOp::kAlways;
173 
174   return CompareOp::kUnknown;
175 }
176 
StrToStencilOp(const std::string & str)177 StencilOp StrToStencilOp(const std::string& str) {
178   if (str == "keep")
179     return StencilOp::kKeep;
180   if (str == "zero")
181     return StencilOp::kZero;
182   if (str == "replace")
183     return StencilOp::kReplace;
184   if (str == "increment_and_clamp")
185     return StencilOp::kIncrementAndClamp;
186   if (str == "decrement_and_clamp")
187     return StencilOp::kDecrementAndClamp;
188   if (str == "invert")
189     return StencilOp::kInvert;
190   if (str == "increment_and_wrap")
191     return StencilOp::kIncrementAndWrap;
192   if (str == "decrement_and_wrap")
193     return StencilOp::kDecrementAndWrap;
194 
195   return StencilOp::kUnknown;
196 }
197 
ParseBufferData(Buffer * buffer,Tokenizer * tokenizer,bool from_data_file)198 Result ParseBufferData(Buffer* buffer,
199                        Tokenizer* tokenizer,
200                        bool from_data_file) {
201   auto fmt = buffer->GetFormat();
202   const auto& segs = fmt->GetSegments();
203   size_t seg_idx = 0;
204   uint32_t value_count = 0;
205 
206   std::vector<Value> values;
207   for (auto token = tokenizer->NextToken();; token = tokenizer->NextToken()) {
208     if (token->IsEOL())
209       continue;
210     if (token->IsEOS()) {
211       if (from_data_file) {
212         break;
213       } else {
214         return Result("missing BUFFER END command");
215       }
216     }
217     if (token->IsIdentifier() && token->AsString() == "END")
218       break;
219     if (!token->IsInteger() && !token->IsDouble() && !token->IsHex())
220       return Result("invalid BUFFER data value: " + token->ToOriginalString());
221 
222     while (segs[seg_idx].IsPadding()) {
223       ++seg_idx;
224       if (seg_idx >= segs.size())
225         seg_idx = 0;
226     }
227 
228     Value v;
229     if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
230       token->ConvertToDouble();
231 
232       double val = token->IsHex() ? static_cast<double>(token->AsHex())
233                                   : token->AsDouble();
234       v.SetDoubleValue(val);
235       ++value_count;
236     } else {
237       if (token->IsDouble()) {
238         return Result("invalid BUFFER data value: " +
239                       token->ToOriginalString());
240       }
241 
242       uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
243       v.SetIntValue(val);
244       ++value_count;
245     }
246     ++seg_idx;
247     if (seg_idx >= segs.size())
248       seg_idx = 0;
249 
250     values.emplace_back(v);
251   }
252   // Write final padding bytes
253   while (segs[seg_idx].IsPadding()) {
254     ++seg_idx;
255     if (seg_idx >= segs.size())
256       break;
257   }
258 
259   buffer->SetValueCount(value_count);
260   Result r = buffer->SetData(std::move(values));
261   if (!r.IsSuccess())
262     return r;
263 
264   return {};
265 }
266 
267 constexpr uint32_t valid_samples[] = {1, 2, 4, 8, 16, 32, 64};
268 
IsValidSampleCount(uint32_t samples)269 bool IsValidSampleCount(uint32_t samples) {
270   return (std::find(std::begin(valid_samples), std::end(valid_samples),
271                     samples) != std::end(valid_samples));
272 }
273 
274 }  // namespace
275 
Parser()276 Parser::Parser() : amber::Parser(nullptr) {}
Parser(Delegate * delegate)277 Parser::Parser(Delegate* delegate) : amber::Parser(delegate) {}
278 
279 Parser::~Parser() = default;
280 
make_error(const std::string & err)281 std::string Parser::make_error(const std::string& err) {
282   return std::to_string(tokenizer_->GetCurrentLine()) + ": " + err;
283 }
284 
Parse(const std::string & data)285 Result Parser::Parse(const std::string& data) {
286   tokenizer_ = MakeUnique<Tokenizer>(data);
287 
288   for (auto token = tokenizer_->NextToken(); !token->IsEOS();
289        token = tokenizer_->NextToken()) {
290     if (token->IsEOL())
291       continue;
292     if (!token->IsIdentifier())
293       return Result(make_error("expected identifier"));
294 
295     Result r;
296     std::string tok = token->AsString();
297     if (IsRepeatable(tok)) {
298       r = ParseRepeatableCommand(tok);
299     } else if (tok == "BUFFER") {
300       r = ParseBuffer();
301     } else if (tok == "DERIVE_PIPELINE") {
302       r = ParseDerivePipelineBlock();
303     } else if (tok == "DEVICE_FEATURE") {
304       r = ParseDeviceFeature();
305     } else if (tok == "DEVICE_EXTENSION") {
306       r = ParseDeviceExtension();
307     } else if (tok == "IMAGE") {
308       r = ParseImage();
309     } else if (tok == "INSTANCE_EXTENSION") {
310       r = ParseInstanceExtension();
311     } else if (tok == "PIPELINE") {
312       r = ParsePipelineBlock();
313     } else if (tok == "REPEAT") {
314       r = ParseRepeat();
315     } else if (tok == "SET") {
316       r = ParseSet();
317     } else if (tok == "SHADER") {
318       r = ParseShaderBlock();
319     } else if (tok == "STRUCT") {
320       r = ParseStruct();
321     } else if (tok == "SAMPLER") {
322       r = ParseSampler();
323     } else if (tok == "VIRTUAL_FILE") {
324       r = ParseVirtualFile();
325     } else {
326       r = Result("unknown token: " + tok);
327     }
328     if (!r.IsSuccess())
329       return Result(make_error(r.Error()));
330   }
331   script_->SetCommands(std::move(command_list_));
332 
333   // Generate any needed color attachments. This is done before
334   // validating in case one of the pipelines specifies the framebuffer size
335   // it needs to be verified against all other pipelines.
336   for (const auto& pipeline : script_->GetPipelines()) {
337     // Add a color attachment if needed
338     if (pipeline->GetColorAttachments().empty()) {
339       auto* buf = script_->GetBuffer(Pipeline::kGeneratedColorBuffer);
340       if (!buf) {
341         auto color_buf = pipeline->GenerateDefaultColorAttachmentBuffer();
342         buf = color_buf.get();
343 
344         Result r = script_->AddBuffer(std::move(color_buf));
345         if (!r.IsSuccess())
346           return r;
347       }
348       Result r = pipeline->AddColorAttachment(buf, 0, 0);
349       if (!r.IsSuccess())
350         return r;
351     }
352   }
353 
354   // Validate all the pipelines at the end. This allows us to verify the
355   // framebuffer sizes are consistent over pipelines.
356   for (const auto& pipeline : script_->GetPipelines()) {
357     Result r = pipeline->Validate();
358     if (!r.IsSuccess())
359       return r;
360   }
361 
362   return {};
363 }
364 
IsRepeatable(const std::string & name) const365 bool Parser::IsRepeatable(const std::string& name) const {
366   return name == "CLEAR" || name == "CLEAR_COLOR" || name == "CLEAR_DEPTH" ||
367          name == "CLEAR_STENCIL" || name == "COPY" || name == "EXPECT" ||
368          name == "RUN" || name == "DEBUG";
369 }
370 
371 // The given |name| must be one of the repeatable commands or this method
372 // returns an error result.
ParseRepeatableCommand(const std::string & name)373 Result Parser::ParseRepeatableCommand(const std::string& name) {
374   if (name == "CLEAR")
375     return ParseClear();
376   if (name == "CLEAR_COLOR")
377     return ParseClearColor();
378   if (name == "CLEAR_DEPTH")
379     return ParseClearDepth();
380   if (name == "CLEAR_STENCIL")
381     return ParseClearStencil();
382   if (name == "COPY")
383     return ParseCopy();
384   if (name == "EXPECT")
385     return ParseExpect();
386   if (name == "RUN")
387     return ParseRun();
388   if (name == "DEBUG")
389     return ParseDebug();
390 
391   return Result("invalid repeatable command: " + name);
392 }
393 
ToShaderType(const std::string & str,ShaderType * type)394 Result Parser::ToShaderType(const std::string& str, ShaderType* type) {
395   assert(type);
396 
397   if (str == "vertex")
398     *type = kShaderTypeVertex;
399   else if (str == "fragment")
400     *type = kShaderTypeFragment;
401   else if (str == "geometry")
402     *type = kShaderTypeGeometry;
403   else if (str == "tessellation_evaluation")
404     *type = kShaderTypeTessellationEvaluation;
405   else if (str == "tessellation_control")
406     *type = kShaderTypeTessellationControl;
407   else if (str == "compute")
408     *type = kShaderTypeCompute;
409   else if (str == "multi")
410     *type = kShaderTypeMulti;
411   else
412     return Result("unknown shader type: " + str);
413   return {};
414 }
415 
ToShaderFormat(const std::string & str,ShaderFormat * fmt)416 Result Parser::ToShaderFormat(const std::string& str, ShaderFormat* fmt) {
417   assert(fmt);
418 
419   if (str == "GLSL")
420     *fmt = kShaderFormatGlsl;
421   else if (str == "HLSL")
422     *fmt = kShaderFormatHlsl;
423   else if (str == "SPIRV-ASM")
424     *fmt = kShaderFormatSpirvAsm;
425   else if (str == "SPIRV-HEX")
426     *fmt = kShaderFormatSpirvHex;
427   else if (str == "OPENCL-C")
428     *fmt = kShaderFormatOpenCLC;
429   else
430     return Result("unknown shader format: " + str);
431   return {};
432 }
433 
ToPipelineType(const std::string & str,PipelineType * type)434 Result Parser::ToPipelineType(const std::string& str, PipelineType* type) {
435   assert(type);
436 
437   if (str == "compute")
438     *type = PipelineType::kCompute;
439   else if (str == "graphics")
440     *type = PipelineType::kGraphics;
441   else
442     return Result("unknown pipeline type: " + str);
443   return {};
444 }
445 
ValidateEndOfStatement(const std::string & name)446 Result Parser::ValidateEndOfStatement(const std::string& name) {
447   auto token = tokenizer_->NextToken();
448   if (token->IsEOL() || token->IsEOS())
449     return {};
450   return Result("extra parameters after " + name + ": " +
451                 token->ToOriginalString());
452 }
453 
ParseShaderBlock()454 Result Parser::ParseShaderBlock() {
455   auto token = tokenizer_->NextToken();
456   if (!token->IsIdentifier())
457     return Result("invalid token when looking for shader type");
458 
459   ShaderType type = kShaderTypeVertex;
460   Result r = ToShaderType(token->AsString(), &type);
461   if (!r.IsSuccess())
462     return r;
463 
464   auto shader = MakeUnique<Shader>(type);
465 
466   token = tokenizer_->NextToken();
467   if (!token->IsIdentifier())
468     return Result("invalid token when looking for shader name");
469 
470   shader->SetName(token->AsString());
471 
472   token = tokenizer_->NextToken();
473   if (!token->IsIdentifier())
474     return Result("invalid token when looking for shader format");
475 
476   std::string fmt = token->AsString();
477   if (fmt == "PASSTHROUGH") {
478     if (type != kShaderTypeVertex) {
479       return Result(
480           "invalid shader type for PASSTHROUGH. Only vertex "
481           "PASSTHROUGH allowed");
482     }
483     shader->SetFormat(kShaderFormatSpirvAsm);
484     shader->SetData(kPassThroughShader);
485     shader->SetTargetEnv("spv1.0");
486 
487     r = script_->AddShader(std::move(shader));
488     if (!r.IsSuccess())
489       return r;
490 
491     return ValidateEndOfStatement("SHADER PASSTHROUGH");
492   }
493 
494   ShaderFormat format = kShaderFormatGlsl;
495   r = ToShaderFormat(fmt, &format);
496   if (!r.IsSuccess())
497     return r;
498 
499   shader->SetFormat(format);
500 
501   token = tokenizer_->PeekNextToken();
502   if (token->IsIdentifier() && token->AsString() == "TARGET_ENV") {
503     tokenizer_->NextToken();
504     token = tokenizer_->NextToken();
505     if (!token->IsIdentifier() && !token->IsString())
506       return Result("expected target environment after TARGET_ENV");
507     shader->SetTargetEnv(token->AsString());
508   }
509 
510   token = tokenizer_->PeekNextToken();
511   if (token->IsIdentifier() && token->AsString() == "VIRTUAL_FILE") {
512     tokenizer_->NextToken();  // Skip VIRTUAL_FILE
513 
514     token = tokenizer_->NextToken();
515     if (!token->IsIdentifier() && !token->IsString())
516       return Result("expected virtual file path after VIRTUAL_FILE");
517 
518     auto path = token->AsString();
519 
520     std::string data;
521     r = script_->GetVirtualFile(path, &data);
522     if (!r.IsSuccess())
523       return r;
524 
525     shader->SetData(data);
526     shader->SetFilePath(path);
527 
528     r = script_->AddShader(std::move(shader));
529     if (!r.IsSuccess())
530       return r;
531 
532     return ValidateEndOfStatement("SHADER command");
533   }
534 
535   r = ValidateEndOfStatement("SHADER command");
536   if (!r.IsSuccess())
537     return r;
538 
539   std::string data = tokenizer_->ExtractToNext("END");
540   if (data.empty())
541     return Result("SHADER must not be empty");
542 
543   shader->SetData(data);
544 
545   auto path = "embedded-shaders/" + shader->GetName();
546   script_->AddVirtualFile(path, data);
547   shader->SetFilePath(path);
548 
549   token = tokenizer_->NextToken();
550   if (!token->IsIdentifier() || token->AsString() != "END")
551     return Result("SHADER missing END command");
552 
553   r = script_->AddShader(std::move(shader));
554   if (!r.IsSuccess())
555     return r;
556 
557   return ValidateEndOfStatement("END");
558 }
559 
ParsePipelineBlock()560 Result Parser::ParsePipelineBlock() {
561   auto token = tokenizer_->NextToken();
562   if (!token->IsIdentifier())
563     return Result("invalid token when looking for pipeline type");
564 
565   PipelineType type = PipelineType::kCompute;
566   Result r = ToPipelineType(token->AsString(), &type);
567   if (!r.IsSuccess())
568     return r;
569 
570   auto pipeline = MakeUnique<Pipeline>(type);
571 
572   token = tokenizer_->NextToken();
573   if (!token->IsIdentifier())
574     return Result("invalid token when looking for pipeline name");
575 
576   pipeline->SetName(token->AsString());
577 
578   r = ValidateEndOfStatement("PIPELINE command");
579   if (!r.IsSuccess())
580     return r;
581 
582   return ParsePipelineBody("PIPELINE", std::move(pipeline));
583 }
584 
ParsePipelineBody(const std::string & cmd_name,std::unique_ptr<Pipeline> pipeline)585 Result Parser::ParsePipelineBody(const std::string& cmd_name,
586                                  std::unique_ptr<Pipeline> pipeline) {
587   std::unique_ptr<Token> token;
588   for (token = tokenizer_->NextToken(); !token->IsEOS();
589        token = tokenizer_->NextToken()) {
590     if (token->IsEOL())
591       continue;
592     if (!token->IsIdentifier())
593       return Result("expected identifier");
594 
595     Result r;
596     std::string tok = token->AsString();
597     if (tok == "END") {
598       break;
599     } else if (tok == "ATTACH") {
600       r = ParsePipelineAttach(pipeline.get());
601     } else if (tok == "SHADER_OPTIMIZATION") {
602       r = ParsePipelineShaderOptimizations(pipeline.get());
603     } else if (tok == "FRAMEBUFFER_SIZE") {
604       r = ParsePipelineFramebufferSize(pipeline.get());
605     } else if (tok == "BIND") {
606       r = ParsePipelineBind(pipeline.get());
607     } else if (tok == "VERTEX_DATA") {
608       r = ParsePipelineVertexData(pipeline.get());
609     } else if (tok == "INDEX_DATA") {
610       r = ParsePipelineIndexData(pipeline.get());
611     } else if (tok == "SET") {
612       r = ParsePipelineSet(pipeline.get());
613     } else if (tok == "COMPILE_OPTIONS") {
614       r = ParsePipelineShaderCompileOptions(pipeline.get());
615     } else if (tok == "POLYGON_MODE") {
616       r = ParsePipelinePolygonMode(pipeline.get());
617     } else if (tok == "DEPTH") {
618       r = ParsePipelineDepth(pipeline.get());
619     } else if (tok == "STENCIL") {
620       r = ParsePipelineStencil(pipeline.get());
621     } else if (tok == "SUBGROUP") {
622       r = ParsePipelineSubgroup(pipeline.get());
623     } else {
624       r = Result("unknown token in pipeline block: " + tok);
625     }
626     if (!r.IsSuccess())
627       return r;
628   }
629 
630   if (!token->IsIdentifier() || token->AsString() != "END")
631     return Result(cmd_name + " missing END command");
632 
633   Result r = script_->AddPipeline(std::move(pipeline));
634   if (!r.IsSuccess())
635     return r;
636 
637   return ValidateEndOfStatement("END");
638 }
639 
ParsePipelineAttach(Pipeline * pipeline)640 Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
641   auto token = tokenizer_->NextToken();
642   if (!token->IsIdentifier())
643     return Result("invalid token in ATTACH command");
644 
645   auto* shader = script_->GetShader(token->AsString());
646   if (!shader)
647     return Result("unknown shader in ATTACH command");
648 
649   token = tokenizer_->NextToken();
650   if (token->IsEOL() || token->IsEOS()) {
651     if (shader->GetType() == kShaderTypeMulti)
652       return Result("multi shader ATTACH requires TYPE");
653 
654     Result r = pipeline->AddShader(shader, shader->GetType());
655     if (!r.IsSuccess())
656       return r;
657     return {};
658   }
659   if (!token->IsIdentifier())
660     return Result("invalid token after ATTACH");
661 
662   bool set_shader_type = false;
663   ShaderType shader_type = shader->GetType();
664   auto type = token->AsString();
665   if (type == "TYPE") {
666     token = tokenizer_->NextToken();
667     if (!token->IsIdentifier())
668       return Result("invalid type in ATTACH");
669 
670     Result r = ToShaderType(token->AsString(), &shader_type);
671     if (!r.IsSuccess())
672       return r;
673 
674     set_shader_type = true;
675 
676     token = tokenizer_->NextToken();
677     if (!token->IsIdentifier())
678       return Result("ATTACH TYPE requires an ENTRY_POINT");
679 
680     type = token->AsString();
681   }
682   if (set_shader_type && type != "ENTRY_POINT")
683     return Result("unknown ATTACH parameter: " + type);
684 
685   if (shader->GetType() == ShaderType::kShaderTypeMulti && !set_shader_type)
686     return Result("ATTACH missing TYPE for multi shader");
687 
688   Result r = pipeline->AddShader(shader, shader_type);
689   if (!r.IsSuccess())
690     return r;
691 
692   if (type == "ENTRY_POINT") {
693     token = tokenizer_->NextToken();
694     if (!token->IsIdentifier())
695       return Result("missing shader name in ATTACH ENTRY_POINT command");
696 
697     r = pipeline->SetShaderEntryPoint(shader, token->AsString());
698     if (!r.IsSuccess())
699       return r;
700 
701     token = tokenizer_->NextToken();
702   }
703 
704   while (true) {
705     if (token->IsIdentifier() && token->AsString() == "SPECIALIZE") {
706       r = ParseShaderSpecialization(pipeline);
707       if (!r.IsSuccess())
708         return r;
709 
710       token = tokenizer_->NextToken();
711     } else {
712       if (token->IsEOL() || token->IsEOS())
713         return {};
714       if (token->IsIdentifier())
715         return Result("unknown ATTACH parameter: " + token->AsString());
716       return Result("extra parameters after ATTACH command: " +
717                     token->ToOriginalString());
718     }
719   }
720 }
721 
ParseShaderSpecialization(Pipeline * pipeline)722 Result Parser::ParseShaderSpecialization(Pipeline* pipeline) {
723   auto token = tokenizer_->NextToken();
724   if (!token->IsInteger())
725     return Result("specialization ID must be an integer");
726 
727   auto spec_id = token->AsUint32();
728 
729   token = tokenizer_->NextToken();
730   if (!token->IsIdentifier() || token->AsString() != "AS")
731     return Result("expected AS as next token");
732 
733   token = tokenizer_->NextToken();
734   if (!token->IsIdentifier())
735     return Result("expected data type in SPECIALIZE subcommand");
736 
737   auto type = ToType(token->AsString());
738   if (!type)
739     return Result("invalid data type '" + token->AsString() + "' provided");
740   if (!type->IsNumber())
741     return Result("only numeric types are accepted for specialization values");
742 
743   auto num = type->AsNumber();
744 
745   token = tokenizer_->NextToken();
746   uint32_t value = 0;
747   if (type::Type::IsUint32(num->GetFormatMode(), num->NumBits()) ||
748       type::Type::IsInt32(num->GetFormatMode(), num->NumBits())) {
749     value = token->AsUint32();
750   } else if (type::Type::IsFloat32(num->GetFormatMode(), num->NumBits())) {
751     Result r = token->ConvertToDouble();
752     if (!r.IsSuccess())
753       return Result("value is not a floating point value");
754 
755     union {
756       uint32_t u;
757       float f;
758     } u;
759     u.f = token->AsFloat();
760     value = u.u;
761   } else {
762     return Result(
763         "only 32-bit types are currently accepted for specialization values");
764   }
765 
766   auto& shader = pipeline->GetShaders()[pipeline->GetShaders().size() - 1];
767   shader.AddSpecialization(spec_id, value);
768   return {};
769 }
770 
ParsePipelineShaderOptimizations(Pipeline * pipeline)771 Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) {
772   auto token = tokenizer_->NextToken();
773   if (!token->IsIdentifier())
774     return Result("missing shader name in SHADER_OPTIMIZATION command");
775 
776   auto* shader = script_->GetShader(token->AsString());
777   if (!shader)
778     return Result("unknown shader in SHADER_OPTIMIZATION command");
779 
780   token = tokenizer_->NextToken();
781   if (!token->IsEOL())
782     return Result("extra parameters after SHADER_OPTIMIZATION command: " +
783                   token->ToOriginalString());
784 
785   std::vector<std::string> optimizations;
786   while (true) {
787     token = tokenizer_->NextToken();
788     if (token->IsEOL())
789       continue;
790     if (token->IsEOS())
791       return Result("SHADER_OPTIMIZATION missing END command");
792     if (!token->IsIdentifier())
793       return Result("SHADER_OPTIMIZATION options must be identifiers");
794     if (token->AsString() == "END")
795       break;
796 
797     optimizations.push_back(token->AsString());
798   }
799 
800   Result r = pipeline->SetShaderOptimizations(shader, optimizations);
801   if (!r.IsSuccess())
802     return r;
803 
804   return ValidateEndOfStatement("SHADER_OPTIMIZATION command");
805 }
806 
ParsePipelineShaderCompileOptions(Pipeline * pipeline)807 Result Parser::ParsePipelineShaderCompileOptions(Pipeline* pipeline) {
808   auto token = tokenizer_->NextToken();
809   if (!token->IsIdentifier())
810     return Result("missing shader name in COMPILE_OPTIONS command");
811 
812   auto* shader = script_->GetShader(token->AsString());
813   if (!shader)
814     return Result("unknown shader in COMPILE_OPTIONS command");
815 
816   if (shader->GetFormat() != kShaderFormatOpenCLC) {
817     return Result("COMPILE_OPTIONS currently only supports OPENCL-C shaders");
818   }
819 
820   token = tokenizer_->NextToken();
821   if (!token->IsEOL())
822     return Result("extra parameters after COMPILE_OPTIONS command: " +
823                   token->ToOriginalString());
824 
825   std::vector<std::string> options;
826   while (true) {
827     token = tokenizer_->NextToken();
828     if (token->IsEOL())
829       continue;
830     if (token->IsEOS())
831       return Result("COMPILE_OPTIONS missing END command");
832     if (token->AsString() == "END")
833       break;
834 
835     options.push_back(token->AsString());
836   }
837 
838   Result r = pipeline->SetShaderCompileOptions(shader, options);
839   if (!r.IsSuccess())
840     return r;
841 
842   return ValidateEndOfStatement("COMPILE_OPTIONS command");
843 }
844 
ParsePipelineSubgroup(Pipeline * pipeline)845 Result Parser::ParsePipelineSubgroup(Pipeline* pipeline) {
846   auto token = tokenizer_->NextToken();
847   if (!token->IsIdentifier())
848     return Result("missing shader name in SUBGROUP command");
849 
850   auto* shader = script_->GetShader(token->AsString());
851   if (!shader)
852     return Result("unknown shader in SUBGROUP command");
853 
854   while (true) {
855     token = tokenizer_->NextToken();
856     if (token->IsEOL())
857       continue;
858     if (token->IsEOS())
859       return Result("SUBGROUP missing END command");
860     if (!token->IsIdentifier())
861       return Result("SUBGROUP options must be identifiers");
862     if (token->AsString() == "END")
863       break;
864 
865     if (token->AsString() == "FULLY_POPULATED") {
866       if (!script_->IsRequiredFeature(
867               "SubgroupSizeControl.computeFullSubgroups"))
868         return Result(
869             "missing DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups");
870       token = tokenizer_->NextToken();
871       if (token->IsEOL() || token->IsEOS())
872         return Result("missing value for FULLY_POPULATED command");
873       bool isOn = false;
874       if (token->AsString() == "on") {
875         isOn = true;
876       } else if (token->AsString() == "off") {
877         isOn = false;
878       } else {
879         return Result("invalid value for FULLY_POPULATED command");
880       }
881       Result r = pipeline->SetShaderRequireFullSubgroups(shader, isOn);
882       if (!r.IsSuccess())
883         return r;
884 
885     } else if (token->AsString() == "VARYING_SIZE") {
886       if (!script_->IsRequiredFeature(
887               "SubgroupSizeControl.subgroupSizeControl"))
888         return Result(
889             "missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl");
890       token = tokenizer_->NextToken();
891       if (token->IsEOL() || token->IsEOS())
892         return Result("missing value for VARYING_SIZE command");
893       bool isOn = false;
894       if (token->AsString() == "on") {
895         isOn = true;
896       } else if (token->AsString() == "off") {
897         isOn = false;
898       } else {
899         return Result("invalid value for VARYING_SIZE command");
900       }
901       Result r = pipeline->SetShaderVaryingSubgroupSize(shader, isOn);
902       if (!r.IsSuccess())
903         return r;
904     } else if (token->AsString() == "REQUIRED_SIZE") {
905       if (!script_->IsRequiredFeature(
906               "SubgroupSizeControl.subgroupSizeControl"))
907         return Result(
908             "missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl");
909       token = tokenizer_->NextToken();
910       if (token->IsEOL() || token->IsEOS())
911         return Result("missing size for REQUIRED_SIZE command");
912       Result r;
913       if (token->IsInteger()) {
914         r = pipeline->SetShaderRequiredSubgroupSize(shader, token->AsUint32());
915       } else if (token->AsString() == "MIN") {
916         r = pipeline->SetShaderRequiredSubgroupSizeToMinimum(shader);
917       } else if (token->AsString() == "MAX") {
918         r = pipeline->SetShaderRequiredSubgroupSizeToMaximum(shader);
919       } else {
920         return Result("invalid size for REQUIRED_SIZE command");
921       }
922       if (!r.IsSuccess())
923         return r;
924     } else {
925       return Result("SUBGROUP invalid value for SUBGROUP " + token->AsString());
926     }
927   }
928 
929   return ValidateEndOfStatement("SUBGROUP command");
930 }
931 
ParsePipelineFramebufferSize(Pipeline * pipeline)932 Result Parser::ParsePipelineFramebufferSize(Pipeline* pipeline) {
933   auto token = tokenizer_->NextToken();
934   if (token->IsEOL() || token->IsEOS())
935     return Result("missing size for FRAMEBUFFER_SIZE command");
936   if (!token->IsInteger())
937     return Result("invalid width for FRAMEBUFFER_SIZE command");
938 
939   pipeline->SetFramebufferWidth(token->AsUint32());
940 
941   token = tokenizer_->NextToken();
942   if (token->IsEOL() || token->IsEOS())
943     return Result("missing height for FRAMEBUFFER_SIZE command");
944   if (!token->IsInteger())
945     return Result("invalid height for FRAMEBUFFER_SIZE command");
946 
947   pipeline->SetFramebufferHeight(token->AsUint32());
948 
949   return ValidateEndOfStatement("FRAMEBUFFER_SIZE command");
950 }
951 
ToBufferType(const std::string & name,BufferType * type)952 Result Parser::ToBufferType(const std::string& name, BufferType* type) {
953   assert(type);
954   if (name == "color")
955     *type = BufferType::kColor;
956   else if (name == "depth_stencil")
957     *type = BufferType::kDepthStencil;
958   else if (name == "push_constant")
959     *type = BufferType::kPushConstant;
960   else if (name == "uniform")
961     *type = BufferType::kUniform;
962   else if (name == "uniform_dynamic")
963     *type = BufferType::kUniformDynamic;
964   else if (name == "storage")
965     *type = BufferType::kStorage;
966   else if (name == "storage_dynamic")
967     *type = BufferType::kStorageDynamic;
968   else if (name == "storage_image")
969     *type = BufferType::kStorageImage;
970   else if (name == "sampled_image")
971     *type = BufferType::kSampledImage;
972   else if (name == "combined_image_sampler")
973     *type = BufferType::kCombinedImageSampler;
974   else if (name == "uniform_texel_buffer")
975     *type = BufferType::kUniformTexelBuffer;
976   else if (name == "storage_texel_buffer")
977     *type = BufferType::kStorageTexelBuffer;
978   else
979     return Result("unknown buffer_type: " + name);
980 
981   return {};
982 }
983 
ParsePipelineBind(Pipeline * pipeline)984 Result Parser::ParsePipelineBind(Pipeline* pipeline) {
985   auto token = tokenizer_->NextToken();
986 
987   if (!token->IsIdentifier()) {
988     return Result(
989         "missing BUFFER, BUFFER_ARRAY, SAMPLER, or SAMPLER_ARRAY in BIND "
990         "command");
991   }
992 
993   auto object_type = token->AsString();
994 
995   if (object_type == "BUFFER" || object_type == "BUFFER_ARRAY") {
996     bool is_buffer_array = object_type == "BUFFER_ARRAY";
997     token = tokenizer_->NextToken();
998     if (!token->IsIdentifier())
999       return Result("missing buffer name in BIND command");
1000 
1001     auto* buffer = script_->GetBuffer(token->AsString());
1002     if (!buffer)
1003       return Result("unknown buffer: " + token->AsString());
1004     std::vector<Buffer*> buffers = {buffer};
1005 
1006     if (is_buffer_array) {
1007       // Check for additional buffer names
1008       token = tokenizer_->PeekNextToken();
1009       while (token->IsIdentifier() && token->AsString() != "AS" &&
1010              token->AsString() != "KERNEL" &&
1011              token->AsString() != "DESCRIPTOR_SET") {
1012         tokenizer_->NextToken();
1013         buffer = script_->GetBuffer(token->AsString());
1014         if (!buffer)
1015           return Result("unknown buffer: " + token->AsString());
1016         buffers.push_back(buffer);
1017         token = tokenizer_->PeekNextToken();
1018       }
1019 
1020       if (buffers.size() < 2)
1021         return Result("expecting multiple buffer names for BUFFER_ARRAY");
1022     }
1023 
1024     BufferType buffer_type = BufferType::kUnknown;
1025     token = tokenizer_->NextToken();
1026     if (token->IsIdentifier() && token->AsString() == "AS") {
1027       token = tokenizer_->NextToken();
1028       if (!token->IsIdentifier())
1029         return Result("invalid token for BUFFER type");
1030 
1031       Result r = ToBufferType(token->AsString(), &buffer_type);
1032       if (!r.IsSuccess())
1033         return r;
1034 
1035       if (buffer_type == BufferType::kColor) {
1036         token = tokenizer_->NextToken();
1037         if (!token->IsIdentifier() || token->AsString() != "LOCATION")
1038           return Result("BIND missing LOCATION");
1039 
1040         token = tokenizer_->NextToken();
1041         if (!token->IsInteger())
1042           return Result("invalid value for BIND LOCATION");
1043         auto location = token->AsUint32();
1044 
1045         uint32_t base_mip_level = 0;
1046         token = tokenizer_->PeekNextToken();
1047         if (token->IsIdentifier() && token->AsString() == "BASE_MIP_LEVEL") {
1048           tokenizer_->NextToken();
1049           token = tokenizer_->NextToken();
1050 
1051           if (!token->IsInteger())
1052             return Result("invalid value for BASE_MIP_LEVEL");
1053 
1054           base_mip_level = token->AsUint32();
1055 
1056           if (base_mip_level >= buffer->GetMipLevels())
1057             return Result(
1058                 "base mip level (now " + token->AsString() +
1059                 ") needs to be larger than the number of buffer mip maps (" +
1060                 std::to_string(buffer->GetMipLevels()) + ")");
1061         }
1062 
1063         r = pipeline->AddColorAttachment(buffer, location, base_mip_level);
1064         if (!r.IsSuccess())
1065           return r;
1066 
1067       } else if (buffer_type == BufferType::kDepthStencil) {
1068         r = pipeline->SetDepthStencilBuffer(buffer);
1069         if (!r.IsSuccess())
1070           return r;
1071 
1072       } else if (buffer_type == BufferType::kPushConstant) {
1073         r = pipeline->SetPushConstantBuffer(buffer);
1074         if (!r.IsSuccess())
1075           return r;
1076 
1077       } else if (buffer_type == BufferType::kCombinedImageSampler) {
1078         token = tokenizer_->NextToken();
1079         if (!token->IsIdentifier() || token->AsString() != "SAMPLER")
1080           return Result("expecting SAMPLER for combined image sampler");
1081 
1082         token = tokenizer_->NextToken();
1083         if (!token->IsIdentifier())
1084           return Result("missing sampler name in BIND command");
1085 
1086         auto* sampler = script_->GetSampler(token->AsString());
1087         if (!sampler)
1088           return Result("unknown sampler: " + token->AsString());
1089 
1090         for (auto& buf : buffers)
1091           buf->SetSampler(sampler);
1092       }
1093     }
1094 
1095     // The OpenCL bindings can be typeless which allows for the kUnknown
1096     // buffer type.
1097     if (buffer_type == BufferType::kUnknown ||
1098         buffer_type == BufferType::kStorage ||
1099         buffer_type == BufferType::kUniform ||
1100         buffer_type == BufferType::kStorageDynamic ||
1101         buffer_type == BufferType::kUniformDynamic ||
1102         buffer_type == BufferType::kStorageImage ||
1103         buffer_type == BufferType::kSampledImage ||
1104         buffer_type == BufferType::kCombinedImageSampler ||
1105         buffer_type == BufferType::kUniformTexelBuffer ||
1106         buffer_type == BufferType::kStorageTexelBuffer) {
1107       // If the buffer type is known, then we proccessed the AS block above
1108       // and have to advance to the next token. Otherwise, we're already on
1109       // the next token and don't want to advance.
1110       if (buffer_type != BufferType::kUnknown)
1111         token = tokenizer_->NextToken();
1112 
1113       // DESCRIPTOR_SET requires a buffer type to have been specified.
1114       if (token->IsIdentifier() && token->AsString() == "DESCRIPTOR_SET") {
1115         token = tokenizer_->NextToken();
1116         if (!token->IsInteger())
1117           return Result("invalid value for DESCRIPTOR_SET in BIND command");
1118         uint32_t descriptor_set = token->AsUint32();
1119 
1120         token = tokenizer_->NextToken();
1121         if (!token->IsIdentifier() || token->AsString() != "BINDING")
1122           return Result("missing BINDING for BIND command");
1123 
1124         token = tokenizer_->NextToken();
1125         if (!token->IsInteger())
1126           return Result("invalid value for BINDING in BIND command");
1127 
1128         auto binding = token->AsUint32();
1129         uint32_t base_mip_level = 0;
1130 
1131         if (buffer_type == BufferType::kStorageImage ||
1132             buffer_type == BufferType::kSampledImage ||
1133             buffer_type == BufferType::kCombinedImageSampler) {
1134           token = tokenizer_->PeekNextToken();
1135           if (token->IsIdentifier() && token->AsString() == "BASE_MIP_LEVEL") {
1136             tokenizer_->NextToken();
1137             token = tokenizer_->NextToken();
1138 
1139             if (!token->IsInteger())
1140               return Result("invalid value for BASE_MIP_LEVEL");
1141 
1142             base_mip_level = token->AsUint32();
1143 
1144             if (base_mip_level >= buffer->GetMipLevels())
1145               return Result("base mip level (now " + token->AsString() +
1146                             ") needs to be larger than the number of buffer "
1147                             "mip maps (" +
1148                             std::to_string(buffer->GetMipLevels()) + ")");
1149           }
1150         }
1151 
1152         std::vector<uint32_t> dynamic_offsets(buffers.size(), 0);
1153         if (buffer_type == BufferType::kUniformDynamic ||
1154             buffer_type == BufferType::kStorageDynamic) {
1155           token = tokenizer_->NextToken();
1156           if (!token->IsIdentifier() || token->AsString() != "OFFSET")
1157             return Result("expecting an OFFSET for dynamic buffer type");
1158 
1159           for (size_t i = 0; i < buffers.size(); i++) {
1160             token = tokenizer_->NextToken();
1161 
1162             if (!token->IsInteger()) {
1163               if (i > 0) {
1164                 return Result(
1165                     "expecting an OFFSET value for each buffer in the array");
1166               } else {
1167                 return Result("expecting an integer value for OFFSET");
1168               }
1169             }
1170 
1171             dynamic_offsets[i] = token->AsUint32();
1172           }
1173         }
1174 
1175         pipeline->ClearBuffers(descriptor_set, binding);
1176         for (size_t i = 0; i < buffers.size(); i++) {
1177           pipeline->AddBuffer(buffers[i], buffer_type, descriptor_set, binding,
1178                               base_mip_level, dynamic_offsets[i]);
1179         }
1180       } else if (token->IsIdentifier() && token->AsString() == "KERNEL") {
1181         token = tokenizer_->NextToken();
1182         if (!token->IsIdentifier())
1183           return Result("missing kernel arg identifier");
1184 
1185         if (token->AsString() == "ARG_NAME") {
1186           token = tokenizer_->NextToken();
1187           if (!token->IsIdentifier())
1188             return Result("expected argument identifier");
1189 
1190           pipeline->AddBuffer(buffer, buffer_type, token->AsString());
1191         } else if (token->AsString() == "ARG_NUMBER") {
1192           token = tokenizer_->NextToken();
1193           if (!token->IsInteger())
1194             return Result("expected argument number");
1195 
1196           pipeline->AddBuffer(buffer, buffer_type, token->AsUint32());
1197         } else {
1198           return Result("missing ARG_NAME or ARG_NUMBER keyword");
1199         }
1200       } else {
1201         return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
1202       }
1203     }
1204   } else if (object_type == "SAMPLER" || object_type == "SAMPLER_ARRAY") {
1205     bool is_sampler_array = object_type == "SAMPLER_ARRAY";
1206     token = tokenizer_->NextToken();
1207     if (!token->IsIdentifier())
1208       return Result("missing sampler name in BIND command");
1209 
1210     auto* sampler = script_->GetSampler(token->AsString());
1211     if (!sampler)
1212       return Result("unknown sampler: " + token->AsString());
1213     std::vector<Sampler*> samplers = {sampler};
1214 
1215     if (is_sampler_array) {
1216       // Check for additional sampler names
1217       token = tokenizer_->PeekNextToken();
1218       while (token->IsIdentifier() && token->AsString() != "KERNEL" &&
1219              token->AsString() != "DESCRIPTOR_SET") {
1220         tokenizer_->NextToken();
1221         sampler = script_->GetSampler(token->AsString());
1222         if (!sampler)
1223           return Result("unknown sampler: " + token->AsString());
1224         samplers.push_back(sampler);
1225         token = tokenizer_->PeekNextToken();
1226       }
1227 
1228       if (samplers.size() < 2)
1229         return Result("expecting multiple sampler names for SAMPLER_ARRAY");
1230     }
1231 
1232     token = tokenizer_->NextToken();
1233     if (!token->IsIdentifier())
1234       return Result("expected a string token for BIND command");
1235 
1236     if (token->AsString() == "DESCRIPTOR_SET") {
1237       token = tokenizer_->NextToken();
1238       if (!token->IsInteger())
1239         return Result("invalid value for DESCRIPTOR_SET in BIND command");
1240       uint32_t descriptor_set = token->AsUint32();
1241 
1242       token = tokenizer_->NextToken();
1243       if (!token->IsIdentifier() || token->AsString() != "BINDING")
1244         return Result("missing BINDING for BIND command");
1245 
1246       token = tokenizer_->NextToken();
1247       if (!token->IsInteger())
1248         return Result("invalid value for BINDING in BIND command");
1249 
1250       uint32_t binding = token->AsUint32();
1251       pipeline->ClearSamplers(descriptor_set, binding);
1252       for (const auto& s : samplers) {
1253         pipeline->AddSampler(s, descriptor_set, binding);
1254       }
1255     } else if (token->AsString() == "KERNEL") {
1256       token = tokenizer_->NextToken();
1257       if (!token->IsIdentifier())
1258         return Result("missing kernel arg identifier");
1259 
1260       if (token->AsString() == "ARG_NAME") {
1261         token = tokenizer_->NextToken();
1262         if (!token->IsIdentifier())
1263           return Result("expected argument identifier");
1264 
1265         pipeline->AddSampler(sampler, token->AsString());
1266       } else if (token->AsString() == "ARG_NUMBER") {
1267         token = tokenizer_->NextToken();
1268         if (!token->IsInteger())
1269           return Result("expected argument number");
1270 
1271         pipeline->AddSampler(sampler, token->AsUint32());
1272       } else {
1273         return Result("missing ARG_NAME or ARG_NUMBER keyword");
1274       }
1275     } else {
1276       return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
1277     }
1278   } else {
1279     return Result("missing BUFFER or SAMPLER in BIND command");
1280   }
1281 
1282   return ValidateEndOfStatement("BIND command");
1283 }
1284 
ParsePipelineVertexData(Pipeline * pipeline)1285 Result Parser::ParsePipelineVertexData(Pipeline* pipeline) {
1286   auto token = tokenizer_->NextToken();
1287   if (!token->IsIdentifier())
1288     return Result("missing buffer name in VERTEX_DATA command");
1289 
1290   auto* buffer = script_->GetBuffer(token->AsString());
1291   if (!buffer)
1292     return Result("unknown buffer: " + token->AsString());
1293 
1294   token = tokenizer_->NextToken();
1295   if (!token->IsIdentifier() || token->AsString() != "LOCATION")
1296     return Result("VERTEX_DATA missing LOCATION");
1297 
1298   token = tokenizer_->NextToken();
1299   if (!token->IsInteger())
1300     return Result("invalid value for VERTEX_DATA LOCATION");
1301   const uint32_t location = token->AsUint32();
1302 
1303   InputRate rate = InputRate::kVertex;
1304   uint32_t offset = 0;
1305   Format* format = buffer->GetFormat();
1306   uint32_t stride = 0;
1307 
1308   token = tokenizer_->PeekNextToken();
1309   while (token->IsIdentifier()) {
1310     if (token->AsString() == "RATE") {
1311       tokenizer_->NextToken();
1312       token = tokenizer_->NextToken();
1313       if (!token->IsIdentifier())
1314         return Result("missing input rate value for RATE");
1315       if (token->AsString() == "instance") {
1316         rate = InputRate::kInstance;
1317       } else if (token->AsString() != "vertex") {
1318         return Result("expecting 'vertex' or 'instance' for RATE value");
1319       }
1320     } else if (token->AsString() == "OFFSET") {
1321       tokenizer_->NextToken();
1322       token = tokenizer_->NextToken();
1323       if (!token->IsInteger())
1324         return Result("expected unsigned integer for OFFSET");
1325       offset = token->AsUint32();
1326     } else if (token->AsString() == "STRIDE") {
1327       tokenizer_->NextToken();
1328       token = tokenizer_->NextToken();
1329       if (!token->IsInteger())
1330         return Result("expected unsigned integer for STRIDE");
1331       stride = token->AsUint32();
1332       if (stride == 0)
1333         return Result("STRIDE needs to be larger than zero");
1334     } else if (token->AsString() == "FORMAT") {
1335       tokenizer_->NextToken();
1336       token = tokenizer_->NextToken();
1337       if (!token->IsIdentifier())
1338         return Result("vertex data FORMAT must be an identifier");
1339       auto type = script_->ParseType(token->AsString());
1340       if (!type)
1341         return Result("invalid vertex data FORMAT");
1342       auto fmt = MakeUnique<Format>(type);
1343       format = fmt.get();
1344       script_->RegisterFormat(std::move(fmt));
1345     } else {
1346       return Result("unexpected identifier for VERTEX_DATA command: " +
1347                     token->ToOriginalString());
1348     }
1349 
1350     token = tokenizer_->PeekNextToken();
1351   }
1352 
1353   if (stride == 0)
1354     stride = format->SizeInBytes();
1355 
1356   Result r =
1357       pipeline->AddVertexBuffer(buffer, location, rate, format, offset, stride);
1358   if (!r.IsSuccess())
1359     return r;
1360 
1361   return ValidateEndOfStatement("VERTEX_DATA command");
1362 }
1363 
ParsePipelineIndexData(Pipeline * pipeline)1364 Result Parser::ParsePipelineIndexData(Pipeline* pipeline) {
1365   auto token = tokenizer_->NextToken();
1366   if (!token->IsIdentifier())
1367     return Result("missing buffer name in INDEX_DATA command");
1368 
1369   auto* buffer = script_->GetBuffer(token->AsString());
1370   if (!buffer)
1371     return Result("unknown buffer: " + token->AsString());
1372 
1373   Result r = pipeline->SetIndexBuffer(buffer);
1374   if (!r.IsSuccess())
1375     return r;
1376 
1377   return ValidateEndOfStatement("INDEX_DATA command");
1378 }
1379 
ParsePipelineSet(Pipeline * pipeline)1380 Result Parser::ParsePipelineSet(Pipeline* pipeline) {
1381   if (pipeline->GetShaders().empty() ||
1382       pipeline->GetShaders()[0].GetShader()->GetFormat() !=
1383           kShaderFormatOpenCLC) {
1384     return Result("SET can only be used with OPENCL-C shaders");
1385   }
1386 
1387   auto token = tokenizer_->NextToken();
1388   if (!token->IsIdentifier() || token->AsString() != "KERNEL")
1389     return Result("missing KERNEL in SET command");
1390 
1391   token = tokenizer_->NextToken();
1392   if (!token->IsIdentifier())
1393     return Result("expected ARG_NAME or ARG_NUMBER");
1394 
1395   std::string arg_name = "";
1396   uint32_t arg_no = std::numeric_limits<uint32_t>::max();
1397   if (token->AsString() == "ARG_NAME") {
1398     token = tokenizer_->NextToken();
1399     if (!token->IsIdentifier())
1400       return Result("expected argument identifier");
1401 
1402     arg_name = token->AsString();
1403   } else if (token->AsString() == "ARG_NUMBER") {
1404     token = tokenizer_->NextToken();
1405     if (!token->IsInteger())
1406       return Result("expected argument number");
1407 
1408     arg_no = token->AsUint32();
1409   } else {
1410     return Result("expected ARG_NAME or ARG_NUMBER");
1411   }
1412 
1413   token = tokenizer_->NextToken();
1414   if (!token->IsIdentifier() || token->AsString() != "AS")
1415     return Result("missing AS in SET command");
1416 
1417   token = tokenizer_->NextToken();
1418   if (!token->IsIdentifier())
1419     return Result("expected data type");
1420 
1421   auto type = ToType(token->AsString());
1422   if (!type)
1423     return Result("invalid data type '" + token->AsString() + "' provided");
1424 
1425   if (type->IsVec() || type->IsMatrix() || type->IsArray() || type->IsStruct())
1426     return Result("data type must be a scalar type");
1427 
1428   token = tokenizer_->NextToken();
1429   if (!token->IsInteger() && !token->IsDouble())
1430     return Result("expected data value");
1431 
1432   auto fmt = MakeUnique<Format>(type.get());
1433   Value value;
1434   if (fmt->IsFloat32() || fmt->IsFloat64())
1435     value.SetDoubleValue(token->AsDouble());
1436   else
1437     value.SetIntValue(token->AsUint64());
1438 
1439   Pipeline::ArgSetInfo info;
1440   info.name = arg_name;
1441   info.ordinal = arg_no;
1442   info.fmt = fmt.get();
1443   info.value = value;
1444   pipeline->SetArg(std::move(info));
1445   script_->RegisterFormat(std::move(fmt));
1446   script_->RegisterType(std::move(type));
1447 
1448   return ValidateEndOfStatement("SET command");
1449 }
1450 
ParsePipelinePolygonMode(Pipeline * pipeline)1451 Result Parser::ParsePipelinePolygonMode(Pipeline* pipeline) {
1452   auto token = tokenizer_->NextToken();
1453   if (!token->IsIdentifier())
1454     return Result("missing mode in POLYGON_MODE command");
1455 
1456   auto mode = token->AsString();
1457 
1458   if (mode == "fill")
1459     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kFill);
1460   else if (mode == "line")
1461     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kLine);
1462   else if (mode == "point")
1463     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kPoint);
1464   else
1465     return Result("invalid polygon mode: " + mode);
1466 
1467   return ValidateEndOfStatement("POLYGON_MODE command");
1468 }
1469 
ParsePipelineDepth(Pipeline * pipeline)1470 Result Parser::ParsePipelineDepth(Pipeline* pipeline) {
1471   while (true) {
1472     auto token = tokenizer_->NextToken();
1473     if (token->IsEOL())
1474       continue;
1475     if (token->IsEOS())
1476       return Result("DEPTH missing END command");
1477     if (!token->IsIdentifier())
1478       return Result("DEPTH options must be identifiers");
1479     if (token->AsString() == "END")
1480       break;
1481 
1482     if (token->AsString() == "TEST") {
1483       token = tokenizer_->NextToken();
1484 
1485       if (!token->IsIdentifier())
1486         return Result("invalid value for TEST");
1487 
1488       if (token->AsString() == "on")
1489         pipeline->GetPipelineData()->SetEnableDepthTest(true);
1490       else if (token->AsString() == "off")
1491         pipeline->GetPipelineData()->SetEnableDepthTest(false);
1492       else
1493         return Result("invalid value for TEST: " + token->AsString());
1494     } else if (token->AsString() == "CLAMP") {
1495       token = tokenizer_->NextToken();
1496 
1497       if (!token->IsIdentifier())
1498         return Result("invalid value for CLAMP");
1499 
1500       if (token->AsString() == "on")
1501         pipeline->GetPipelineData()->SetEnableDepthClamp(true);
1502       else if (token->AsString() == "off")
1503         pipeline->GetPipelineData()->SetEnableDepthClamp(false);
1504       else
1505         return Result("invalid value for CLAMP: " + token->AsString());
1506     } else if (token->AsString() == "WRITE") {
1507       token = tokenizer_->NextToken();
1508 
1509       if (!token->IsIdentifier())
1510         return Result("invalid value for WRITE");
1511 
1512       if (token->AsString() == "on")
1513         pipeline->GetPipelineData()->SetEnableDepthWrite(true);
1514       else if (token->AsString() == "off")
1515         pipeline->GetPipelineData()->SetEnableDepthWrite(false);
1516       else
1517         return Result("invalid value for WRITE: " + token->AsString());
1518     } else if (token->AsString() == "COMPARE_OP") {
1519       token = tokenizer_->NextToken();
1520 
1521       if (!token->IsIdentifier())
1522         return Result("invalid value for COMPARE_OP");
1523 
1524       CompareOp compare_op = StrToCompareOp(token->AsString());
1525       if (compare_op != CompareOp::kUnknown) {
1526         pipeline->GetPipelineData()->SetDepthCompareOp(compare_op);
1527       } else {
1528         return Result("invalid value for COMPARE_OP: " + token->AsString());
1529       }
1530     } else if (token->AsString() == "BOUNDS") {
1531       token = tokenizer_->NextToken();
1532       if (!token->IsIdentifier() || token->AsString() != "min")
1533         return Result("BOUNDS expecting min");
1534 
1535       token = tokenizer_->NextToken();
1536       if (!token->IsDouble())
1537         return Result("BOUNDS invalid value for min");
1538       pipeline->GetPipelineData()->SetMinDepthBounds(token->AsFloat());
1539 
1540       token = tokenizer_->NextToken();
1541       if (!token->IsIdentifier() || token->AsString() != "max")
1542         return Result("BOUNDS expecting max");
1543 
1544       token = tokenizer_->NextToken();
1545       if (!token->IsDouble())
1546         return Result("BOUNDS invalid value for max");
1547       pipeline->GetPipelineData()->SetMaxDepthBounds(token->AsFloat());
1548     } else if (token->AsString() == "BIAS") {
1549       pipeline->GetPipelineData()->SetEnableDepthBias(true);
1550 
1551       token = tokenizer_->NextToken();
1552       if (!token->IsIdentifier() || token->AsString() != "constant")
1553         return Result("BIAS expecting constant");
1554 
1555       token = tokenizer_->NextToken();
1556       if (!token->IsDouble())
1557         return Result("BIAS invalid value for constant");
1558       pipeline->GetPipelineData()->SetDepthBiasConstantFactor(token->AsFloat());
1559 
1560       token = tokenizer_->NextToken();
1561       if (!token->IsIdentifier() || token->AsString() != "clamp")
1562         return Result("BIAS expecting clamp");
1563 
1564       token = tokenizer_->NextToken();
1565       if (!token->IsDouble())
1566         return Result("BIAS invalid value for clamp");
1567       pipeline->GetPipelineData()->SetDepthBiasClamp(token->AsFloat());
1568 
1569       token = tokenizer_->NextToken();
1570       if (!token->IsIdentifier() || token->AsString() != "slope")
1571         return Result("BIAS expecting slope");
1572 
1573       token = tokenizer_->NextToken();
1574       if (!token->IsDouble())
1575         return Result("BIAS invalid value for slope");
1576       pipeline->GetPipelineData()->SetDepthBiasSlopeFactor(token->AsFloat());
1577     } else {
1578       return Result("invalid value for DEPTH: " + token->AsString());
1579     }
1580   }
1581 
1582   return ValidateEndOfStatement("DEPTH command");
1583 }
1584 
ParsePipelineStencil(Pipeline * pipeline)1585 Result Parser::ParsePipelineStencil(Pipeline* pipeline) {
1586   auto token = tokenizer_->NextToken();
1587   if (!token->IsIdentifier())
1588     return Result("STENCIL missing face");
1589 
1590   bool setFront = false;
1591   bool setBack = false;
1592 
1593   if (token->AsString() == "front") {
1594     setFront = true;
1595   } else if (token->AsString() == "back") {
1596     setBack = true;
1597   } else if (token->AsString() == "front_and_back") {
1598     setFront = true;
1599     setBack = true;
1600   } else {
1601     return Result("STENCIL invalid face: " + token->AsString());
1602   }
1603 
1604   while (true) {
1605     token = tokenizer_->NextToken();
1606     if (token->IsEOL())
1607       continue;
1608     if (token->IsEOS())
1609       return Result("STENCIL missing END command");
1610     if (!token->IsIdentifier())
1611       return Result("STENCIL options must be identifiers");
1612     if (token->AsString() == "END")
1613       break;
1614 
1615     if (token->AsString() == "TEST") {
1616       token = tokenizer_->NextToken();
1617 
1618       if (!token->IsIdentifier())
1619         return Result("STENCIL invalid value for TEST");
1620 
1621       if (token->AsString() == "on")
1622         pipeline->GetPipelineData()->SetEnableStencilTest(true);
1623       else if (token->AsString() == "off")
1624         pipeline->GetPipelineData()->SetEnableStencilTest(false);
1625       else
1626         return Result("STENCIL invalid value for TEST: " + token->AsString());
1627     } else if (token->AsString() == "FAIL_OP") {
1628       token = tokenizer_->NextToken();
1629 
1630       if (!token->IsIdentifier())
1631         return Result("STENCIL invalid value for FAIL_OP");
1632 
1633       StencilOp stencil_op = StrToStencilOp(token->AsString());
1634       if (stencil_op == StencilOp::kUnknown) {
1635         return Result("STENCIL invalid value for FAIL_OP: " +
1636                       token->AsString());
1637       }
1638       if (setFront)
1639         pipeline->GetPipelineData()->SetFrontFailOp(stencil_op);
1640       if (setBack)
1641         pipeline->GetPipelineData()->SetBackFailOp(stencil_op);
1642     } else if (token->AsString() == "PASS_OP") {
1643       token = tokenizer_->NextToken();
1644 
1645       if (!token->IsIdentifier())
1646         return Result("STENCIL invalid value for PASS_OP");
1647 
1648       StencilOp stencil_op = StrToStencilOp(token->AsString());
1649       if (stencil_op == StencilOp::kUnknown) {
1650         return Result("STENCIL invalid value for PASS_OP: " +
1651                       token->AsString());
1652       }
1653       if (setFront)
1654         pipeline->GetPipelineData()->SetFrontPassOp(stencil_op);
1655       if (setBack)
1656         pipeline->GetPipelineData()->SetBackPassOp(stencil_op);
1657     } else if (token->AsString() == "DEPTH_FAIL_OP") {
1658       token = tokenizer_->NextToken();
1659 
1660       if (!token->IsIdentifier())
1661         return Result("STENCIL invalid value for DEPTH_FAIL_OP");
1662 
1663       StencilOp stencil_op = StrToStencilOp(token->AsString());
1664       if (stencil_op == StencilOp::kUnknown) {
1665         return Result("STENCIL invalid value for DEPTH_FAIL_OP: " +
1666                       token->AsString());
1667       }
1668       if (setFront)
1669         pipeline->GetPipelineData()->SetFrontDepthFailOp(stencil_op);
1670       if (setBack)
1671         pipeline->GetPipelineData()->SetBackDepthFailOp(stencil_op);
1672     } else if (token->AsString() == "COMPARE_OP") {
1673       token = tokenizer_->NextToken();
1674 
1675       if (!token->IsIdentifier())
1676         return Result("STENCIL invalid value for COMPARE_OP");
1677 
1678       CompareOp compare_op = StrToCompareOp(token->AsString());
1679       if (compare_op == CompareOp::kUnknown) {
1680         return Result("STENCIL invalid value for COMPARE_OP: " +
1681                       token->AsString());
1682       }
1683       if (setFront)
1684         pipeline->GetPipelineData()->SetFrontCompareOp(compare_op);
1685       if (setBack)
1686         pipeline->GetPipelineData()->SetBackCompareOp(compare_op);
1687     } else if (token->AsString() == "COMPARE_MASK") {
1688       token = tokenizer_->NextToken();
1689 
1690       if (!token->IsInteger())
1691         return Result("STENCIL invalid value for COMPARE_MASK");
1692 
1693       if (setFront)
1694         pipeline->GetPipelineData()->SetFrontCompareMask(token->AsUint32());
1695       if (setBack)
1696         pipeline->GetPipelineData()->SetBackCompareMask(token->AsUint32());
1697     } else if (token->AsString() == "WRITE_MASK") {
1698       token = tokenizer_->NextToken();
1699 
1700       if (!token->IsInteger())
1701         return Result("STENCIL invalid value for WRITE_MASK");
1702 
1703       if (setFront)
1704         pipeline->GetPipelineData()->SetFrontWriteMask(token->AsUint32());
1705       if (setBack)
1706         pipeline->GetPipelineData()->SetBackWriteMask(token->AsUint32());
1707     } else if (token->AsString() == "REFERENCE") {
1708       token = tokenizer_->NextToken();
1709 
1710       if (!token->IsInteger())
1711         return Result("STENCIL invalid value for REFERENCE");
1712 
1713       if (setFront)
1714         pipeline->GetPipelineData()->SetFrontReference(token->AsUint32());
1715       if (setBack)
1716         pipeline->GetPipelineData()->SetBackReference(token->AsUint32());
1717     } else {
1718       return Result("STENCIL invalid value for STENCIL: " + token->AsString());
1719     }
1720   }
1721 
1722   return ValidateEndOfStatement("STENCIL command");
1723 }
1724 
ParseStruct()1725 Result Parser::ParseStruct() {
1726   auto token = tokenizer_->NextToken();
1727   if (!token->IsIdentifier())
1728     return Result("invalid STRUCT name provided");
1729 
1730   auto struct_name = token->AsString();
1731   if (struct_name == "STRIDE")
1732     return Result("missing STRUCT name");
1733 
1734   auto s = MakeUnique<type::Struct>();
1735   auto type = s.get();
1736 
1737   Result r = script_->AddType(struct_name, std::move(s));
1738   if (!r.IsSuccess())
1739     return r;
1740 
1741   token = tokenizer_->NextToken();
1742   if (token->IsIdentifier()) {
1743     if (token->AsString() != "STRIDE")
1744       return Result("invalid token in STRUCT definition");
1745 
1746     token = tokenizer_->NextToken();
1747     if (token->IsEOL() || token->IsEOS())
1748       return Result("missing value for STRIDE");
1749     if (!token->IsInteger())
1750       return Result("invalid value for STRIDE");
1751 
1752     type->SetStrideInBytes(token->AsUint32());
1753     token = tokenizer_->NextToken();
1754   }
1755   if (!token->IsEOL()) {
1756     return Result("extra token " + token->ToOriginalString() +
1757                   " after STRUCT header");
1758   }
1759 
1760   std::map<std::string, bool> seen;
1761   for (;;) {
1762     token = tokenizer_->NextToken();
1763     if (!token->IsIdentifier())
1764       return Result("invalid type for STRUCT member");
1765     if (token->AsString() == "END")
1766       break;
1767 
1768     if (token->AsString() == struct_name)
1769       return Result("recursive types are not allowed");
1770 
1771     type::Type* member_type = script_->GetType(token->AsString());
1772     if (!member_type) {
1773       auto t = ToType(token->AsString());
1774       if (!t) {
1775         return Result("unknown type '" + token->AsString() +
1776                       "' for STRUCT member");
1777       }
1778 
1779       member_type = t.get();
1780       script_->RegisterType(std::move(t));
1781     }
1782 
1783     token = tokenizer_->NextToken();
1784     if (token->IsEOL())
1785       return Result("missing name for STRUCT member");
1786     if (!token->IsIdentifier())
1787       return Result("invalid name for STRUCT member");
1788 
1789     auto member_name = token->AsString();
1790     if (seen.find(member_name) != seen.end())
1791       return Result("duplicate name for STRUCT member");
1792 
1793     seen[member_name] = true;
1794 
1795     auto m = type->AddMember(member_type);
1796     m->name = member_name;
1797 
1798     token = tokenizer_->NextToken();
1799     while (token->IsIdentifier()) {
1800       if (token->AsString() == "OFFSET") {
1801         token = tokenizer_->NextToken();
1802         if (token->IsEOL())
1803           return Result("missing value for STRUCT member OFFSET");
1804         if (!token->IsInteger())
1805           return Result("invalid value for STRUCT member OFFSET");
1806 
1807         m->offset_in_bytes = token->AsInt32();
1808       } else if (token->AsString() == "ARRAY_STRIDE") {
1809         token = tokenizer_->NextToken();
1810         if (token->IsEOL())
1811           return Result("missing value for STRUCT member ARRAY_STRIDE");
1812         if (!token->IsInteger())
1813           return Result("invalid value for STRUCT member ARRAY_STRIDE");
1814         if (!member_type->IsArray())
1815           return Result("ARRAY_STRIDE only valid on array members");
1816 
1817         m->array_stride_in_bytes = token->AsInt32();
1818       } else if (token->AsString() == "MATRIX_STRIDE") {
1819         token = tokenizer_->NextToken();
1820         if (token->IsEOL())
1821           return Result("missing value for STRUCT member MATRIX_STRIDE");
1822         if (!token->IsInteger())
1823           return Result("invalid value for STRUCT member MATRIX_STRIDE");
1824         if (!member_type->IsMatrix())
1825           return Result("MATRIX_STRIDE only valid on matrix members");
1826 
1827         m->matrix_stride_in_bytes = token->AsInt32();
1828       } else {
1829         return Result("unknown param '" + token->AsString() +
1830                       "' for STRUCT member");
1831       }
1832 
1833       token = tokenizer_->NextToken();
1834     }
1835 
1836     if (!token->IsEOL())
1837       return Result("extra param for STRUCT member");
1838   }
1839 
1840   return {};
1841 }
1842 
ParseBuffer()1843 Result Parser::ParseBuffer() {
1844   auto token = tokenizer_->NextToken();
1845   if (!token->IsIdentifier())
1846     return Result("invalid BUFFER name provided");
1847 
1848   auto name = token->AsString();
1849   if (name == "DATA_TYPE" || name == "FORMAT")
1850     return Result("missing BUFFER name");
1851 
1852   token = tokenizer_->NextToken();
1853   if (!token->IsIdentifier())
1854     return Result("invalid BUFFER command provided");
1855 
1856   std::unique_ptr<Buffer> buffer;
1857   auto& cmd = token->AsString();
1858   if (cmd == "DATA_TYPE") {
1859     buffer = MakeUnique<Buffer>();
1860 
1861     Result r = ParseBufferInitializer(buffer.get());
1862     if (!r.IsSuccess())
1863       return r;
1864   } else if (cmd == "FORMAT") {
1865     token = tokenizer_->NextToken();
1866     if (!token->IsIdentifier())
1867       return Result("BUFFER FORMAT must be an identifier");
1868 
1869     buffer = MakeUnique<Buffer>();
1870 
1871     auto type = script_->ParseType(token->AsString());
1872     if (!type)
1873       return Result("invalid BUFFER FORMAT");
1874 
1875     auto fmt = MakeUnique<Format>(type);
1876     buffer->SetFormat(fmt.get());
1877     script_->RegisterFormat(std::move(fmt));
1878 
1879     token = tokenizer_->PeekNextToken();
1880     while (token->IsIdentifier()) {
1881       if (token->AsString() == "MIP_LEVELS") {
1882         tokenizer_->NextToken();
1883         token = tokenizer_->NextToken();
1884 
1885         if (!token->IsInteger())
1886           return Result("invalid value for MIP_LEVELS");
1887 
1888         buffer->SetMipLevels(token->AsUint32());
1889       } else if (token->AsString() == "FILE") {
1890         tokenizer_->NextToken();
1891         Result r = ParseBufferInitializerFile(buffer.get());
1892 
1893         if (!r.IsSuccess())
1894           return r;
1895       } else if (token->AsString() == "SAMPLES") {
1896         tokenizer_->NextToken();
1897         token = tokenizer_->NextToken();
1898         if (!token->IsInteger())
1899           return Result("expected integer value for SAMPLES");
1900 
1901         const uint32_t samples = token->AsUint32();
1902         if (!IsValidSampleCount(samples))
1903           return Result("invalid sample count: " + token->ToOriginalString());
1904 
1905         buffer->SetSamples(samples);
1906       } else {
1907         break;
1908       }
1909       token = tokenizer_->PeekNextToken();
1910     }
1911   } else {
1912     return Result("unknown BUFFER command provided: " + cmd);
1913   }
1914   buffer->SetName(name);
1915 
1916   Result r = script_->AddBuffer(std::move(buffer));
1917   if (!r.IsSuccess())
1918     return r;
1919 
1920   return {};
1921 }
1922 
ParseImage()1923 Result Parser::ParseImage() {
1924   auto token = tokenizer_->NextToken();
1925   if (!token->IsIdentifier())
1926     return Result("invalid IMAGE name provided");
1927 
1928   auto name = token->AsString();
1929   if (name == "DATA_TYPE" || name == "FORMAT")
1930     return Result("missing IMAGE name");
1931 
1932   std::unique_ptr<Buffer> buffer = MakeUnique<Buffer>();
1933   buffer->SetName(name);
1934   bool width_set = false;
1935   bool height_set = false;
1936   bool depth_set = false;
1937 
1938   token = tokenizer_->PeekNextToken();
1939   while (token->IsIdentifier()) {
1940     if (token->AsString() == "FILL" || token->AsString() == "SERIES_FROM" ||
1941         token->AsString() == "DATA") {
1942       break;
1943     }
1944 
1945     tokenizer_->NextToken();
1946 
1947     if (token->AsString() == "DATA_TYPE") {
1948       token = tokenizer_->NextToken();
1949       if (!token->IsIdentifier())
1950         return Result("IMAGE invalid data type");
1951 
1952       auto type = script_->ParseType(token->AsString());
1953       std::unique_ptr<Format> fmt;
1954       if (type != nullptr) {
1955         fmt = MakeUnique<Format>(type);
1956         buffer->SetFormat(fmt.get());
1957       } else {
1958         auto new_type = ToType(token->AsString());
1959         if (!new_type) {
1960           return Result("invalid data type '" + token->AsString() +
1961                         "' provided");
1962         }
1963 
1964         fmt = MakeUnique<Format>(new_type.get());
1965         buffer->SetFormat(fmt.get());
1966         script_->RegisterType(std::move(new_type));
1967       }
1968       script_->RegisterFormat(std::move(fmt));
1969     } else if (token->AsString() == "FORMAT") {
1970       token = tokenizer_->NextToken();
1971       if (!token->IsIdentifier())
1972         return Result("IMAGE FORMAT must be an identifier");
1973 
1974       auto type = script_->ParseType(token->AsString());
1975       if (!type)
1976         return Result("invalid IMAGE FORMAT");
1977 
1978       auto fmt = MakeUnique<Format>(type);
1979       buffer->SetFormat(fmt.get());
1980       script_->RegisterFormat(std::move(fmt));
1981     } else if (token->AsString() == "MIP_LEVELS") {
1982       token = tokenizer_->NextToken();
1983 
1984       if (!token->IsInteger())
1985         return Result("invalid value for MIP_LEVELS");
1986 
1987       buffer->SetMipLevels(token->AsUint32());
1988     } else if (token->AsString() == "DIM_1D") {
1989       buffer->SetImageDimension(ImageDimension::k1D);
1990     } else if (token->AsString() == "DIM_2D") {
1991       buffer->SetImageDimension(ImageDimension::k2D);
1992     } else if (token->AsString() == "DIM_3D") {
1993       buffer->SetImageDimension(ImageDimension::k3D);
1994     } else if (token->AsString() == "WIDTH") {
1995       token = tokenizer_->NextToken();
1996       if (!token->IsInteger() || token->AsUint32() == 0)
1997         return Result("expected positive IMAGE WIDTH");
1998 
1999       buffer->SetWidth(token->AsUint32());
2000       width_set = true;
2001     } else if (token->AsString() == "HEIGHT") {
2002       token = tokenizer_->NextToken();
2003       if (!token->IsInteger() || token->AsUint32() == 0)
2004         return Result("expected positive IMAGE HEIGHT");
2005 
2006       buffer->SetHeight(token->AsUint32());
2007       height_set = true;
2008     } else if (token->AsString() == "DEPTH") {
2009       token = tokenizer_->NextToken();
2010       if (!token->IsInteger() || token->AsUint32() == 0)
2011         return Result("expected positive IMAGE DEPTH");
2012 
2013       buffer->SetDepth(token->AsUint32());
2014       depth_set = true;
2015     } else if (token->AsString() == "SAMPLES") {
2016       token = tokenizer_->NextToken();
2017       if (!token->IsInteger())
2018         return Result("expected integer value for SAMPLES");
2019 
2020       const uint32_t samples = token->AsUint32();
2021       if (!IsValidSampleCount(samples))
2022         return Result("invalid sample count: " + token->ToOriginalString());
2023 
2024       buffer->SetSamples(samples);
2025     } else {
2026       return Result("unknown IMAGE command provided: " +
2027                     token->ToOriginalString());
2028     }
2029     token = tokenizer_->PeekNextToken();
2030   }
2031 
2032   if (buffer->GetImageDimension() == ImageDimension::k3D && !depth_set)
2033     return Result("expected IMAGE DEPTH");
2034 
2035   if ((buffer->GetImageDimension() == ImageDimension::k3D ||
2036        buffer->GetImageDimension() == ImageDimension::k2D) &&
2037       !height_set) {
2038     return Result("expected IMAGE HEIGHT");
2039   }
2040   if (!width_set)
2041     return Result("expected IMAGE WIDTH");
2042 
2043   const uint32_t size_in_items =
2044       buffer->GetWidth() * buffer->GetHeight() * buffer->GetDepth();
2045   buffer->SetElementCount(size_in_items);
2046 
2047   // Parse initializers.
2048   token = tokenizer_->NextToken();
2049   if (token->IsIdentifier()) {
2050     if (token->AsString() == "DATA") {
2051       Result r = ParseBufferInitializerData(buffer.get());
2052       if (!r.IsSuccess())
2053         return r;
2054 
2055       if (size_in_items != buffer->ElementCount()) {
2056         return Result(
2057             "Elements provided in data does not match size specified: " +
2058             std::to_string(size_in_items) + " specified vs " +
2059             std::to_string(buffer->ElementCount()) + " provided");
2060       }
2061     } else if (token->AsString() == "FILL") {
2062       Result r = ParseBufferInitializerFill(buffer.get(), size_in_items);
2063       if (!r.IsSuccess())
2064         return r;
2065     } else if (token->AsString() == "SERIES_FROM") {
2066       Result r = ParseBufferInitializerSeries(buffer.get(), size_in_items);
2067       if (!r.IsSuccess())
2068         return r;
2069     } else {
2070       return Result("unexpected IMAGE token: " + token->AsString());
2071     }
2072   } else if (!token->IsEOL() && !token->IsEOS()) {
2073     return Result("unexpected IMAGE token: " + token->ToOriginalString());
2074   }
2075 
2076   Result r = script_->AddBuffer(std::move(buffer));
2077   if (!r.IsSuccess())
2078     return r;
2079 
2080   return {};
2081 }
2082 
ParseBufferInitializer(Buffer * buffer)2083 Result Parser::ParseBufferInitializer(Buffer* buffer) {
2084   auto token = tokenizer_->NextToken();
2085   if (!token->IsIdentifier())
2086     return Result("BUFFER invalid data type");
2087 
2088   auto type = script_->ParseType(token->AsString());
2089   std::unique_ptr<Format> fmt;
2090   if (type != nullptr) {
2091     fmt = MakeUnique<Format>(type);
2092     buffer->SetFormat(fmt.get());
2093   } else {
2094     auto new_type = ToType(token->AsString());
2095     if (!new_type)
2096       return Result("invalid data type '" + token->AsString() + "' provided");
2097 
2098     fmt = MakeUnique<Format>(new_type.get());
2099     buffer->SetFormat(fmt.get());
2100     type = new_type.get();
2101     script_->RegisterType(std::move(new_type));
2102   }
2103   script_->RegisterFormat(std::move(fmt));
2104 
2105   token = tokenizer_->NextToken();
2106   if (!token->IsIdentifier())
2107     return Result("BUFFER missing initializer");
2108 
2109   if (token->AsString() == "STD140") {
2110     buffer->GetFormat()->SetLayout(Format::Layout::kStd140);
2111     token = tokenizer_->NextToken();
2112   } else if (token->AsString() == "STD430") {
2113     buffer->GetFormat()->SetLayout(Format::Layout::kStd430);
2114     token = tokenizer_->NextToken();
2115   }
2116 
2117   if (!token->IsIdentifier())
2118     return Result("BUFFER missing initializer");
2119 
2120   if (token->AsString() == "SIZE")
2121     return ParseBufferInitializerSize(buffer);
2122   if (token->AsString() == "WIDTH") {
2123     token = tokenizer_->NextToken();
2124     if (!token->IsInteger())
2125       return Result("expected an integer for WIDTH");
2126     const uint32_t width = token->AsUint32();
2127     if (width == 0)
2128       return Result("expected WIDTH to be positive");
2129     buffer->SetWidth(width);
2130     buffer->SetImageDimension(ImageDimension::k2D);
2131 
2132     token = tokenizer_->NextToken();
2133     if (token->AsString() != "HEIGHT")
2134       return Result("BUFFER HEIGHT missing");
2135     token = tokenizer_->NextToken();
2136     if (!token->IsInteger())
2137       return Result("expected an integer for HEIGHT");
2138     const uint32_t height = token->AsUint32();
2139     if (height == 0)
2140       return Result("expected HEIGHT to be positive");
2141     buffer->SetHeight(height);
2142 
2143     token = tokenizer_->NextToken();
2144     uint32_t size_in_items = width * height;
2145     buffer->SetElementCount(size_in_items);
2146     if (token->AsString() == "FILL")
2147       return ParseBufferInitializerFill(buffer, size_in_items);
2148     if (token->AsString() == "SERIES_FROM")
2149       return ParseBufferInitializerSeries(buffer, size_in_items);
2150     return {};
2151   }
2152   if (token->AsString() == "DATA")
2153     return ParseBufferInitializerData(buffer);
2154 
2155   return Result("unknown initializer for BUFFER");
2156 }
2157 
ParseBufferInitializerSize(Buffer * buffer)2158 Result Parser::ParseBufferInitializerSize(Buffer* buffer) {
2159   auto token = tokenizer_->NextToken();
2160   if (token->IsEOS() || token->IsEOL())
2161     return Result("BUFFER size missing");
2162   if (!token->IsInteger())
2163     return Result("BUFFER size invalid");
2164 
2165   uint32_t size_in_items = token->AsUint32();
2166   buffer->SetElementCount(size_in_items);
2167 
2168   token = tokenizer_->NextToken();
2169   if (!token->IsIdentifier())
2170     return Result("BUFFER invalid initializer");
2171 
2172   if (token->AsString() == "FILL")
2173     return ParseBufferInitializerFill(buffer, size_in_items);
2174   if (token->AsString() == "SERIES_FROM")
2175     return ParseBufferInitializerSeries(buffer, size_in_items);
2176   if (token->AsString() == "FILE")
2177     return ParseBufferInitializerFile(buffer);
2178 
2179   return Result("invalid BUFFER initializer provided");
2180 }
2181 
ParseBufferInitializerFill(Buffer * buffer,uint32_t size_in_items)2182 Result Parser::ParseBufferInitializerFill(Buffer* buffer,
2183                                           uint32_t size_in_items) {
2184   auto token = tokenizer_->NextToken();
2185   if (token->IsEOS() || token->IsEOL())
2186     return Result("missing BUFFER fill value");
2187   if (!token->IsInteger() && !token->IsDouble())
2188     return Result("invalid BUFFER fill value");
2189 
2190   auto fmt = buffer->GetFormat();
2191   bool is_double_data = fmt->IsFloat32() || fmt->IsFloat64();
2192 
2193   // Inflate the size because our items are multi-dimensional.
2194   size_in_items = size_in_items * fmt->InputNeededPerElement();
2195 
2196   std::vector<Value> values;
2197   values.resize(size_in_items);
2198   for (size_t i = 0; i < size_in_items; ++i) {
2199     if (is_double_data)
2200       values[i].SetDoubleValue(token->AsDouble());
2201     else
2202       values[i].SetIntValue(token->AsUint64());
2203   }
2204   Result r = buffer->SetData(std::move(values));
2205   if (!r.IsSuccess())
2206     return r;
2207 
2208   return ValidateEndOfStatement("BUFFER fill command");
2209 }
2210 
ParseBufferInitializerSeries(Buffer * buffer,uint32_t size_in_items)2211 Result Parser::ParseBufferInitializerSeries(Buffer* buffer,
2212                                             uint32_t size_in_items) {
2213   auto token = tokenizer_->NextToken();
2214   if (token->IsEOS() || token->IsEOL())
2215     return Result("missing BUFFER series_from value");
2216   if (!token->IsInteger() && !token->IsDouble())
2217     return Result("invalid BUFFER series_from value");
2218 
2219   auto type = buffer->GetFormat()->GetType();
2220   if (type->IsMatrix() || type->IsVec())
2221     return Result("BUFFER series_from must not be multi-row/column types");
2222 
2223   Value counter;
2224 
2225   auto n = type->AsNumber();
2226   FormatMode mode = n->GetFormatMode();
2227   uint32_t num_bits = n->NumBits();
2228   if (type::Type::IsFloat32(mode, num_bits) ||
2229       type::Type::IsFloat64(mode, num_bits)) {
2230     counter.SetDoubleValue(token->AsDouble());
2231   } else {
2232     counter.SetIntValue(token->AsUint64());
2233   }
2234 
2235   token = tokenizer_->NextToken();
2236   if (!token->IsIdentifier())
2237     return Result("missing BUFFER series_from inc_by");
2238   if (token->AsString() != "INC_BY")
2239     return Result("BUFFER series_from invalid command");
2240 
2241   token = tokenizer_->NextToken();
2242   if (token->IsEOS() || token->IsEOL())
2243     return Result("missing BUFFER series_from inc_by value");
2244   if (!token->IsInteger() && !token->IsDouble())
2245     return Result("invalid BUFFER series_from inc_by value");
2246 
2247   std::vector<Value> values;
2248   values.resize(size_in_items);
2249   for (size_t i = 0; i < size_in_items; ++i) {
2250     if (type::Type::IsFloat32(mode, num_bits) ||
2251         type::Type::IsFloat64(mode, num_bits)) {
2252       double value = counter.AsDouble();
2253       values[i].SetDoubleValue(value);
2254       counter.SetDoubleValue(value + token->AsDouble());
2255     } else {
2256       uint64_t value = counter.AsUint64();
2257       values[i].SetIntValue(value);
2258       counter.SetIntValue(value + token->AsUint64());
2259     }
2260   }
2261   Result r = buffer->SetData(std::move(values));
2262   if (!r.IsSuccess())
2263     return r;
2264 
2265   return ValidateEndOfStatement("BUFFER series_from command");
2266 }
2267 
ParseBufferInitializerData(Buffer * buffer)2268 Result Parser::ParseBufferInitializerData(Buffer* buffer) {
2269   Result r = ParseBufferData(buffer, tokenizer_.get(), false);
2270 
2271   if (!r.IsSuccess())
2272     return r;
2273 
2274   return ValidateEndOfStatement("BUFFER data command");
2275 }
2276 
ParseBufferInitializerFile(Buffer * buffer)2277 Result Parser::ParseBufferInitializerFile(Buffer* buffer) {
2278   auto token = tokenizer_->NextToken();
2279 
2280   if (!token->IsIdentifier())
2281     return Result("invalid value for FILE");
2282 
2283   BufferDataFileType file_type = BufferDataFileType::kPng;
2284 
2285   if (token->AsString() == "TEXT") {
2286     file_type = BufferDataFileType::kText;
2287     token = tokenizer_->NextToken();
2288   } else if (token->AsString() == "BINARY") {
2289     file_type = BufferDataFileType::kBinary;
2290     token = tokenizer_->NextToken();
2291   } else if (token->AsString() == "PNG") {
2292     token = tokenizer_->NextToken();
2293   }
2294 
2295   if (!token->IsIdentifier())
2296     return Result("missing file name for FILE");
2297 
2298   if (!delegate_)
2299     return Result("missing delegate");
2300 
2301   BufferInfo info;
2302   Result r = delegate_->LoadBufferData(token->AsString(), file_type, &info);
2303 
2304   if (!r.IsSuccess())
2305     return r;
2306 
2307   std::vector<uint8_t>* data = buffer->ValuePtr();
2308 
2309   data->clear();
2310   data->reserve(info.values.size());
2311   for (auto v : info.values) {
2312     data->push_back(v.AsUint8());
2313   }
2314 
2315   if (file_type == BufferDataFileType::kText) {
2316     auto s = std::string(data->begin(), data->end());
2317     Tokenizer tok(s);
2318     r = ParseBufferData(buffer, &tok, true);
2319     if (!r.IsSuccess())
2320       return r;
2321   } else {
2322     buffer->SetElementCount(static_cast<uint32_t>(data->size()) /
2323                             buffer->GetFormat()->SizeInBytes());
2324     buffer->SetWidth(info.width);
2325     buffer->SetHeight(info.height);
2326   }
2327 
2328   return {};
2329 }
2330 
ParseRun()2331 Result Parser::ParseRun() {
2332   auto token = tokenizer_->NextToken();
2333   if (!token->IsIdentifier())
2334     return Result("missing pipeline name for RUN command");
2335 
2336   size_t line = tokenizer_->GetCurrentLine();
2337 
2338   auto* pipeline = script_->GetPipeline(token->AsString());
2339   if (!pipeline)
2340     return Result("unknown pipeline for RUN command: " + token->AsString());
2341 
2342   token = tokenizer_->NextToken();
2343   if (token->IsEOL() || token->IsEOS())
2344     return Result("RUN command requires parameters");
2345 
2346   if (token->IsInteger()) {
2347     if (!pipeline->IsCompute())
2348       return Result("RUN command requires compute pipeline");
2349 
2350     auto cmd = MakeUnique<ComputeCommand>(pipeline);
2351     cmd->SetLine(line);
2352     cmd->SetX(token->AsUint32());
2353 
2354     token = tokenizer_->NextToken();
2355     if (!token->IsInteger()) {
2356       return Result("invalid parameter for RUN command: " +
2357                     token->ToOriginalString());
2358     }
2359     cmd->SetY(token->AsUint32());
2360 
2361     token = tokenizer_->NextToken();
2362     if (!token->IsInteger()) {
2363       return Result("invalid parameter for RUN command: " +
2364                     token->ToOriginalString());
2365     }
2366     cmd->SetZ(token->AsUint32());
2367 
2368     command_list_.push_back(std::move(cmd));
2369     return ValidateEndOfStatement("RUN command");
2370   }
2371 
2372   if (!token->IsIdentifier())
2373     return Result("invalid token in RUN command: " + token->ToOriginalString());
2374 
2375   if (token->AsString() == "DRAW_RECT") {
2376     if (!pipeline->IsGraphics())
2377       return Result("RUN command requires graphics pipeline");
2378 
2379     if (pipeline->GetVertexBuffers().size() > 1) {
2380       return Result(
2381           "RUN DRAW_RECT is not supported in a pipeline with more than one "
2382           "vertex buffer attached");
2383     }
2384 
2385     token = tokenizer_->NextToken();
2386     if (token->IsEOS() || token->IsEOL())
2387       return Result("RUN DRAW_RECT command requires parameters");
2388 
2389     if (!token->IsIdentifier() || token->AsString() != "POS") {
2390       return Result("invalid token in RUN command: " +
2391                     token->ToOriginalString() + "; expected POS");
2392     }
2393 
2394     token = tokenizer_->NextToken();
2395     if (!token->IsInteger())
2396       return Result("missing X position for RUN command");
2397 
2398     auto cmd =
2399         MakeUnique<DrawRectCommand>(pipeline, *pipeline->GetPipelineData());
2400     cmd->SetLine(line);
2401     cmd->EnableOrtho();
2402 
2403     Result r = token->ConvertToDouble();
2404     if (!r.IsSuccess())
2405       return r;
2406     cmd->SetX(token->AsFloat());
2407 
2408     token = tokenizer_->NextToken();
2409     if (!token->IsInteger())
2410       return Result("missing Y position for RUN command");
2411 
2412     r = token->ConvertToDouble();
2413     if (!r.IsSuccess())
2414       return r;
2415     cmd->SetY(token->AsFloat());
2416 
2417     token = tokenizer_->NextToken();
2418     if (!token->IsIdentifier() || token->AsString() != "SIZE") {
2419       return Result("invalid token in RUN command: " +
2420                     token->ToOriginalString() + "; expected SIZE");
2421     }
2422 
2423     token = tokenizer_->NextToken();
2424     if (!token->IsInteger())
2425       return Result("missing width value for RUN command");
2426 
2427     r = token->ConvertToDouble();
2428     if (!r.IsSuccess())
2429       return r;
2430     cmd->SetWidth(token->AsFloat());
2431 
2432     token = tokenizer_->NextToken();
2433     if (!token->IsInteger())
2434       return Result("missing height value for RUN command");
2435 
2436     r = token->ConvertToDouble();
2437     if (!r.IsSuccess())
2438       return r;
2439     cmd->SetHeight(token->AsFloat());
2440 
2441     command_list_.push_back(std::move(cmd));
2442     return ValidateEndOfStatement("RUN command");
2443   }
2444 
2445   if (token->AsString() == "DRAW_GRID") {
2446     if (!pipeline->IsGraphics())
2447       return Result("RUN command requires graphics pipeline");
2448 
2449     if (pipeline->GetVertexBuffers().size() > 0) {
2450       return Result(
2451           "RUN DRAW_GRID is not supported in a pipeline with "
2452           "vertex buffers attached");
2453     }
2454 
2455     token = tokenizer_->NextToken();
2456     if (token->IsEOS() || token->IsEOL())
2457       return Result("RUN DRAW_GRID command requires parameters");
2458 
2459     if (!token->IsIdentifier() || token->AsString() != "POS") {
2460       return Result("invalid token in RUN command: " +
2461                     token->ToOriginalString() + "; expected POS");
2462     }
2463 
2464     token = tokenizer_->NextToken();
2465     if (!token->IsInteger())
2466       return Result("missing X position for RUN command");
2467 
2468     auto cmd =
2469         MakeUnique<DrawGridCommand>(pipeline, *pipeline->GetPipelineData());
2470     cmd->SetLine(line);
2471 
2472     Result r = token->ConvertToDouble();
2473     if (!r.IsSuccess())
2474       return r;
2475     cmd->SetX(token->AsFloat());
2476 
2477     token = tokenizer_->NextToken();
2478     if (!token->IsInteger())
2479       return Result("missing Y position for RUN command");
2480 
2481     r = token->ConvertToDouble();
2482     if (!r.IsSuccess())
2483       return r;
2484     cmd->SetY(token->AsFloat());
2485 
2486     token = tokenizer_->NextToken();
2487     if (!token->IsIdentifier() || token->AsString() != "SIZE") {
2488       return Result("invalid token in RUN command: " +
2489                     token->ToOriginalString() + "; expected SIZE");
2490     }
2491 
2492     token = tokenizer_->NextToken();
2493     if (!token->IsInteger())
2494       return Result("missing width value for RUN command");
2495 
2496     r = token->ConvertToDouble();
2497     if (!r.IsSuccess())
2498       return r;
2499     cmd->SetWidth(token->AsFloat());
2500 
2501     token = tokenizer_->NextToken();
2502     if (!token->IsInteger())
2503       return Result("missing height value for RUN command");
2504 
2505     r = token->ConvertToDouble();
2506     if (!r.IsSuccess())
2507       return r;
2508     cmd->SetHeight(token->AsFloat());
2509 
2510     token = tokenizer_->NextToken();
2511     if (!token->IsIdentifier() || token->AsString() != "CELLS") {
2512       return Result("invalid token in RUN command: " +
2513                     token->ToOriginalString() + "; expected CELLS");
2514     }
2515 
2516     token = tokenizer_->NextToken();
2517     if (!token->IsInteger())
2518       return Result("missing columns value for RUN command");
2519 
2520     cmd->SetColumns(token->AsUint32());
2521 
2522     token = tokenizer_->NextToken();
2523     if (!token->IsInteger())
2524       return Result("missing rows value for RUN command");
2525 
2526     cmd->SetRows(token->AsUint32());
2527 
2528     command_list_.push_back(std::move(cmd));
2529     return ValidateEndOfStatement("RUN command");
2530   }
2531 
2532   if (token->AsString() == "DRAW_ARRAY") {
2533     if (!pipeline->IsGraphics())
2534       return Result("RUN command requires graphics pipeline");
2535 
2536     if (pipeline->GetVertexBuffers().empty())
2537       return Result("RUN DRAW_ARRAY requires attached vertex buffer");
2538 
2539     token = tokenizer_->NextToken();
2540     if (!token->IsIdentifier() || token->AsString() != "AS")
2541       return Result("missing AS for RUN command");
2542 
2543     token = tokenizer_->NextToken();
2544     if (!token->IsIdentifier()) {
2545       return Result("invalid topology for RUN command: " +
2546                     token->ToOriginalString());
2547     }
2548 
2549     Topology topo = NameToTopology(token->AsString());
2550     if (topo == Topology::kUnknown)
2551       return Result("invalid topology for RUN command: " + token->AsString());
2552 
2553     bool indexed = false;
2554     uint32_t start_idx = 0;
2555     uint32_t count = 0;
2556     uint32_t start_instance = 0;
2557     uint32_t instance_count = 1;
2558 
2559     token = tokenizer_->PeekNextToken();
2560 
2561     while (!token->IsEOS() && !token->IsEOL()) {
2562       token = tokenizer_->NextToken();
2563 
2564       if (!token->IsIdentifier())
2565         return Result("expecting identifier for RUN command");
2566 
2567       if (token->AsString() == "INDEXED") {
2568         if (!pipeline->GetIndexBuffer()) {
2569           return Result(
2570               "RUN DRAW_ARRAYS INDEXED requires attached index buffer");
2571         }
2572 
2573         indexed = true;
2574       } else if (token->AsString() == "START_IDX") {
2575         token = tokenizer_->NextToken();
2576         if (!token->IsInteger()) {
2577           return Result("invalid START_IDX value for RUN command: " +
2578                         token->ToOriginalString());
2579         }
2580         if (token->AsInt32() < 0)
2581           return Result("START_IDX value must be >= 0 for RUN command");
2582         start_idx = token->AsUint32();
2583       } else if (token->AsString() == "COUNT") {
2584         token = tokenizer_->NextToken();
2585         if (!token->IsInteger()) {
2586           return Result("invalid COUNT value for RUN command: " +
2587                         token->ToOriginalString());
2588         }
2589         if (token->AsInt32() <= 0)
2590           return Result("COUNT value must be > 0 for RUN command");
2591 
2592         count = token->AsUint32();
2593       } else if (token->AsString() == "INSTANCE_COUNT") {
2594         token = tokenizer_->NextToken();
2595         if (!token->IsInteger()) {
2596           return Result("invalid INSTANCE_COUNT value for RUN command: " +
2597                         token->ToOriginalString());
2598         }
2599         if (token->AsInt32() <= 0)
2600           return Result("INSTANCE_COUNT value must be > 0 for RUN command");
2601 
2602         instance_count = token->AsUint32();
2603       } else if (token->AsString() == "START_INSTANCE") {
2604         token = tokenizer_->NextToken();
2605         if (!token->IsInteger()) {
2606           return Result("invalid START_INSTANCE value for RUN command: " +
2607                         token->ToOriginalString());
2608         }
2609         if (token->AsInt32() < 0)
2610           return Result("START_INSTANCE value must be >= 0 for RUN command");
2611         start_instance = token->AsUint32();
2612       } else {
2613         return Result("Unexpected identifier for RUN command: " +
2614                       token->ToOriginalString());
2615       }
2616 
2617       token = tokenizer_->PeekNextToken();
2618     }
2619 
2620     uint32_t vertex_count =
2621         indexed ? pipeline->GetIndexBuffer()->ElementCount()
2622                 : pipeline->GetVertexBuffers()[0].buffer->ElementCount();
2623 
2624     // If we get here then we never set count, as if count was set it must
2625     // be > 0.
2626     if (count == 0)
2627       count = vertex_count - start_idx;
2628 
2629     if (start_idx + count > vertex_count) {
2630       if (indexed)
2631         return Result("START_IDX plus COUNT exceeds index buffer data size");
2632       else
2633         return Result("START_IDX plus COUNT exceeds vertex buffer data size");
2634     }
2635 
2636     auto cmd =
2637         MakeUnique<DrawArraysCommand>(pipeline, *pipeline->GetPipelineData());
2638     cmd->SetLine(line);
2639     cmd->SetTopology(topo);
2640     cmd->SetFirstVertexIndex(start_idx);
2641     cmd->SetVertexCount(count);
2642     cmd->SetInstanceCount(instance_count);
2643     cmd->SetFirstInstance(start_instance);
2644 
2645     if (indexed)
2646       cmd->EnableIndexed();
2647 
2648     command_list_.push_back(std::move(cmd));
2649     return ValidateEndOfStatement("RUN command");
2650   }
2651 
2652   return Result("invalid token in RUN command: " + token->AsString());
2653 }
2654 
ParseDebug()2655 Result Parser::ParseDebug() {
2656   // DEBUG extends a RUN with debugger test cases
2657   auto res = ParseRun();
2658   if (!res.IsSuccess()) {
2659     return res;
2660   }
2661 
2662   // As ParseRun() succeeded, we know it emplaced a run command at the back of
2663   // the command_list_.
2664   auto cmd = command_list_.back().get();
2665 
2666   // We also know this command must derive from PipelineCommand, as it is
2667   // runnable.
2668   auto pipeline_cmd = static_cast<PipelineCommand*>(cmd);
2669   auto pipeline = pipeline_cmd->GetPipeline();
2670 
2671   auto dbg = debug::Script::Create();
2672   for (auto token = tokenizer_->NextToken();; token = tokenizer_->NextToken()) {
2673     if (token->IsEOL())
2674       continue;
2675     if (token->IsEOS())
2676       return Result("missing DEBUG END command");
2677     if (token->IsIdentifier() && token->AsString() == "END")
2678       break;
2679 
2680     if (token->AsString() == "THREAD") {
2681       res = ParseDebugThread(dbg.get(), pipeline);
2682       if (!res.IsSuccess()) {
2683         return res;
2684       }
2685     } else {
2686       return Result("invalid token in DEBUG command: " + token->AsString());
2687     }
2688   }
2689 
2690   cmd->SetDebugScript(std::move(dbg));
2691 
2692   return Result();
2693 }
2694 
ParseDebugThread(debug::Events * dbg,Pipeline * pipeline)2695 Result Parser::ParseDebugThread(debug::Events* dbg, Pipeline* pipeline) {
2696   auto token = tokenizer_->NextToken();
2697   if (token->AsString() == "GLOBAL_INVOCATION_ID") {
2698     for (auto& shader : pipeline->GetShaders()) {
2699       shader.SetEmitDebugInfo(true);
2700     }
2701 
2702     uint32_t invocation[3] = {};
2703     for (int i = 0; i < 3; i++) {
2704       token = tokenizer_->NextToken();
2705       if (!token->IsInteger())
2706         return Result("expected invocation index");
2707       invocation[i] = token->AsUint32();
2708     }
2709 
2710     auto thread = debug::ThreadScript::Create();
2711     auto result = ParseDebugThreadBody(thread.get());
2712     if (!result.IsSuccess()) {
2713       return result;
2714     }
2715 
2716     dbg->BreakOnComputeGlobalInvocation(invocation[0], invocation[1],
2717                                         invocation[2], thread);
2718   } else if (token->AsString() == "VERTEX_INDEX") {
2719     for (auto& shader : pipeline->GetShaders()) {
2720       if (shader.GetShaderType() == kShaderTypeVertex) {
2721         shader.SetEmitDebugInfo(true);
2722       }
2723     }
2724 
2725     token = tokenizer_->NextToken();
2726     if (!token->IsInteger())
2727       return Result("expected vertex index");
2728     auto vertex_index = token->AsUint32();
2729 
2730     auto thread = debug::ThreadScript::Create();
2731     auto result = ParseDebugThreadBody(thread.get());
2732     if (!result.IsSuccess()) {
2733       return result;
2734     }
2735 
2736     dbg->BreakOnVertexIndex(vertex_index, thread);
2737   } else if (token->AsString() == "FRAGMENT_WINDOW_SPACE_POSITION") {
2738     for (auto& shader : pipeline->GetShaders()) {
2739       if (shader.GetShaderType() == kShaderTypeFragment) {
2740         shader.SetEmitDebugInfo(true);
2741       }
2742     }
2743 
2744     token = tokenizer_->NextToken();
2745     if (!token->IsInteger())
2746       return Result("expected x unsigned integer coordinate");
2747     auto x = token->AsUint32();
2748 
2749     token = tokenizer_->NextToken();
2750     if (!token->IsInteger())
2751       return Result("expected y unsigned integer coordinate");
2752     auto y = token->AsUint32();
2753 
2754     auto thread = debug::ThreadScript::Create();
2755     auto result = ParseDebugThreadBody(thread.get());
2756     if (!result.IsSuccess()) {
2757       return result;
2758     }
2759 
2760     dbg->BreakOnFragmentWindowSpacePosition(x, y, thread);
2761   } else {
2762     return Result("expected GLOBAL_INVOCATION_ID or VERTEX_INDEX");
2763   }
2764 
2765   return Result();
2766 }
2767 
ParseDebugThreadBody(debug::Thread * thread)2768 Result Parser::ParseDebugThreadBody(debug::Thread* thread) {
2769   for (auto token = tokenizer_->NextToken();; token = tokenizer_->NextToken()) {
2770     if (token->IsEOL()) {
2771       continue;
2772     }
2773     if (token->IsEOS()) {
2774       return Result("missing THREAD END command");
2775     }
2776     if (token->IsIdentifier() && token->AsString() == "END") {
2777       break;
2778     }
2779 
2780     if (token->AsString() == "EXPECT") {
2781       token = tokenizer_->NextToken();
2782       if (token->AsString() == "LOCATION") {
2783         debug::Location location;
2784         token = tokenizer_->NextToken();
2785         if (!token->IsString()) {
2786           return Result("expected file name string");
2787         }
2788         location.file = token->AsString();
2789 
2790         token = tokenizer_->NextToken();
2791         if (!token->IsInteger()) {
2792           return Result("expected line number");
2793         }
2794         location.line = token->AsUint32();
2795 
2796         std::string line_source;
2797         token = tokenizer_->NextToken();
2798         if (token->IsString()) {
2799           line_source = token->AsString();
2800         }
2801 
2802         thread->ExpectLocation(location, line_source);
2803       } else if (token->AsString() == "LOCAL") {
2804         auto name = tokenizer_->NextToken();
2805         if (!name->IsString()) {
2806           return Result("expected variable name");
2807         }
2808 
2809         if (tokenizer_->NextToken()->AsString() != "EQ") {
2810           return Result("expected EQ");
2811         }
2812 
2813         auto value = tokenizer_->NextToken();
2814         if (value->IsHex() || value->IsInteger()) {
2815           thread->ExpectLocal(name->AsString(), value->AsInt64());
2816         } else if (value->IsDouble()) {
2817           thread->ExpectLocal(name->AsString(), value->AsDouble());
2818         } else if (value->IsString()) {
2819           thread->ExpectLocal(name->AsString(), value->AsString());
2820         } else {
2821           return Result("expected variable value");
2822         }
2823       } else if (token->AsString() == "CALLSTACK") {
2824         std::vector<debug::StackFrame> stack;
2825         for (auto tok = tokenizer_->NextToken(); tok->AsString() != "END";
2826              tok = tokenizer_->NextToken()) {
2827           if (tok->IsEOL()) {
2828             continue;
2829           }
2830           debug::StackFrame frame;
2831           if (!tok->IsString()) {
2832             return Result("expected stack frame name");
2833           }
2834           frame.name = tok->AsString();
2835 
2836           tok = tokenizer_->NextToken();
2837           if (tok->IsString()) {
2838             frame.location.file = tok->AsString();
2839             tok = tokenizer_->NextToken();
2840             if (tok->IsInteger()) {
2841               frame.location.line = tok->AsUint32();
2842             } else if (!tok->IsEOL()) {
2843               return Result(
2844                   "expected end of line or stack frame location number");
2845             }
2846           } else if (!tok->IsEOL()) {
2847             return Result(
2848                 "expected end of line or stack frame location file name");
2849           }
2850 
2851           stack.emplace_back(frame);
2852         }
2853         thread->ExpectCallstack(stack);
2854       } else {
2855         return Result("expected LOCATION or LOCAL");
2856       }
2857     } else if (token->AsString() == "STEP_IN") {
2858       thread->StepIn();
2859     } else if (token->AsString() == "STEP_OUT") {
2860       thread->StepOut();
2861     } else if (token->AsString() == "STEP_OVER") {
2862       thread->StepOver();
2863     } else if (token->AsString() == "CONTINUE") {
2864       thread->Continue();
2865     } else {
2866       return Result("invalid token in THREAD block: " + token->AsString());
2867     }
2868   }
2869   return Result();
2870 }
2871 
ParseClear()2872 Result Parser::ParseClear() {
2873   auto token = tokenizer_->NextToken();
2874   if (!token->IsIdentifier())
2875     return Result("missing pipeline name for CLEAR command");
2876 
2877   size_t line = tokenizer_->GetCurrentLine();
2878 
2879   auto* pipeline = script_->GetPipeline(token->AsString());
2880   if (!pipeline)
2881     return Result("unknown pipeline for CLEAR command: " + token->AsString());
2882   if (!pipeline->IsGraphics())
2883     return Result("CLEAR command requires graphics pipeline");
2884 
2885   auto cmd = MakeUnique<ClearCommand>(pipeline);
2886   cmd->SetLine(line);
2887   command_list_.push_back(std::move(cmd));
2888 
2889   return ValidateEndOfStatement("CLEAR command");
2890 }
2891 
ParseValues(const std::string & name,Format * fmt,std::vector<Value> * values)2892 Result Parser::ParseValues(const std::string& name,
2893                            Format* fmt,
2894                            std::vector<Value>* values) {
2895   assert(values);
2896 
2897   auto token = tokenizer_->NextToken();
2898   const auto& segs = fmt->GetSegments();
2899   size_t seg_idx = 0;
2900   while (!token->IsEOL() && !token->IsEOS()) {
2901     Value v;
2902 
2903     while (segs[seg_idx].IsPadding()) {
2904       ++seg_idx;
2905       if (seg_idx >= segs.size())
2906         seg_idx = 0;
2907     }
2908 
2909     if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
2910       if (!token->IsInteger() && !token->IsDouble() && !token->IsHex()) {
2911         return Result(std::string("Invalid value provided to ") + name +
2912                       " command: " + token->ToOriginalString());
2913       }
2914 
2915       Result r = token->ConvertToDouble();
2916       if (!r.IsSuccess())
2917         return r;
2918 
2919       v.SetDoubleValue(token->AsDouble());
2920     } else {
2921       if (!token->IsInteger() && !token->IsHex()) {
2922         return Result(std::string("Invalid value provided to ") + name +
2923                       " command: " + token->ToOriginalString());
2924       }
2925 
2926       uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
2927       v.SetIntValue(val);
2928     }
2929     ++seg_idx;
2930     if (seg_idx >= segs.size())
2931       seg_idx = 0;
2932 
2933     values->push_back(v);
2934     token = tokenizer_->NextToken();
2935   }
2936   return {};
2937 }
2938 
ParseExpect()2939 Result Parser::ParseExpect() {
2940   auto token = tokenizer_->NextToken();
2941   if (!token->IsIdentifier())
2942     return Result("invalid buffer name in EXPECT command");
2943 
2944   if (token->AsString() == "IDX")
2945     return Result("missing buffer name between EXPECT and IDX");
2946   if (token->AsString() == "EQ_BUFFER")
2947     return Result("missing buffer name between EXPECT and EQ_BUFFER");
2948   if (token->AsString() == "RMSE_BUFFER")
2949     return Result("missing buffer name between EXPECT and RMSE_BUFFER");
2950   if (token->AsString() == "EQ_HISTOGRAM_EMD_BUFFER") {
2951     return Result(
2952         "missing buffer name between EXPECT and EQ_HISTOGRAM_EMD_BUFFER");
2953   }
2954 
2955   size_t line = tokenizer_->GetCurrentLine();
2956   auto* buffer = script_->GetBuffer(token->AsString());
2957   if (!buffer)
2958     return Result("unknown buffer name for EXPECT command: " +
2959                   token->AsString());
2960 
2961   token = tokenizer_->NextToken();
2962 
2963   if (!token->IsIdentifier())
2964     return Result("invalid comparator in EXPECT command");
2965 
2966   if (token->AsString() == "EQ_BUFFER" || token->AsString() == "RMSE_BUFFER" ||
2967       token->AsString() == "EQ_HISTOGRAM_EMD_BUFFER") {
2968     auto type = token->AsString();
2969 
2970     token = tokenizer_->NextToken();
2971     if (!token->IsIdentifier())
2972       return Result("invalid buffer name in EXPECT " + type + " command");
2973 
2974     auto* buffer_2 = script_->GetBuffer(token->AsString());
2975     if (!buffer_2) {
2976       return Result("unknown buffer name for EXPECT " + type +
2977                     " command: " + token->AsString());
2978     }
2979 
2980     if (!buffer->GetFormat()->Equal(buffer_2->GetFormat())) {
2981       return Result("EXPECT " + type +
2982                     " command cannot compare buffers of differing format");
2983     }
2984     if (buffer->ElementCount() != buffer_2->ElementCount()) {
2985       return Result("EXPECT " + type +
2986                     " command cannot compare buffers of different size: " +
2987                     std::to_string(buffer->ElementCount()) + " vs " +
2988                     std::to_string(buffer_2->ElementCount()));
2989     }
2990     if (buffer->GetWidth() != buffer_2->GetWidth()) {
2991       return Result("EXPECT " + type +
2992                     " command cannot compare buffers of different width");
2993     }
2994     if (buffer->GetHeight() != buffer_2->GetHeight()) {
2995       return Result("EXPECT " + type +
2996                     " command cannot compare buffers of different height");
2997     }
2998 
2999     auto cmd = MakeUnique<CompareBufferCommand>(buffer, buffer_2);
3000     if (type == "RMSE_BUFFER") {
3001       cmd->SetComparator(CompareBufferCommand::Comparator::kRmse);
3002 
3003       token = tokenizer_->NextToken();
3004       if (!token->IsIdentifier() && token->AsString() == "TOLERANCE")
3005         return Result("missing TOLERANCE for EXPECT RMSE_BUFFER");
3006 
3007       token = tokenizer_->NextToken();
3008       if (!token->IsInteger() && !token->IsDouble())
3009         return Result("invalid TOLERANCE for EXPECT RMSE_BUFFER");
3010 
3011       Result r = token->ConvertToDouble();
3012       if (!r.IsSuccess())
3013         return r;
3014 
3015       cmd->SetTolerance(token->AsFloat());
3016     } else if (type == "EQ_HISTOGRAM_EMD_BUFFER") {
3017       cmd->SetComparator(CompareBufferCommand::Comparator::kHistogramEmd);
3018 
3019       token = tokenizer_->NextToken();
3020       if (!token->IsIdentifier() && token->AsString() == "TOLERANCE")
3021         return Result("missing TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
3022 
3023       token = tokenizer_->NextToken();
3024       if (!token->IsInteger() && !token->IsDouble())
3025         return Result("invalid TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
3026 
3027       Result r = token->ConvertToDouble();
3028       if (!r.IsSuccess())
3029         return r;
3030 
3031       cmd->SetTolerance(token->AsFloat());
3032     }
3033 
3034     command_list_.push_back(std::move(cmd));
3035 
3036     // Early return
3037     return ValidateEndOfStatement("EXPECT " + type + " command");
3038   }
3039 
3040   if (token->AsString() != "IDX")
3041     return Result("missing IDX in EXPECT command");
3042 
3043   token = tokenizer_->NextToken();
3044   if (!token->IsInteger() || token->AsInt32() < 0)
3045     return Result("invalid X value in EXPECT command");
3046   token->ConvertToDouble();
3047   float x = token->AsFloat();
3048 
3049   bool has_y_val = false;
3050   float y = 0;
3051   token = tokenizer_->NextToken();
3052   if (token->IsInteger()) {
3053     has_y_val = true;
3054 
3055     if (token->AsInt32() < 0)
3056       return Result("invalid Y value in EXPECT command");
3057     token->ConvertToDouble();
3058     y = token->AsFloat();
3059 
3060     token = tokenizer_->NextToken();
3061   }
3062 
3063   if (token->IsIdentifier() && token->AsString() == "SIZE") {
3064     if (!has_y_val)
3065       return Result("invalid Y value in EXPECT command");
3066 
3067     auto probe = MakeUnique<ProbeCommand>(buffer);
3068     probe->SetLine(line);
3069     probe->SetX(x);
3070     probe->SetY(y);
3071     probe->SetProbeRect();
3072 
3073     token = tokenizer_->NextToken();
3074     if (!token->IsInteger() || token->AsInt32() <= 0)
3075       return Result("invalid width in EXPECT command");
3076     token->ConvertToDouble();
3077     probe->SetWidth(token->AsFloat());
3078 
3079     token = tokenizer_->NextToken();
3080     if (!token->IsInteger() || token->AsInt32() <= 0)
3081       return Result("invalid height in EXPECT command");
3082     token->ConvertToDouble();
3083     probe->SetHeight(token->AsFloat());
3084 
3085     token = tokenizer_->NextToken();
3086     if (!token->IsIdentifier()) {
3087       return Result("invalid token in EXPECT command:" +
3088                     token->ToOriginalString());
3089     }
3090 
3091     if (token->AsString() == "EQ_RGBA") {
3092       probe->SetIsRGBA();
3093     } else if (token->AsString() != "EQ_RGB") {
3094       return Result("unknown comparator type in EXPECT: " +
3095                     token->ToOriginalString());
3096     }
3097 
3098     token = tokenizer_->NextToken();
3099     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3100       return Result("invalid R value in EXPECT command");
3101     token->ConvertToDouble();
3102     probe->SetR(token->AsFloat() / 255.f);
3103 
3104     token = tokenizer_->NextToken();
3105     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3106       return Result("invalid G value in EXPECT command");
3107     token->ConvertToDouble();
3108     probe->SetG(token->AsFloat() / 255.f);
3109 
3110     token = tokenizer_->NextToken();
3111     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3112       return Result("invalid B value in EXPECT command");
3113     token->ConvertToDouble();
3114     probe->SetB(token->AsFloat() / 255.f);
3115 
3116     if (probe->IsRGBA()) {
3117       token = tokenizer_->NextToken();
3118       if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3119         return Result("invalid A value in EXPECT command");
3120       token->ConvertToDouble();
3121       probe->SetA(token->AsFloat() / 255.f);
3122     }
3123 
3124     token = tokenizer_->NextToken();
3125     if (token->IsIdentifier() && token->AsString() == "TOLERANCE") {
3126       std::vector<Probe::Tolerance> tolerances;
3127 
3128       Result r = ParseTolerances(&tolerances);
3129 
3130       if (!r.IsSuccess())
3131         return r;
3132 
3133       if (tolerances.empty())
3134         return Result("TOLERANCE specified but no tolerances provided");
3135 
3136       if (!probe->IsRGBA() && tolerances.size() > 3) {
3137         return Result(
3138             "TOLERANCE for an RGB comparison has a maximum of 3 values");
3139       }
3140 
3141       if (tolerances.size() > 4) {
3142         return Result(
3143             "TOLERANCE for an RGBA comparison has a maximum of 4 values");
3144       }
3145 
3146       probe->SetTolerances(std::move(tolerances));
3147       token = tokenizer_->NextToken();
3148     }
3149 
3150     if (!token->IsEOL() && !token->IsEOS()) {
3151       return Result("extra parameters after EXPECT command: " +
3152                     token->ToOriginalString());
3153     }
3154 
3155     command_list_.push_back(std::move(probe));
3156 
3157     return {};
3158   }
3159 
3160   auto probe = MakeUnique<ProbeSSBOCommand>(buffer);
3161   probe->SetLine(line);
3162 
3163   if (token->IsIdentifier() && token->AsString() == "TOLERANCE") {
3164     std::vector<Probe::Tolerance> tolerances;
3165 
3166     Result r = ParseTolerances(&tolerances);
3167 
3168     if (!r.IsSuccess())
3169       return r;
3170 
3171     if (tolerances.empty())
3172       return Result("TOLERANCE specified but no tolerances provided");
3173     if (tolerances.size() > 4)
3174       return Result("TOLERANCE has a maximum of 4 values");
3175 
3176     probe->SetTolerances(std::move(tolerances));
3177     token = tokenizer_->NextToken();
3178   }
3179 
3180   if (!token->IsIdentifier() || !IsComparator(token->AsString())) {
3181     return Result("unexpected token in EXPECT command: " +
3182                   token->ToOriginalString());
3183   }
3184 
3185   if (has_y_val)
3186     return Result("Y value not needed for non-color comparator");
3187 
3188   auto cmp = ToComparator(token->AsString());
3189   if (probe->HasTolerances()) {
3190     if (cmp != ProbeSSBOCommand::Comparator::kEqual)
3191       return Result("TOLERANCE only available with EQ probes");
3192 
3193     cmp = ProbeSSBOCommand::Comparator::kFuzzyEqual;
3194   }
3195 
3196   probe->SetComparator(cmp);
3197   probe->SetFormat(buffer->GetFormat());
3198   probe->SetOffset(static_cast<uint32_t>(x));
3199 
3200   std::vector<Value> values;
3201   Result r = ParseValues("EXPECT", buffer->GetFormat(), &values);
3202   if (!r.IsSuccess())
3203     return r;
3204 
3205   if (values.empty())
3206     return Result("missing comparison values for EXPECT command");
3207 
3208   probe->SetValues(std::move(values));
3209   command_list_.push_back(std::move(probe));
3210 
3211   return {};
3212 }
3213 
ParseCopy()3214 Result Parser::ParseCopy() {
3215   auto token = tokenizer_->NextToken();
3216   if (token->IsEOL() || token->IsEOS())
3217     return Result("missing buffer name after COPY");
3218   if (!token->IsIdentifier())
3219     return Result("invalid buffer name after COPY");
3220 
3221   size_t line = tokenizer_->GetCurrentLine();
3222 
3223   auto name = token->AsString();
3224   if (name == "TO")
3225     return Result("missing buffer name between COPY and TO");
3226 
3227   Buffer* buffer_from = script_->GetBuffer(name);
3228   if (!buffer_from)
3229     return Result("COPY origin buffer was not declared");
3230 
3231   token = tokenizer_->NextToken();
3232   if (token->IsEOL() || token->IsEOS())
3233     return Result("missing 'TO' after COPY and buffer name");
3234   if (!token->IsIdentifier())
3235     return Result("expected 'TO' after COPY and buffer name");
3236 
3237   name = token->AsString();
3238   if (name != "TO")
3239     return Result("expected 'TO' after COPY and buffer name");
3240 
3241   token = tokenizer_->NextToken();
3242   if (token->IsEOL() || token->IsEOS())
3243     return Result("missing buffer name after TO");
3244   if (!token->IsIdentifier())
3245     return Result("invalid buffer name after TO");
3246 
3247   name = token->AsString();
3248   Buffer* buffer_to = script_->GetBuffer(name);
3249   if (!buffer_to)
3250     return Result("COPY destination buffer was not declared");
3251 
3252   // Set destination buffer to mirror origin buffer
3253   buffer_to->SetWidth(buffer_from->GetWidth());
3254   buffer_to->SetHeight(buffer_from->GetHeight());
3255   buffer_to->SetElementCount(buffer_from->ElementCount());
3256 
3257   if (buffer_from == buffer_to)
3258     return Result("COPY origin and destination buffers are identical");
3259 
3260   auto cmd = MakeUnique<CopyCommand>(buffer_from, buffer_to);
3261   cmd->SetLine(line);
3262   command_list_.push_back(std::move(cmd));
3263 
3264   return ValidateEndOfStatement("COPY command");
3265 }
3266 
ParseClearColor()3267 Result Parser::ParseClearColor() {
3268   auto token = tokenizer_->NextToken();
3269   if (!token->IsIdentifier())
3270     return Result("missing pipeline name for CLEAR_COLOR command");
3271 
3272   size_t line = tokenizer_->GetCurrentLine();
3273 
3274   auto* pipeline = script_->GetPipeline(token->AsString());
3275   if (!pipeline) {
3276     return Result("unknown pipeline for CLEAR_COLOR command: " +
3277                   token->AsString());
3278   }
3279   if (!pipeline->IsGraphics()) {
3280     return Result("CLEAR_COLOR command requires graphics pipeline");
3281   }
3282 
3283   auto cmd = MakeUnique<ClearColorCommand>(pipeline);
3284   cmd->SetLine(line);
3285 
3286   token = tokenizer_->NextToken();
3287   if (token->IsEOL() || token->IsEOS())
3288     return Result("missing R value for CLEAR_COLOR command");
3289   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3290     return Result("invalid R value for CLEAR_COLOR command: " +
3291                   token->ToOriginalString());
3292   }
3293   token->ConvertToDouble();
3294   cmd->SetR(token->AsFloat() / 255.f);
3295 
3296   token = tokenizer_->NextToken();
3297   if (token->IsEOL() || token->IsEOS())
3298     return Result("missing G value for CLEAR_COLOR command");
3299   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3300     return Result("invalid G value for CLEAR_COLOR command: " +
3301                   token->ToOriginalString());
3302   }
3303   token->ConvertToDouble();
3304   cmd->SetG(token->AsFloat() / 255.f);
3305 
3306   token = tokenizer_->NextToken();
3307   if (token->IsEOL() || token->IsEOS())
3308     return Result("missing B value for CLEAR_COLOR command");
3309   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3310     return Result("invalid B value for CLEAR_COLOR command: " +
3311                   token->ToOriginalString());
3312   }
3313   token->ConvertToDouble();
3314   cmd->SetB(token->AsFloat() / 255.f);
3315 
3316   token = tokenizer_->NextToken();
3317   if (token->IsEOL() || token->IsEOS())
3318     return Result("missing A value for CLEAR_COLOR command");
3319   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3320     return Result("invalid A value for CLEAR_COLOR command: " +
3321                   token->ToOriginalString());
3322   }
3323   token->ConvertToDouble();
3324   cmd->SetA(token->AsFloat() / 255.f);
3325 
3326   command_list_.push_back(std::move(cmd));
3327   return ValidateEndOfStatement("CLEAR_COLOR command");
3328 }
3329 
ParseClearDepth()3330 Result Parser::ParseClearDepth() {
3331   auto token = tokenizer_->NextToken();
3332   if (!token->IsIdentifier())
3333     return Result("missing pipeline name for CLEAR_DEPTH command");
3334 
3335   size_t line = tokenizer_->GetCurrentLine();
3336 
3337   auto* pipeline = script_->GetPipeline(token->AsString());
3338   if (!pipeline) {
3339     return Result("unknown pipeline for CLEAR_DEPTH command: " +
3340                   token->AsString());
3341   }
3342   if (!pipeline->IsGraphics()) {
3343     return Result("CLEAR_DEPTH command requires graphics pipeline");
3344   }
3345 
3346   auto cmd = MakeUnique<ClearDepthCommand>(pipeline);
3347   cmd->SetLine(line);
3348 
3349   token = tokenizer_->NextToken();
3350   if (token->IsEOL() || token->IsEOS())
3351     return Result("missing value for CLEAR_DEPTH command");
3352   if (!token->IsDouble()) {
3353     return Result("invalid value for CLEAR_DEPTH command: " +
3354                   token->ToOriginalString());
3355   }
3356   cmd->SetValue(token->AsFloat());
3357 
3358   command_list_.push_back(std::move(cmd));
3359   return ValidateEndOfStatement("CLEAR_DEPTH command");
3360 }
3361 
ParseClearStencil()3362 Result Parser::ParseClearStencil() {
3363   auto token = tokenizer_->NextToken();
3364   if (!token->IsIdentifier())
3365     return Result("missing pipeline name for CLEAR_STENCIL command");
3366 
3367   size_t line = tokenizer_->GetCurrentLine();
3368 
3369   auto* pipeline = script_->GetPipeline(token->AsString());
3370   if (!pipeline) {
3371     return Result("unknown pipeline for CLEAR_STENCIL command: " +
3372                   token->AsString());
3373   }
3374   if (!pipeline->IsGraphics()) {
3375     return Result("CLEAR_STENCIL command requires graphics pipeline");
3376   }
3377 
3378   auto cmd = MakeUnique<ClearStencilCommand>(pipeline);
3379   cmd->SetLine(line);
3380 
3381   token = tokenizer_->NextToken();
3382   if (token->IsEOL() || token->IsEOS())
3383     return Result("missing value for CLEAR_STENCIL command");
3384   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3385     return Result("invalid value for CLEAR_STENCIL command: " +
3386                   token->ToOriginalString());
3387   }
3388   cmd->SetValue(token->AsUint32());
3389 
3390   command_list_.push_back(std::move(cmd));
3391   return ValidateEndOfStatement("CLEAR_STENCIL command");
3392 }
3393 
ParseDeviceFeature()3394 Result Parser::ParseDeviceFeature() {
3395   auto token = tokenizer_->NextToken();
3396   if (token->IsEOS() || token->IsEOL())
3397     return Result("missing feature name for DEVICE_FEATURE command");
3398   if (!token->IsIdentifier())
3399     return Result("invalid feature name for DEVICE_FEATURE command");
3400   if (!script_->IsKnownFeature(token->AsString()))
3401     return Result("unknown feature name for DEVICE_FEATURE command");
3402 
3403   script_->AddRequiredFeature(token->AsString());
3404 
3405   return ValidateEndOfStatement("DEVICE_FEATURE command");
3406 }
3407 
ParseRepeat()3408 Result Parser::ParseRepeat() {
3409   auto token = tokenizer_->NextToken();
3410   if (token->IsEOL() || token->IsEOL())
3411     return Result("missing count parameter for REPEAT command");
3412   if (!token->IsInteger()) {
3413     return Result("invalid count parameter for REPEAT command: " +
3414                   token->ToOriginalString());
3415   }
3416   if (token->AsInt32() <= 0)
3417     return Result("count parameter must be > 0 for REPEAT command");
3418 
3419   uint32_t count = token->AsUint32();
3420 
3421   std::vector<std::unique_ptr<Command>> cur_commands;
3422   std::swap(cur_commands, command_list_);
3423 
3424   for (token = tokenizer_->NextToken(); !token->IsEOS();
3425        token = tokenizer_->NextToken()) {
3426     if (token->IsEOL())
3427       continue;
3428     if (!token->IsIdentifier())
3429       return Result("expected identifier");
3430 
3431     std::string tok = token->AsString();
3432     if (tok == "END")
3433       break;
3434     if (!IsRepeatable(tok))
3435       return Result("unknown token: " + tok);
3436 
3437     Result r = ParseRepeatableCommand(tok);
3438     if (!r.IsSuccess())
3439       return r;
3440   }
3441   if (!token->IsIdentifier() || token->AsString() != "END")
3442     return Result("missing END for REPEAT command");
3443 
3444   auto cmd = MakeUnique<RepeatCommand>(count);
3445   cmd->SetCommands(std::move(command_list_));
3446 
3447   std::swap(cur_commands, command_list_);
3448   command_list_.push_back(std::move(cmd));
3449 
3450   return ValidateEndOfStatement("REPEAT command");
3451 }
3452 
ParseDerivePipelineBlock()3453 Result Parser::ParseDerivePipelineBlock() {
3454   auto token = tokenizer_->NextToken();
3455   if (!token->IsIdentifier() || token->AsString() == "FROM")
3456     return Result("missing pipeline name for DERIVE_PIPELINE command");
3457 
3458   std::string name = token->AsString();
3459   if (script_->GetPipeline(name) != nullptr)
3460     return Result("duplicate pipeline name for DERIVE_PIPELINE command");
3461 
3462   token = tokenizer_->NextToken();
3463   if (!token->IsIdentifier() || token->AsString() != "FROM")
3464     return Result("missing FROM in DERIVE_PIPELINE command");
3465 
3466   token = tokenizer_->NextToken();
3467   if (!token->IsIdentifier())
3468     return Result("missing parent pipeline name in DERIVE_PIPELINE command");
3469 
3470   Pipeline* parent = script_->GetPipeline(token->AsString());
3471   if (!parent)
3472     return Result("unknown parent pipeline in DERIVE_PIPELINE command");
3473 
3474   Result r = ValidateEndOfStatement("DERIVE_PIPELINE command");
3475   if (!r.IsSuccess())
3476     return r;
3477 
3478   auto pipeline = parent->Clone();
3479   pipeline->SetName(name);
3480 
3481   return ParsePipelineBody("DERIVE_PIPELINE", std::move(pipeline));
3482 }
3483 
ParseDeviceExtension()3484 Result Parser::ParseDeviceExtension() {
3485   auto token = tokenizer_->NextToken();
3486   if (token->IsEOL() || token->IsEOS())
3487     return Result("DEVICE_EXTENSION missing name");
3488   if (!token->IsIdentifier()) {
3489     return Result("DEVICE_EXTENSION invalid name: " +
3490                   token->ToOriginalString());
3491   }
3492 
3493   script_->AddRequiredDeviceExtension(token->AsString());
3494 
3495   return ValidateEndOfStatement("DEVICE_EXTENSION command");
3496 }
3497 
ParseInstanceExtension()3498 Result Parser::ParseInstanceExtension() {
3499   auto token = tokenizer_->NextToken();
3500   if (token->IsEOL() || token->IsEOS())
3501     return Result("INSTANCE_EXTENSION missing name");
3502   if (!token->IsIdentifier()) {
3503     return Result("INSTANCE_EXTENSION invalid name: " +
3504                   token->ToOriginalString());
3505   }
3506 
3507   script_->AddRequiredInstanceExtension(token->AsString());
3508 
3509   return ValidateEndOfStatement("INSTANCE_EXTENSION command");
3510 }
3511 
ParseSet()3512 Result Parser::ParseSet() {
3513   auto token = tokenizer_->NextToken();
3514   if (!token->IsIdentifier() || token->AsString() != "ENGINE_DATA")
3515     return Result("SET missing ENGINE_DATA");
3516 
3517   token = tokenizer_->NextToken();
3518   if (token->IsEOS() || token->IsEOL())
3519     return Result("SET missing variable to be set");
3520 
3521   if (!token->IsIdentifier())
3522     return Result("SET invalid variable to set: " + token->ToOriginalString());
3523 
3524   if (token->AsString() != "fence_timeout_ms")
3525     return Result("SET unknown variable provided: " + token->AsString());
3526 
3527   token = tokenizer_->NextToken();
3528   if (token->IsEOS() || token->IsEOL())
3529     return Result("SET missing value for fence_timeout_ms");
3530   if (!token->IsInteger())
3531     return Result("SET invalid value for fence_timeout_ms, must be uint32");
3532 
3533   script_->GetEngineData().fence_timeout_ms = token->AsUint32();
3534 
3535   return ValidateEndOfStatement("SET command");
3536 }
3537 
ParseSampler()3538 Result Parser::ParseSampler() {
3539   auto token = tokenizer_->NextToken();
3540   if (!token->IsIdentifier())
3541     return Result("invalid token when looking for sampler name");
3542 
3543   auto sampler = MakeUnique<Sampler>();
3544   sampler->SetName(token->AsString());
3545 
3546   token = tokenizer_->NextToken();
3547   while (!token->IsEOS() && !token->IsEOL()) {
3548     if (!token->IsIdentifier())
3549       return Result("invalid token when looking for sampler parameters");
3550 
3551     auto param = token->AsString();
3552     if (param == "MAG_FILTER") {
3553       token = tokenizer_->NextToken();
3554 
3555       if (!token->IsIdentifier())
3556         return Result("invalid token when looking for MAG_FILTER value");
3557 
3558       auto filter = token->AsString();
3559 
3560       if (filter == "linear")
3561         sampler->SetMagFilter(FilterType::kLinear);
3562       else if (filter == "nearest")
3563         sampler->SetMagFilter(FilterType::kNearest);
3564       else
3565         return Result("invalid MAG_FILTER value " + filter);
3566     } else if (param == "MIN_FILTER") {
3567       token = tokenizer_->NextToken();
3568 
3569       if (!token->IsIdentifier())
3570         return Result("invalid token when looking for MIN_FILTER value");
3571 
3572       auto filter = token->AsString();
3573 
3574       if (filter == "linear")
3575         sampler->SetMinFilter(FilterType::kLinear);
3576       else if (filter == "nearest")
3577         sampler->SetMinFilter(FilterType::kNearest);
3578       else
3579         return Result("invalid MIN_FILTER value " + filter);
3580     } else if (param == "ADDRESS_MODE_U") {
3581       token = tokenizer_->NextToken();
3582 
3583       if (!token->IsIdentifier())
3584         return Result("invalid token when looking for ADDRESS_MODE_U value");
3585 
3586       auto mode_str = token->AsString();
3587       auto mode = StrToAddressMode(mode_str);
3588 
3589       if (mode == AddressMode::kUnknown)
3590         return Result("invalid ADDRESS_MODE_U value " + mode_str);
3591 
3592       sampler->SetAddressModeU(mode);
3593     } else if (param == "ADDRESS_MODE_V") {
3594       token = tokenizer_->NextToken();
3595 
3596       if (!token->IsIdentifier())
3597         return Result("invalid token when looking for ADDRESS_MODE_V value");
3598 
3599       auto mode_str = token->AsString();
3600       auto mode = StrToAddressMode(mode_str);
3601 
3602       if (mode == AddressMode::kUnknown)
3603         return Result("invalid ADDRESS_MODE_V value " + mode_str);
3604 
3605       sampler->SetAddressModeV(mode);
3606     } else if (param == "ADDRESS_MODE_W") {
3607       token = tokenizer_->NextToken();
3608 
3609       if (!token->IsIdentifier())
3610         return Result("invalid token when looking for ADDRESS_MODE_W value");
3611 
3612       auto mode_str = token->AsString();
3613       auto mode = StrToAddressMode(mode_str);
3614 
3615       if (mode == AddressMode::kUnknown)
3616         return Result("invalid ADDRESS_MODE_W value " + mode_str);
3617 
3618       sampler->SetAddressModeW(mode);
3619     } else if (param == "BORDER_COLOR") {
3620       token = tokenizer_->NextToken();
3621 
3622       if (!token->IsIdentifier())
3623         return Result("invalid token when looking for BORDER_COLOR value");
3624 
3625       auto color_str = token->AsString();
3626 
3627       if (color_str == "float_transparent_black")
3628         sampler->SetBorderColor(BorderColor::kFloatTransparentBlack);
3629       else if (color_str == "int_transparent_black")
3630         sampler->SetBorderColor(BorderColor::kIntTransparentBlack);
3631       else if (color_str == "float_opaque_black")
3632         sampler->SetBorderColor(BorderColor::kFloatOpaqueBlack);
3633       else if (color_str == "int_opaque_black")
3634         sampler->SetBorderColor(BorderColor::kIntOpaqueBlack);
3635       else if (color_str == "float_opaque_white")
3636         sampler->SetBorderColor(BorderColor::kFloatOpaqueWhite);
3637       else if (color_str == "int_opaque_white")
3638         sampler->SetBorderColor(BorderColor::kIntOpaqueWhite);
3639       else
3640         return Result("invalid BORDER_COLOR value " + color_str);
3641     } else if (param == "MIN_LOD") {
3642       token = tokenizer_->NextToken();
3643 
3644       if (!token->IsDouble())
3645         return Result("invalid token when looking for MIN_LOD value");
3646 
3647       sampler->SetMinLOD(token->AsFloat());
3648     } else if (param == "MAX_LOD") {
3649       token = tokenizer_->NextToken();
3650 
3651       if (!token->IsDouble())
3652         return Result("invalid token when looking for MAX_LOD value");
3653 
3654       sampler->SetMaxLOD(token->AsFloat());
3655     } else if (param == "NORMALIZED_COORDS") {
3656       sampler->SetNormalizedCoords(true);
3657     } else if (param == "UNNORMALIZED_COORDS") {
3658       sampler->SetNormalizedCoords(false);
3659       sampler->SetMinLOD(0.0f);
3660       sampler->SetMaxLOD(0.0f);
3661     } else {
3662       return Result("unexpected sampler parameter " + param);
3663     }
3664 
3665     token = tokenizer_->NextToken();
3666   }
3667 
3668   if (sampler->GetMaxLOD() < sampler->GetMinLOD()) {
3669     return Result("max LOD needs to be greater than or equal to min LOD");
3670   }
3671 
3672   return script_->AddSampler(std::move(sampler));
3673 }
3674 
ParseTolerances(std::vector<Probe::Tolerance> * tolerances)3675 Result Parser::ParseTolerances(std::vector<Probe::Tolerance>* tolerances) {
3676   auto token = tokenizer_->PeekNextToken();
3677   while (!token->IsEOL() && !token->IsEOS()) {
3678     if (!token->IsInteger() && !token->IsDouble())
3679       break;
3680 
3681     token = tokenizer_->NextToken();
3682     Result r = token->ConvertToDouble();
3683     if (!r.IsSuccess())
3684       return r;
3685 
3686     double value = token->AsDouble();
3687     token = tokenizer_->PeekNextToken();
3688     if (token->IsIdentifier() && token->AsString() == "%") {
3689       tolerances->push_back(Probe::Tolerance{true, value});
3690       tokenizer_->NextToken();
3691       token = tokenizer_->PeekNextToken();
3692     } else {
3693       tolerances->push_back(Probe::Tolerance{false, value});
3694     }
3695   }
3696 
3697   return {};
3698 }
3699 
ParseVirtualFile()3700 Result Parser::ParseVirtualFile() {
3701   auto token = tokenizer_->NextToken();
3702   if (!token->IsIdentifier() && !token->IsString())
3703     return Result("invalid virtual file path");
3704 
3705   auto path = token->AsString();
3706 
3707   auto r = ValidateEndOfStatement("VIRTUAL_FILE command");
3708   if (!r.IsSuccess())
3709     return r;
3710 
3711   auto data = tokenizer_->ExtractToNext("END");
3712 
3713   token = tokenizer_->NextToken();
3714   if (!token->IsIdentifier() || token->AsString() != "END")
3715     return Result("VIRTUAL_FILE missing END command");
3716 
3717   return script_->AddVirtualFile(path, data);
3718 }
3719 
3720 }  // namespace amberscript
3721 }  // namespace amber
3722