• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 == "VIEWPORT") {
606       r = ParsePipelineViewport(pipeline.get());
607     } else if (tok == "BIND") {
608       r = ParsePipelineBind(pipeline.get());
609     } else if (tok == "VERTEX_DATA") {
610       r = ParsePipelineVertexData(pipeline.get());
611     } else if (tok == "INDEX_DATA") {
612       r = ParsePipelineIndexData(pipeline.get());
613     } else if (tok == "SET") {
614       r = ParsePipelineSet(pipeline.get());
615     } else if (tok == "COMPILE_OPTIONS") {
616       r = ParsePipelineShaderCompileOptions(pipeline.get());
617     } else if (tok == "POLYGON_MODE") {
618       r = ParsePipelinePolygonMode(pipeline.get());
619     } else if (tok == "DEPTH") {
620       r = ParsePipelineDepth(pipeline.get());
621     } else if (tok == "STENCIL") {
622       r = ParsePipelineStencil(pipeline.get());
623     } else if (tok == "SUBGROUP") {
624       r = ParsePipelineSubgroup(pipeline.get());
625     } else if (tok == "PATCH_CONTROL_POINTS") {
626       r = ParsePipelinePatchControlPoints(pipeline.get());
627     } else if (tok == "BLEND") {
628       r = ParsePipelineBlend(pipeline.get());
629     } else {
630       r = Result("unknown token in pipeline block: " + tok);
631     }
632     if (!r.IsSuccess())
633       return r;
634   }
635 
636   if (!token->IsIdentifier() || token->AsString() != "END")
637     return Result(cmd_name + " missing END command");
638 
639   Result r = script_->AddPipeline(std::move(pipeline));
640   if (!r.IsSuccess())
641     return r;
642 
643   return ValidateEndOfStatement("END");
644 }
645 
ParsePipelineAttach(Pipeline * pipeline)646 Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
647   auto token = tokenizer_->NextToken();
648   if (!token->IsIdentifier())
649     return Result("invalid token in ATTACH command");
650 
651   auto* shader = script_->GetShader(token->AsString());
652   if (!shader)
653     return Result("unknown shader in ATTACH command");
654 
655   token = tokenizer_->NextToken();
656   if (token->IsEOL() || token->IsEOS()) {
657     if (shader->GetType() == kShaderTypeMulti)
658       return Result("multi shader ATTACH requires TYPE");
659 
660     Result r = pipeline->AddShader(shader, shader->GetType());
661     if (!r.IsSuccess())
662       return r;
663     return {};
664   }
665   if (!token->IsIdentifier())
666     return Result("invalid token after ATTACH");
667 
668   bool set_shader_type = false;
669   ShaderType shader_type = shader->GetType();
670   auto type = token->AsString();
671   if (type == "TYPE") {
672     token = tokenizer_->NextToken();
673     if (!token->IsIdentifier())
674       return Result("invalid type in ATTACH");
675 
676     Result r = ToShaderType(token->AsString(), &shader_type);
677     if (!r.IsSuccess())
678       return r;
679 
680     set_shader_type = true;
681 
682     token = tokenizer_->NextToken();
683     if (!token->IsIdentifier())
684       return Result("ATTACH TYPE requires an ENTRY_POINT");
685 
686     type = token->AsString();
687   }
688   if (set_shader_type && type != "ENTRY_POINT")
689     return Result("unknown ATTACH parameter: " + type);
690 
691   if (shader->GetType() == ShaderType::kShaderTypeMulti && !set_shader_type)
692     return Result("ATTACH missing TYPE for multi shader");
693 
694   Result r = pipeline->AddShader(shader, shader_type);
695   if (!r.IsSuccess())
696     return r;
697 
698   if (type == "ENTRY_POINT") {
699     token = tokenizer_->NextToken();
700     if (!token->IsIdentifier())
701       return Result("missing shader name in ATTACH ENTRY_POINT command");
702 
703     r = pipeline->SetShaderEntryPoint(shader, token->AsString());
704     if (!r.IsSuccess())
705       return r;
706 
707     token = tokenizer_->NextToken();
708   }
709 
710   while (true) {
711     if (token->IsIdentifier() && token->AsString() == "SPECIALIZE") {
712       r = ParseShaderSpecialization(pipeline);
713       if (!r.IsSuccess())
714         return r;
715 
716       token = tokenizer_->NextToken();
717     } else {
718       if (token->IsEOL() || token->IsEOS())
719         return {};
720       if (token->IsIdentifier())
721         return Result("unknown ATTACH parameter: " + token->AsString());
722       return Result("extra parameters after ATTACH command: " +
723                     token->ToOriginalString());
724     }
725   }
726 }
727 
ParseShaderSpecialization(Pipeline * pipeline)728 Result Parser::ParseShaderSpecialization(Pipeline* pipeline) {
729   auto token = tokenizer_->NextToken();
730   if (!token->IsInteger())
731     return Result("specialization ID must be an integer");
732 
733   auto spec_id = token->AsUint32();
734 
735   token = tokenizer_->NextToken();
736   if (!token->IsIdentifier() || token->AsString() != "AS")
737     return Result("expected AS as next token");
738 
739   token = tokenizer_->NextToken();
740   if (!token->IsIdentifier())
741     return Result("expected data type in SPECIALIZE subcommand");
742 
743   auto type = ToType(token->AsString());
744   if (!type)
745     return Result("invalid data type '" + token->AsString() + "' provided");
746   if (!type->IsNumber())
747     return Result("only numeric types are accepted for specialization values");
748 
749   auto num = type->AsNumber();
750 
751   token = tokenizer_->NextToken();
752   uint32_t value = 0;
753   if (type::Type::IsUint32(num->GetFormatMode(), num->NumBits()) ||
754       type::Type::IsInt32(num->GetFormatMode(), num->NumBits())) {
755     value = token->AsUint32();
756   } else if (type::Type::IsFloat32(num->GetFormatMode(), num->NumBits())) {
757     Result r = token->ConvertToDouble();
758     if (!r.IsSuccess())
759       return Result("value is not a floating point value");
760 
761     union {
762       uint32_t u;
763       float f;
764     } u;
765     u.f = token->AsFloat();
766     value = u.u;
767   } else {
768     return Result(
769         "only 32-bit types are currently accepted for specialization values");
770   }
771 
772   auto& shader = pipeline->GetShaders()[pipeline->GetShaders().size() - 1];
773   shader.AddSpecialization(spec_id, value);
774   return {};
775 }
776 
ParsePipelineShaderOptimizations(Pipeline * pipeline)777 Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) {
778   auto token = tokenizer_->NextToken();
779   if (!token->IsIdentifier())
780     return Result("missing shader name in SHADER_OPTIMIZATION command");
781 
782   auto* shader = script_->GetShader(token->AsString());
783   if (!shader)
784     return Result("unknown shader in SHADER_OPTIMIZATION command");
785 
786   token = tokenizer_->NextToken();
787   if (!token->IsEOL())
788     return Result("extra parameters after SHADER_OPTIMIZATION command: " +
789                   token->ToOriginalString());
790 
791   std::vector<std::string> optimizations;
792   while (true) {
793     token = tokenizer_->NextToken();
794     if (token->IsEOL())
795       continue;
796     if (token->IsEOS())
797       return Result("SHADER_OPTIMIZATION missing END command");
798     if (!token->IsIdentifier())
799       return Result("SHADER_OPTIMIZATION options must be identifiers");
800     if (token->AsString() == "END")
801       break;
802 
803     optimizations.push_back(token->AsString());
804   }
805 
806   Result r = pipeline->SetShaderOptimizations(shader, optimizations);
807   if (!r.IsSuccess())
808     return r;
809 
810   return ValidateEndOfStatement("SHADER_OPTIMIZATION command");
811 }
812 
ParsePipelineShaderCompileOptions(Pipeline * pipeline)813 Result Parser::ParsePipelineShaderCompileOptions(Pipeline* pipeline) {
814   auto token = tokenizer_->NextToken();
815   if (!token->IsIdentifier())
816     return Result("missing shader name in COMPILE_OPTIONS command");
817 
818   auto* shader = script_->GetShader(token->AsString());
819   if (!shader)
820     return Result("unknown shader in COMPILE_OPTIONS command");
821 
822   if (shader->GetFormat() != kShaderFormatOpenCLC) {
823     return Result("COMPILE_OPTIONS currently only supports OPENCL-C shaders");
824   }
825 
826   token = tokenizer_->NextToken();
827   if (!token->IsEOL())
828     return Result("extra parameters after COMPILE_OPTIONS command: " +
829                   token->ToOriginalString());
830 
831   std::vector<std::string> options;
832   while (true) {
833     token = tokenizer_->NextToken();
834     if (token->IsEOL())
835       continue;
836     if (token->IsEOS())
837       return Result("COMPILE_OPTIONS missing END command");
838     if (token->AsString() == "END")
839       break;
840 
841     options.push_back(token->AsString());
842   }
843 
844   Result r = pipeline->SetShaderCompileOptions(shader, options);
845   if (!r.IsSuccess())
846     return r;
847 
848   return ValidateEndOfStatement("COMPILE_OPTIONS command");
849 }
850 
ParsePipelineSubgroup(Pipeline * pipeline)851 Result Parser::ParsePipelineSubgroup(Pipeline* pipeline) {
852   auto token = tokenizer_->NextToken();
853   if (!token->IsIdentifier())
854     return Result("missing shader name in SUBGROUP command");
855 
856   auto* shader = script_->GetShader(token->AsString());
857   if (!shader)
858     return Result("unknown shader in SUBGROUP command");
859 
860   while (true) {
861     token = tokenizer_->NextToken();
862     if (token->IsEOL())
863       continue;
864     if (token->IsEOS())
865       return Result("SUBGROUP missing END command");
866     if (!token->IsIdentifier())
867       return Result("SUBGROUP options must be identifiers");
868     if (token->AsString() == "END")
869       break;
870 
871     if (token->AsString() == "FULLY_POPULATED") {
872       if (!script_->IsRequiredFeature(
873               "SubgroupSizeControl.computeFullSubgroups"))
874         return Result(
875             "missing DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups");
876       token = tokenizer_->NextToken();
877       if (token->IsEOL() || token->IsEOS())
878         return Result("missing value for FULLY_POPULATED command");
879       bool isOn = false;
880       if (token->AsString() == "on") {
881         isOn = true;
882       } else if (token->AsString() == "off") {
883         isOn = false;
884       } else {
885         return Result("invalid value for FULLY_POPULATED command");
886       }
887       Result r = pipeline->SetShaderRequireFullSubgroups(shader, isOn);
888       if (!r.IsSuccess())
889         return r;
890 
891     } else if (token->AsString() == "VARYING_SIZE") {
892       if (!script_->IsRequiredFeature(
893               "SubgroupSizeControl.subgroupSizeControl"))
894         return Result(
895             "missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl");
896       token = tokenizer_->NextToken();
897       if (token->IsEOL() || token->IsEOS())
898         return Result("missing value for VARYING_SIZE command");
899       bool isOn = false;
900       if (token->AsString() == "on") {
901         isOn = true;
902       } else if (token->AsString() == "off") {
903         isOn = false;
904       } else {
905         return Result("invalid value for VARYING_SIZE command");
906       }
907       Result r = pipeline->SetShaderVaryingSubgroupSize(shader, isOn);
908       if (!r.IsSuccess())
909         return r;
910     } else if (token->AsString() == "REQUIRED_SIZE") {
911       if (!script_->IsRequiredFeature(
912               "SubgroupSizeControl.subgroupSizeControl"))
913         return Result(
914             "missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl");
915       token = tokenizer_->NextToken();
916       if (token->IsEOL() || token->IsEOS())
917         return Result("missing size for REQUIRED_SIZE command");
918       Result r;
919       if (token->IsInteger()) {
920         r = pipeline->SetShaderRequiredSubgroupSize(shader, token->AsUint32());
921       } else if (token->AsString() == "MIN") {
922         r = pipeline->SetShaderRequiredSubgroupSizeToMinimum(shader);
923       } else if (token->AsString() == "MAX") {
924         r = pipeline->SetShaderRequiredSubgroupSizeToMaximum(shader);
925       } else {
926         return Result("invalid size for REQUIRED_SIZE command");
927       }
928       if (!r.IsSuccess())
929         return r;
930     } else {
931       return Result("SUBGROUP invalid value for SUBGROUP " + token->AsString());
932     }
933   }
934 
935   return ValidateEndOfStatement("SUBGROUP command");
936 }
937 
ParsePipelinePatchControlPoints(Pipeline * pipeline)938 Result Parser::ParsePipelinePatchControlPoints(Pipeline* pipeline) {
939   auto token = tokenizer_->NextToken();
940   if (token->IsEOL() || token->IsEOS())
941     return Result(
942         "missing number of control points in PATCH_CONTROL_POINTS command");
943 
944   if (!token->IsInteger())
945     return Result("expecting integer for the number of control points");
946 
947   pipeline->GetPipelineData()->SetPatchControlPoints(token->AsUint32());
948 
949   return ValidateEndOfStatement("PATCH_CONTROL_POINTS command");
950 }
951 
ParsePipelineFramebufferSize(Pipeline * pipeline)952 Result Parser::ParsePipelineFramebufferSize(Pipeline* pipeline) {
953   auto token = tokenizer_->NextToken();
954   if (token->IsEOL() || token->IsEOS())
955     return Result("missing size for FRAMEBUFFER_SIZE command");
956   if (!token->IsInteger())
957     return Result("invalid width for FRAMEBUFFER_SIZE command");
958 
959   pipeline->SetFramebufferWidth(token->AsUint32());
960 
961   token = tokenizer_->NextToken();
962   if (token->IsEOL() || token->IsEOS())
963     return Result("missing height for FRAMEBUFFER_SIZE command");
964   if (!token->IsInteger())
965     return Result("invalid height for FRAMEBUFFER_SIZE command");
966 
967   pipeline->SetFramebufferHeight(token->AsUint32());
968 
969   return ValidateEndOfStatement("FRAMEBUFFER_SIZE command");
970 }
971 
ParsePipelineViewport(Pipeline * pipeline)972 Result Parser::ParsePipelineViewport(Pipeline* pipeline) {
973   Viewport vp;
974   vp.mind = 0.0f;
975   vp.maxd = 1.0f;
976 
977   float val[2];
978   for (int i = 0; i < 2; i++) {
979     auto token = tokenizer_->NextToken();
980     if (token->IsEOL() || token->IsEOS())
981       return Result("missing offset for VIEWPORT command");
982     Result r = token->ConvertToDouble();
983     if (!r.IsSuccess())
984       return Result("invalid offset for VIEWPORT command");
985 
986     val[i] = token->AsFloat();
987   }
988   vp.x = val[0];
989   vp.y = val[1];
990 
991   auto token = tokenizer_->NextToken();
992   if (!token->IsIdentifier() || token->AsString() != "SIZE")
993     return Result("missing SIZE for VIEWPORT command");
994 
995   for (int i = 0; i < 2; i++) {
996     token = tokenizer_->NextToken();
997     if (token->IsEOL() || token->IsEOS())
998       return Result("missing size for VIEWPORT command");
999     Result r = token->ConvertToDouble();
1000     if (!r.IsSuccess())
1001       return Result("invalid size for VIEWPORT command");
1002 
1003     val[i] = token->AsFloat();
1004   }
1005   vp.w = val[0];
1006   vp.h = val[1];
1007 
1008   token = tokenizer_->PeekNextToken();
1009   while (token->IsIdentifier()) {
1010     if (token->AsString() == "MIN_DEPTH") {
1011       tokenizer_->NextToken();
1012       token = tokenizer_->NextToken();
1013       if (token->IsEOL() || token->IsEOS())
1014         return Result("missing min_depth for VIEWPORT command");
1015       Result r = token->ConvertToDouble();
1016       if (!r.IsSuccess())
1017         return Result("invalid min_depth for VIEWPORT command");
1018 
1019       vp.mind = token->AsFloat();
1020     }
1021     if (token->AsString() == "MAX_DEPTH") {
1022       tokenizer_->NextToken();
1023       token = tokenizer_->NextToken();
1024       if (token->IsEOL() || token->IsEOS())
1025         return Result("missing max_depth for VIEWPORT command");
1026       Result r = token->ConvertToDouble();
1027       if (!r.IsSuccess())
1028         return Result("invalid max_depth for VIEWPORT command");
1029 
1030       vp.maxd = token->AsFloat();
1031     }
1032 
1033     token = tokenizer_->PeekNextToken();
1034   }
1035 
1036   pipeline->GetPipelineData()->SetViewport(vp);
1037 
1038   return ValidateEndOfStatement("VIEWPORT command");
1039 }
1040 
ToBufferType(const std::string & name,BufferType * type)1041 Result Parser::ToBufferType(const std::string& name, BufferType* type) {
1042   assert(type);
1043   if (name == "color")
1044     *type = BufferType::kColor;
1045   else if (name == "depth_stencil")
1046     *type = BufferType::kDepthStencil;
1047   else if (name == "push_constant")
1048     *type = BufferType::kPushConstant;
1049   else if (name == "uniform")
1050     *type = BufferType::kUniform;
1051   else if (name == "uniform_dynamic")
1052     *type = BufferType::kUniformDynamic;
1053   else if (name == "storage")
1054     *type = BufferType::kStorage;
1055   else if (name == "storage_dynamic")
1056     *type = BufferType::kStorageDynamic;
1057   else if (name == "storage_image")
1058     *type = BufferType::kStorageImage;
1059   else if (name == "sampled_image")
1060     *type = BufferType::kSampledImage;
1061   else if (name == "combined_image_sampler")
1062     *type = BufferType::kCombinedImageSampler;
1063   else if (name == "uniform_texel_buffer")
1064     *type = BufferType::kUniformTexelBuffer;
1065   else if (name == "storage_texel_buffer")
1066     *type = BufferType::kStorageTexelBuffer;
1067   else if (name == "resolve")
1068     *type = BufferType::kResolve;
1069   else
1070     return Result("unknown buffer_type: " + name);
1071 
1072   return {};
1073 }
1074 
ParsePipelineBind(Pipeline * pipeline)1075 Result Parser::ParsePipelineBind(Pipeline* pipeline) {
1076   auto token = tokenizer_->NextToken();
1077 
1078   if (!token->IsIdentifier()) {
1079     return Result(
1080         "missing BUFFER, BUFFER_ARRAY, SAMPLER, or SAMPLER_ARRAY in BIND "
1081         "command");
1082   }
1083 
1084   auto object_type = token->AsString();
1085 
1086   if (object_type == "BUFFER" || object_type == "BUFFER_ARRAY") {
1087     bool is_buffer_array = object_type == "BUFFER_ARRAY";
1088     token = tokenizer_->NextToken();
1089     if (!token->IsIdentifier())
1090       return Result("missing buffer name in BIND command");
1091 
1092     auto* buffer = script_->GetBuffer(token->AsString());
1093     if (!buffer)
1094       return Result("unknown buffer: " + token->AsString());
1095     std::vector<Buffer*> buffers = {buffer};
1096 
1097     if (is_buffer_array) {
1098       // Check for additional buffer names
1099       token = tokenizer_->PeekNextToken();
1100       while (token->IsIdentifier() && token->AsString() != "AS" &&
1101              token->AsString() != "KERNEL" &&
1102              token->AsString() != "DESCRIPTOR_SET") {
1103         tokenizer_->NextToken();
1104         buffer = script_->GetBuffer(token->AsString());
1105         if (!buffer)
1106           return Result("unknown buffer: " + token->AsString());
1107         buffers.push_back(buffer);
1108         token = tokenizer_->PeekNextToken();
1109       }
1110 
1111       if (buffers.size() < 2)
1112         return Result("expecting multiple buffer names for BUFFER_ARRAY");
1113     }
1114 
1115     BufferType buffer_type = BufferType::kUnknown;
1116     token = tokenizer_->NextToken();
1117     if (token->IsIdentifier() && token->AsString() == "AS") {
1118       token = tokenizer_->NextToken();
1119       if (!token->IsIdentifier())
1120         return Result("invalid token for BUFFER type");
1121 
1122       Result r = ToBufferType(token->AsString(), &buffer_type);
1123       if (!r.IsSuccess())
1124         return r;
1125 
1126       if (buffer_type == BufferType::kColor) {
1127         token = tokenizer_->NextToken();
1128         if (!token->IsIdentifier() || token->AsString() != "LOCATION")
1129           return Result("BIND missing LOCATION");
1130 
1131         token = tokenizer_->NextToken();
1132         if (!token->IsInteger())
1133           return Result("invalid value for BIND LOCATION");
1134         auto location = token->AsUint32();
1135 
1136         uint32_t base_mip_level = 0;
1137         token = tokenizer_->PeekNextToken();
1138         if (token->IsIdentifier() && token->AsString() == "BASE_MIP_LEVEL") {
1139           tokenizer_->NextToken();
1140           token = tokenizer_->NextToken();
1141 
1142           if (!token->IsInteger())
1143             return Result("invalid value for BASE_MIP_LEVEL");
1144 
1145           base_mip_level = token->AsUint32();
1146 
1147           if (base_mip_level >= buffer->GetMipLevels())
1148             return Result(
1149                 "base mip level (now " + token->AsString() +
1150                 ") needs to be larger than the number of buffer mip maps (" +
1151                 std::to_string(buffer->GetMipLevels()) + ")");
1152         }
1153 
1154         r = pipeline->AddColorAttachment(buffer, location, base_mip_level);
1155         if (!r.IsSuccess())
1156           return r;
1157 
1158       } else if (buffer_type == BufferType::kDepthStencil) {
1159         r = pipeline->SetDepthStencilBuffer(buffer);
1160         if (!r.IsSuccess())
1161           return r;
1162 
1163       } else if (buffer_type == BufferType::kPushConstant) {
1164         r = pipeline->SetPushConstantBuffer(buffer);
1165         if (!r.IsSuccess())
1166           return r;
1167 
1168       } else if (buffer_type == BufferType::kCombinedImageSampler) {
1169         token = tokenizer_->NextToken();
1170         if (!token->IsIdentifier() || token->AsString() != "SAMPLER")
1171           return Result("expecting SAMPLER for combined image sampler");
1172 
1173         token = tokenizer_->NextToken();
1174         if (!token->IsIdentifier())
1175           return Result("missing sampler name in BIND command");
1176 
1177         auto* sampler = script_->GetSampler(token->AsString());
1178         if (!sampler)
1179           return Result("unknown sampler: " + token->AsString());
1180 
1181         for (auto& buf : buffers)
1182           buf->SetSampler(sampler);
1183       } else if (buffer_type == BufferType::kResolve) {
1184         r = pipeline->AddResolveTarget(buffer);
1185       }
1186     }
1187 
1188     // The OpenCL bindings can be typeless which allows for the kUnknown
1189     // buffer type.
1190     if (buffer_type == BufferType::kUnknown ||
1191         buffer_type == BufferType::kStorage ||
1192         buffer_type == BufferType::kUniform ||
1193         buffer_type == BufferType::kStorageDynamic ||
1194         buffer_type == BufferType::kUniformDynamic ||
1195         buffer_type == BufferType::kStorageImage ||
1196         buffer_type == BufferType::kSampledImage ||
1197         buffer_type == BufferType::kCombinedImageSampler ||
1198         buffer_type == BufferType::kUniformTexelBuffer ||
1199         buffer_type == BufferType::kStorageTexelBuffer) {
1200       // If the buffer type is known, then we proccessed the AS block above
1201       // and have to advance to the next token. Otherwise, we're already on
1202       // the next token and don't want to advance.
1203       if (buffer_type != BufferType::kUnknown)
1204         token = tokenizer_->NextToken();
1205 
1206       // DESCRIPTOR_SET requires a buffer type to have been specified.
1207       if (token->IsIdentifier() && token->AsString() == "DESCRIPTOR_SET") {
1208         token = tokenizer_->NextToken();
1209         if (!token->IsInteger())
1210           return Result("invalid value for DESCRIPTOR_SET in BIND command");
1211         uint32_t descriptor_set = token->AsUint32();
1212 
1213         token = tokenizer_->NextToken();
1214         if (!token->IsIdentifier() || token->AsString() != "BINDING")
1215           return Result("missing BINDING for BIND command");
1216 
1217         token = tokenizer_->NextToken();
1218         if (!token->IsInteger())
1219           return Result("invalid value for BINDING in BIND command");
1220 
1221         auto binding = token->AsUint32();
1222         uint32_t base_mip_level = 0;
1223 
1224         if (buffer_type == BufferType::kStorageImage ||
1225             buffer_type == BufferType::kSampledImage ||
1226             buffer_type == BufferType::kCombinedImageSampler) {
1227           token = tokenizer_->PeekNextToken();
1228           if (token->IsIdentifier() && token->AsString() == "BASE_MIP_LEVEL") {
1229             tokenizer_->NextToken();
1230             token = tokenizer_->NextToken();
1231 
1232             if (!token->IsInteger())
1233               return Result("invalid value for BASE_MIP_LEVEL");
1234 
1235             base_mip_level = token->AsUint32();
1236 
1237             if (base_mip_level >= buffer->GetMipLevels())
1238               return Result("base mip level (now " + token->AsString() +
1239                             ") needs to be larger than the number of buffer "
1240                             "mip maps (" +
1241                             std::to_string(buffer->GetMipLevels()) + ")");
1242           }
1243         }
1244 
1245         std::vector<uint32_t> dynamic_offsets(buffers.size(), 0);
1246         if (buffer_type == BufferType::kUniformDynamic ||
1247             buffer_type == BufferType::kStorageDynamic) {
1248           token = tokenizer_->NextToken();
1249           if (!token->IsIdentifier() || token->AsString() != "OFFSET")
1250             return Result("expecting an OFFSET for dynamic buffer type");
1251 
1252           for (size_t i = 0; i < buffers.size(); i++) {
1253             token = tokenizer_->NextToken();
1254 
1255             if (!token->IsInteger()) {
1256               if (i > 0) {
1257                 return Result(
1258                     "expecting an OFFSET value for each buffer in the array");
1259               } else {
1260                 return Result("expecting an integer value for OFFSET");
1261               }
1262             }
1263 
1264             dynamic_offsets[i] = token->AsUint32();
1265           }
1266         }
1267 
1268         // Set default descriptor buffer offsets to 0 and descriptor buffer
1269         // ranges to VK_WHOLE_SIZE (~0ULL).
1270         std::vector<uint64_t> descriptor_offsets(buffers.size(), 0);
1271         std::vector<uint64_t> descriptor_ranges(buffers.size(), ~0ULL);
1272         if (buffer_type == BufferType::kUniformDynamic ||
1273             buffer_type == BufferType::kStorageDynamic ||
1274             buffer_type == BufferType::kStorage ||
1275             buffer_type == BufferType::kUniform) {
1276           token = tokenizer_->PeekNextToken();
1277           if (token->IsIdentifier() &&
1278               token->AsString() == "DESCRIPTOR_OFFSET") {
1279             token = tokenizer_->NextToken();
1280             for (size_t i = 0; i < buffers.size(); i++) {
1281               token = tokenizer_->NextToken();
1282               if (!token->IsInteger()) {
1283                 if (i > 0) {
1284                   return Result(
1285                       "expecting a DESCRIPTOR_OFFSET value for each buffer in "
1286                       "the array");
1287                 } else {
1288                   return Result(
1289                       "expecting an integer value for DESCRIPTOR_OFFSET");
1290                 }
1291               }
1292               descriptor_offsets[i] = token->AsUint64();
1293             }
1294           }
1295 
1296           token = tokenizer_->PeekNextToken();
1297           if (token->IsIdentifier() &&
1298               token->AsString() == "DESCRIPTOR_RANGE") {
1299             token = tokenizer_->NextToken();
1300             for (size_t i = 0; i < buffers.size(); i++) {
1301               token = tokenizer_->NextToken();
1302               if (!token->IsInteger()) {
1303                 if (i > 0) {
1304                   return Result(
1305                       "expecting a DESCRIPTOR_RANGE value for each buffer in "
1306                       "the array");
1307                 } else {
1308                   return Result(
1309                       "expecting an integer value for DESCRIPTOR_RANGE");
1310                 }
1311               }
1312               descriptor_ranges[i] = token->AsUint64();
1313             }
1314           }
1315         }
1316 
1317         pipeline->ClearBuffers(descriptor_set, binding);
1318         for (size_t i = 0; i < buffers.size(); i++) {
1319           pipeline->AddBuffer(buffers[i], buffer_type, descriptor_set, binding,
1320                               base_mip_level, dynamic_offsets[i],
1321                               descriptor_offsets[i], descriptor_ranges[i]);
1322         }
1323       } else if (token->IsIdentifier() && token->AsString() == "KERNEL") {
1324         token = tokenizer_->NextToken();
1325         if (!token->IsIdentifier())
1326           return Result("missing kernel arg identifier");
1327 
1328         if (token->AsString() == "ARG_NAME") {
1329           token = tokenizer_->NextToken();
1330           if (!token->IsIdentifier())
1331             return Result("expected argument identifier");
1332 
1333           pipeline->AddBuffer(buffer, buffer_type, token->AsString());
1334         } else if (token->AsString() == "ARG_NUMBER") {
1335           token = tokenizer_->NextToken();
1336           if (!token->IsInteger())
1337             return Result("expected argument number");
1338 
1339           pipeline->AddBuffer(buffer, buffer_type, token->AsUint32());
1340         } else {
1341           return Result("missing ARG_NAME or ARG_NUMBER keyword");
1342         }
1343       } else {
1344         return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
1345       }
1346     }
1347   } else if (object_type == "SAMPLER" || object_type == "SAMPLER_ARRAY") {
1348     bool is_sampler_array = object_type == "SAMPLER_ARRAY";
1349     token = tokenizer_->NextToken();
1350     if (!token->IsIdentifier())
1351       return Result("missing sampler name in BIND command");
1352 
1353     auto* sampler = script_->GetSampler(token->AsString());
1354     if (!sampler)
1355       return Result("unknown sampler: " + token->AsString());
1356     std::vector<Sampler*> samplers = {sampler};
1357 
1358     if (is_sampler_array) {
1359       // Check for additional sampler names
1360       token = tokenizer_->PeekNextToken();
1361       while (token->IsIdentifier() && token->AsString() != "KERNEL" &&
1362              token->AsString() != "DESCRIPTOR_SET") {
1363         tokenizer_->NextToken();
1364         sampler = script_->GetSampler(token->AsString());
1365         if (!sampler)
1366           return Result("unknown sampler: " + token->AsString());
1367         samplers.push_back(sampler);
1368         token = tokenizer_->PeekNextToken();
1369       }
1370 
1371       if (samplers.size() < 2)
1372         return Result("expecting multiple sampler names for SAMPLER_ARRAY");
1373     }
1374 
1375     token = tokenizer_->NextToken();
1376     if (!token->IsIdentifier())
1377       return Result("expected a string token for BIND command");
1378 
1379     if (token->AsString() == "DESCRIPTOR_SET") {
1380       token = tokenizer_->NextToken();
1381       if (!token->IsInteger())
1382         return Result("invalid value for DESCRIPTOR_SET in BIND command");
1383       uint32_t descriptor_set = token->AsUint32();
1384 
1385       token = tokenizer_->NextToken();
1386       if (!token->IsIdentifier() || token->AsString() != "BINDING")
1387         return Result("missing BINDING for BIND command");
1388 
1389       token = tokenizer_->NextToken();
1390       if (!token->IsInteger())
1391         return Result("invalid value for BINDING in BIND command");
1392 
1393       uint32_t binding = token->AsUint32();
1394       pipeline->ClearSamplers(descriptor_set, binding);
1395       for (const auto& s : samplers) {
1396         pipeline->AddSampler(s, descriptor_set, binding);
1397       }
1398     } else if (token->AsString() == "KERNEL") {
1399       token = tokenizer_->NextToken();
1400       if (!token->IsIdentifier())
1401         return Result("missing kernel arg identifier");
1402 
1403       if (token->AsString() == "ARG_NAME") {
1404         token = tokenizer_->NextToken();
1405         if (!token->IsIdentifier())
1406           return Result("expected argument identifier");
1407 
1408         pipeline->AddSampler(sampler, token->AsString());
1409       } else if (token->AsString() == "ARG_NUMBER") {
1410         token = tokenizer_->NextToken();
1411         if (!token->IsInteger())
1412           return Result("expected argument number");
1413 
1414         pipeline->AddSampler(sampler, token->AsUint32());
1415       } else {
1416         return Result("missing ARG_NAME or ARG_NUMBER keyword");
1417       }
1418     } else {
1419       return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
1420     }
1421   } else {
1422     return Result("missing BUFFER or SAMPLER in BIND command");
1423   }
1424 
1425   return ValidateEndOfStatement("BIND command");
1426 }
1427 
ParsePipelineVertexData(Pipeline * pipeline)1428 Result Parser::ParsePipelineVertexData(Pipeline* pipeline) {
1429   auto token = tokenizer_->NextToken();
1430   if (!token->IsIdentifier())
1431     return Result("missing buffer name in VERTEX_DATA command");
1432 
1433   auto* buffer = script_->GetBuffer(token->AsString());
1434   if (!buffer)
1435     return Result("unknown buffer: " + token->AsString());
1436 
1437   token = tokenizer_->NextToken();
1438   if (!token->IsIdentifier() || token->AsString() != "LOCATION")
1439     return Result("VERTEX_DATA missing LOCATION");
1440 
1441   token = tokenizer_->NextToken();
1442   if (!token->IsInteger())
1443     return Result("invalid value for VERTEX_DATA LOCATION");
1444   const uint32_t location = token->AsUint32();
1445 
1446   InputRate rate = InputRate::kVertex;
1447   uint32_t offset = 0;
1448   Format* format = buffer->GetFormat();
1449   uint32_t stride = 0;
1450 
1451   token = tokenizer_->PeekNextToken();
1452   while (token->IsIdentifier()) {
1453     if (token->AsString() == "RATE") {
1454       tokenizer_->NextToken();
1455       token = tokenizer_->NextToken();
1456       if (!token->IsIdentifier())
1457         return Result("missing input rate value for RATE");
1458       if (token->AsString() == "instance") {
1459         rate = InputRate::kInstance;
1460       } else if (token->AsString() != "vertex") {
1461         return Result("expecting 'vertex' or 'instance' for RATE value");
1462       }
1463     } else if (token->AsString() == "OFFSET") {
1464       tokenizer_->NextToken();
1465       token = tokenizer_->NextToken();
1466       if (!token->IsInteger())
1467         return Result("expected unsigned integer for OFFSET");
1468       offset = token->AsUint32();
1469     } else if (token->AsString() == "STRIDE") {
1470       tokenizer_->NextToken();
1471       token = tokenizer_->NextToken();
1472       if (!token->IsInteger())
1473         return Result("expected unsigned integer for STRIDE");
1474       stride = token->AsUint32();
1475       if (stride == 0)
1476         return Result("STRIDE needs to be larger than zero");
1477     } else if (token->AsString() == "FORMAT") {
1478       tokenizer_->NextToken();
1479       token = tokenizer_->NextToken();
1480       if (!token->IsIdentifier())
1481         return Result("vertex data FORMAT must be an identifier");
1482       auto type = script_->ParseType(token->AsString());
1483       if (!type)
1484         return Result("invalid vertex data FORMAT");
1485       auto fmt = MakeUnique<Format>(type);
1486       format = fmt.get();
1487       script_->RegisterFormat(std::move(fmt));
1488     } else {
1489       return Result("unexpected identifier for VERTEX_DATA command: " +
1490                     token->ToOriginalString());
1491     }
1492 
1493     token = tokenizer_->PeekNextToken();
1494   }
1495 
1496   if (stride == 0)
1497     stride = format->SizeInBytes();
1498 
1499   Result r =
1500       pipeline->AddVertexBuffer(buffer, location, rate, format, offset, stride);
1501   if (!r.IsSuccess())
1502     return r;
1503 
1504   return ValidateEndOfStatement("VERTEX_DATA command");
1505 }
1506 
ParsePipelineIndexData(Pipeline * pipeline)1507 Result Parser::ParsePipelineIndexData(Pipeline* pipeline) {
1508   auto token = tokenizer_->NextToken();
1509   if (!token->IsIdentifier())
1510     return Result("missing buffer name in INDEX_DATA command");
1511 
1512   auto* buffer = script_->GetBuffer(token->AsString());
1513   if (!buffer)
1514     return Result("unknown buffer: " + token->AsString());
1515 
1516   Result r = pipeline->SetIndexBuffer(buffer);
1517   if (!r.IsSuccess())
1518     return r;
1519 
1520   return ValidateEndOfStatement("INDEX_DATA command");
1521 }
1522 
ParsePipelineSet(Pipeline * pipeline)1523 Result Parser::ParsePipelineSet(Pipeline* pipeline) {
1524   if (pipeline->GetShaders().empty() ||
1525       pipeline->GetShaders()[0].GetShader()->GetFormat() !=
1526           kShaderFormatOpenCLC) {
1527     return Result("SET can only be used with OPENCL-C shaders");
1528   }
1529 
1530   auto token = tokenizer_->NextToken();
1531   if (!token->IsIdentifier() || token->AsString() != "KERNEL")
1532     return Result("missing KERNEL in SET command");
1533 
1534   token = tokenizer_->NextToken();
1535   if (!token->IsIdentifier())
1536     return Result("expected ARG_NAME or ARG_NUMBER");
1537 
1538   std::string arg_name = "";
1539   uint32_t arg_no = std::numeric_limits<uint32_t>::max();
1540   if (token->AsString() == "ARG_NAME") {
1541     token = tokenizer_->NextToken();
1542     if (!token->IsIdentifier())
1543       return Result("expected argument identifier");
1544 
1545     arg_name = token->AsString();
1546   } else if (token->AsString() == "ARG_NUMBER") {
1547     token = tokenizer_->NextToken();
1548     if (!token->IsInteger())
1549       return Result("expected argument number");
1550 
1551     arg_no = token->AsUint32();
1552   } else {
1553     return Result("expected ARG_NAME or ARG_NUMBER");
1554   }
1555 
1556   token = tokenizer_->NextToken();
1557   if (!token->IsIdentifier() || token->AsString() != "AS")
1558     return Result("missing AS in SET command");
1559 
1560   token = tokenizer_->NextToken();
1561   if (!token->IsIdentifier())
1562     return Result("expected data type");
1563 
1564   auto type = ToType(token->AsString());
1565   if (!type)
1566     return Result("invalid data type '" + token->AsString() + "' provided");
1567 
1568   if (type->IsVec() || type->IsMatrix() || type->IsArray() || type->IsStruct())
1569     return Result("data type must be a scalar type");
1570 
1571   token = tokenizer_->NextToken();
1572   if (!token->IsInteger() && !token->IsDouble())
1573     return Result("expected data value");
1574 
1575   auto fmt = MakeUnique<Format>(type.get());
1576   Value value;
1577   if (fmt->IsFloat32() || fmt->IsFloat64())
1578     value.SetDoubleValue(token->AsDouble());
1579   else
1580     value.SetIntValue(token->AsUint64());
1581 
1582   Pipeline::ArgSetInfo info;
1583   info.name = arg_name;
1584   info.ordinal = arg_no;
1585   info.fmt = fmt.get();
1586   info.value = value;
1587   pipeline->SetArg(std::move(info));
1588   script_->RegisterFormat(std::move(fmt));
1589   script_->RegisterType(std::move(type));
1590 
1591   return ValidateEndOfStatement("SET command");
1592 }
1593 
ParsePipelinePolygonMode(Pipeline * pipeline)1594 Result Parser::ParsePipelinePolygonMode(Pipeline* pipeline) {
1595   auto token = tokenizer_->NextToken();
1596   if (!token->IsIdentifier())
1597     return Result("missing mode in POLYGON_MODE command");
1598 
1599   auto mode = token->AsString();
1600 
1601   if (mode == "fill")
1602     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kFill);
1603   else if (mode == "line")
1604     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kLine);
1605   else if (mode == "point")
1606     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kPoint);
1607   else
1608     return Result("invalid polygon mode: " + mode);
1609 
1610   return ValidateEndOfStatement("POLYGON_MODE command");
1611 }
1612 
ParsePipelineDepth(Pipeline * pipeline)1613 Result Parser::ParsePipelineDepth(Pipeline* pipeline) {
1614   while (true) {
1615     auto token = tokenizer_->NextToken();
1616     if (token->IsEOL())
1617       continue;
1618     if (token->IsEOS())
1619       return Result("DEPTH missing END command");
1620     if (!token->IsIdentifier())
1621       return Result("DEPTH options must be identifiers");
1622     if (token->AsString() == "END")
1623       break;
1624 
1625     if (token->AsString() == "TEST") {
1626       token = tokenizer_->NextToken();
1627 
1628       if (!token->IsIdentifier())
1629         return Result("invalid value for TEST");
1630 
1631       if (token->AsString() == "on")
1632         pipeline->GetPipelineData()->SetEnableDepthTest(true);
1633       else if (token->AsString() == "off")
1634         pipeline->GetPipelineData()->SetEnableDepthTest(false);
1635       else
1636         return Result("invalid value for TEST: " + token->AsString());
1637     } else if (token->AsString() == "CLAMP") {
1638       token = tokenizer_->NextToken();
1639 
1640       if (!token->IsIdentifier())
1641         return Result("invalid value for CLAMP");
1642 
1643       if (token->AsString() == "on")
1644         pipeline->GetPipelineData()->SetEnableDepthClamp(true);
1645       else if (token->AsString() == "off")
1646         pipeline->GetPipelineData()->SetEnableDepthClamp(false);
1647       else
1648         return Result("invalid value for CLAMP: " + token->AsString());
1649     } else if (token->AsString() == "WRITE") {
1650       token = tokenizer_->NextToken();
1651 
1652       if (!token->IsIdentifier())
1653         return Result("invalid value for WRITE");
1654 
1655       if (token->AsString() == "on")
1656         pipeline->GetPipelineData()->SetEnableDepthWrite(true);
1657       else if (token->AsString() == "off")
1658         pipeline->GetPipelineData()->SetEnableDepthWrite(false);
1659       else
1660         return Result("invalid value for WRITE: " + token->AsString());
1661     } else if (token->AsString() == "COMPARE_OP") {
1662       token = tokenizer_->NextToken();
1663 
1664       if (!token->IsIdentifier())
1665         return Result("invalid value for COMPARE_OP");
1666 
1667       CompareOp compare_op = StrToCompareOp(token->AsString());
1668       if (compare_op != CompareOp::kUnknown) {
1669         pipeline->GetPipelineData()->SetDepthCompareOp(compare_op);
1670       } else {
1671         return Result("invalid value for COMPARE_OP: " + token->AsString());
1672       }
1673     } else if (token->AsString() == "BOUNDS") {
1674       token = tokenizer_->NextToken();
1675       if (!token->IsIdentifier() || token->AsString() != "min")
1676         return Result("BOUNDS expecting min");
1677 
1678       token = tokenizer_->NextToken();
1679       if (!token->IsDouble())
1680         return Result("BOUNDS invalid value for min");
1681       pipeline->GetPipelineData()->SetMinDepthBounds(token->AsFloat());
1682 
1683       token = tokenizer_->NextToken();
1684       if (!token->IsIdentifier() || token->AsString() != "max")
1685         return Result("BOUNDS expecting max");
1686 
1687       token = tokenizer_->NextToken();
1688       if (!token->IsDouble())
1689         return Result("BOUNDS invalid value for max");
1690       pipeline->GetPipelineData()->SetMaxDepthBounds(token->AsFloat());
1691     } else if (token->AsString() == "BIAS") {
1692       pipeline->GetPipelineData()->SetEnableDepthBias(true);
1693 
1694       token = tokenizer_->NextToken();
1695       if (!token->IsIdentifier() || token->AsString() != "constant")
1696         return Result("BIAS expecting constant");
1697 
1698       token = tokenizer_->NextToken();
1699       if (!token->IsDouble())
1700         return Result("BIAS invalid value for constant");
1701       pipeline->GetPipelineData()->SetDepthBiasConstantFactor(token->AsFloat());
1702 
1703       token = tokenizer_->NextToken();
1704       if (!token->IsIdentifier() || token->AsString() != "clamp")
1705         return Result("BIAS expecting clamp");
1706 
1707       token = tokenizer_->NextToken();
1708       if (!token->IsDouble())
1709         return Result("BIAS invalid value for clamp");
1710       pipeline->GetPipelineData()->SetDepthBiasClamp(token->AsFloat());
1711 
1712       token = tokenizer_->NextToken();
1713       if (!token->IsIdentifier() || token->AsString() != "slope")
1714         return Result("BIAS expecting slope");
1715 
1716       token = tokenizer_->NextToken();
1717       if (!token->IsDouble())
1718         return Result("BIAS invalid value for slope");
1719       pipeline->GetPipelineData()->SetDepthBiasSlopeFactor(token->AsFloat());
1720     } else {
1721       return Result("invalid value for DEPTH: " + token->AsString());
1722     }
1723   }
1724 
1725   return ValidateEndOfStatement("DEPTH command");
1726 }
1727 
ParsePipelineStencil(Pipeline * pipeline)1728 Result Parser::ParsePipelineStencil(Pipeline* pipeline) {
1729   auto token = tokenizer_->NextToken();
1730   if (!token->IsIdentifier())
1731     return Result("STENCIL missing face");
1732 
1733   bool setFront = false;
1734   bool setBack = false;
1735 
1736   if (token->AsString() == "front") {
1737     setFront = true;
1738   } else if (token->AsString() == "back") {
1739     setBack = true;
1740   } else if (token->AsString() == "front_and_back") {
1741     setFront = true;
1742     setBack = true;
1743   } else {
1744     return Result("STENCIL invalid face: " + token->AsString());
1745   }
1746 
1747   while (true) {
1748     token = tokenizer_->NextToken();
1749     if (token->IsEOL())
1750       continue;
1751     if (token->IsEOS())
1752       return Result("STENCIL missing END command");
1753     if (!token->IsIdentifier())
1754       return Result("STENCIL options must be identifiers");
1755     if (token->AsString() == "END")
1756       break;
1757 
1758     if (token->AsString() == "TEST") {
1759       token = tokenizer_->NextToken();
1760 
1761       if (!token->IsIdentifier())
1762         return Result("STENCIL invalid value for TEST");
1763 
1764       if (token->AsString() == "on")
1765         pipeline->GetPipelineData()->SetEnableStencilTest(true);
1766       else if (token->AsString() == "off")
1767         pipeline->GetPipelineData()->SetEnableStencilTest(false);
1768       else
1769         return Result("STENCIL invalid value for TEST: " + token->AsString());
1770     } else if (token->AsString() == "FAIL_OP") {
1771       token = tokenizer_->NextToken();
1772 
1773       if (!token->IsIdentifier())
1774         return Result("STENCIL invalid value for FAIL_OP");
1775 
1776       StencilOp stencil_op = StrToStencilOp(token->AsString());
1777       if (stencil_op == StencilOp::kUnknown) {
1778         return Result("STENCIL invalid value for FAIL_OP: " +
1779                       token->AsString());
1780       }
1781       if (setFront)
1782         pipeline->GetPipelineData()->SetFrontFailOp(stencil_op);
1783       if (setBack)
1784         pipeline->GetPipelineData()->SetBackFailOp(stencil_op);
1785     } else if (token->AsString() == "PASS_OP") {
1786       token = tokenizer_->NextToken();
1787 
1788       if (!token->IsIdentifier())
1789         return Result("STENCIL invalid value for PASS_OP");
1790 
1791       StencilOp stencil_op = StrToStencilOp(token->AsString());
1792       if (stencil_op == StencilOp::kUnknown) {
1793         return Result("STENCIL invalid value for PASS_OP: " +
1794                       token->AsString());
1795       }
1796       if (setFront)
1797         pipeline->GetPipelineData()->SetFrontPassOp(stencil_op);
1798       if (setBack)
1799         pipeline->GetPipelineData()->SetBackPassOp(stencil_op);
1800     } else if (token->AsString() == "DEPTH_FAIL_OP") {
1801       token = tokenizer_->NextToken();
1802 
1803       if (!token->IsIdentifier())
1804         return Result("STENCIL invalid value for DEPTH_FAIL_OP");
1805 
1806       StencilOp stencil_op = StrToStencilOp(token->AsString());
1807       if (stencil_op == StencilOp::kUnknown) {
1808         return Result("STENCIL invalid value for DEPTH_FAIL_OP: " +
1809                       token->AsString());
1810       }
1811       if (setFront)
1812         pipeline->GetPipelineData()->SetFrontDepthFailOp(stencil_op);
1813       if (setBack)
1814         pipeline->GetPipelineData()->SetBackDepthFailOp(stencil_op);
1815     } else if (token->AsString() == "COMPARE_OP") {
1816       token = tokenizer_->NextToken();
1817 
1818       if (!token->IsIdentifier())
1819         return Result("STENCIL invalid value for COMPARE_OP");
1820 
1821       CompareOp compare_op = StrToCompareOp(token->AsString());
1822       if (compare_op == CompareOp::kUnknown) {
1823         return Result("STENCIL invalid value for COMPARE_OP: " +
1824                       token->AsString());
1825       }
1826       if (setFront)
1827         pipeline->GetPipelineData()->SetFrontCompareOp(compare_op);
1828       if (setBack)
1829         pipeline->GetPipelineData()->SetBackCompareOp(compare_op);
1830     } else if (token->AsString() == "COMPARE_MASK") {
1831       token = tokenizer_->NextToken();
1832 
1833       if (!token->IsInteger())
1834         return Result("STENCIL invalid value for COMPARE_MASK");
1835 
1836       if (setFront)
1837         pipeline->GetPipelineData()->SetFrontCompareMask(token->AsUint32());
1838       if (setBack)
1839         pipeline->GetPipelineData()->SetBackCompareMask(token->AsUint32());
1840     } else if (token->AsString() == "WRITE_MASK") {
1841       token = tokenizer_->NextToken();
1842 
1843       if (!token->IsInteger())
1844         return Result("STENCIL invalid value for WRITE_MASK");
1845 
1846       if (setFront)
1847         pipeline->GetPipelineData()->SetFrontWriteMask(token->AsUint32());
1848       if (setBack)
1849         pipeline->GetPipelineData()->SetBackWriteMask(token->AsUint32());
1850     } else if (token->AsString() == "REFERENCE") {
1851       token = tokenizer_->NextToken();
1852 
1853       if (!token->IsInteger())
1854         return Result("STENCIL invalid value for REFERENCE");
1855 
1856       if (setFront)
1857         pipeline->GetPipelineData()->SetFrontReference(token->AsUint32());
1858       if (setBack)
1859         pipeline->GetPipelineData()->SetBackReference(token->AsUint32());
1860     } else {
1861       return Result("STENCIL invalid value for STENCIL: " + token->AsString());
1862     }
1863   }
1864 
1865   return ValidateEndOfStatement("STENCIL command");
1866 }
1867 
ParsePipelineBlend(Pipeline * pipeline)1868 Result Parser::ParsePipelineBlend(Pipeline* pipeline) {
1869   pipeline->GetPipelineData()->SetEnableBlend(true);
1870 
1871   while (true) {
1872     auto token = tokenizer_->NextToken();
1873     if (token->IsEOL())
1874       continue;
1875     if (token->IsEOS())
1876       return Result("BLEND missing END command");
1877     if (!token->IsIdentifier())
1878       return Result("BLEND options must be identifiers");
1879     if (token->AsString() == "END")
1880       break;
1881 
1882     if (token->AsString() == "SRC_COLOR_FACTOR") {
1883       token = tokenizer_->NextToken();
1884 
1885       if (!token->IsIdentifier())
1886         return Result("BLEND invalid value for SRC_COLOR_FACTOR");
1887 
1888       const auto factor = NameToBlendFactor(token->AsString());
1889       if (factor == BlendFactor::kUnknown)
1890         return Result("BLEND invalid value for SRC_COLOR_FACTOR: " +
1891                       token->AsString());
1892       pipeline->GetPipelineData()->SetSrcColorBlendFactor(
1893           NameToBlendFactor(token->AsString()));
1894     } else if (token->AsString() == "DST_COLOR_FACTOR") {
1895       token = tokenizer_->NextToken();
1896 
1897       if (!token->IsIdentifier())
1898         return Result("BLEND invalid value for DST_COLOR_FACTOR");
1899 
1900       const auto factor = NameToBlendFactor(token->AsString());
1901       if (factor == BlendFactor::kUnknown)
1902         return Result("BLEND invalid value for DST_COLOR_FACTOR: " +
1903                       token->AsString());
1904       pipeline->GetPipelineData()->SetDstColorBlendFactor(
1905           NameToBlendFactor(token->AsString()));
1906     } else if (token->AsString() == "SRC_ALPHA_FACTOR") {
1907       token = tokenizer_->NextToken();
1908 
1909       if (!token->IsIdentifier())
1910         return Result("BLEND invalid value for SRC_ALPHA_FACTOR");
1911 
1912       const auto factor = NameToBlendFactor(token->AsString());
1913       if (factor == BlendFactor::kUnknown)
1914         return Result("BLEND invalid value for SRC_ALPHA_FACTOR: " +
1915                       token->AsString());
1916       pipeline->GetPipelineData()->SetSrcAlphaBlendFactor(
1917           NameToBlendFactor(token->AsString()));
1918     } else if (token->AsString() == "DST_ALPHA_FACTOR") {
1919       token = tokenizer_->NextToken();
1920 
1921       if (!token->IsIdentifier())
1922         return Result("BLEND invalid value for DST_ALPHA_FACTOR");
1923 
1924       const auto factor = NameToBlendFactor(token->AsString());
1925       if (factor == BlendFactor::kUnknown)
1926         return Result("BLEND invalid value for DST_ALPHA_FACTOR: " +
1927                       token->AsString());
1928       pipeline->GetPipelineData()->SetDstAlphaBlendFactor(
1929           NameToBlendFactor(token->AsString()));
1930     } else if (token->AsString() == "COLOR_OP") {
1931       token = tokenizer_->NextToken();
1932 
1933       if (!token->IsIdentifier())
1934         return Result("BLEND invalid value for COLOR_OP");
1935 
1936       const auto op = NameToBlendOp(token->AsString());
1937       if (op == BlendOp::kUnknown)
1938         return Result("BLEND invalid value for COLOR_OP: " + token->AsString());
1939       pipeline->GetPipelineData()->SetColorBlendOp(
1940           NameToBlendOp(token->AsString()));
1941     } else if (token->AsString() == "ALPHA_OP") {
1942       token = tokenizer_->NextToken();
1943 
1944       if (!token->IsIdentifier())
1945         return Result("BLEND invalid value for ALPHA_OP");
1946 
1947       const auto op = NameToBlendOp(token->AsString());
1948       if (op == BlendOp::kUnknown)
1949         return Result("BLEND invalid value for ALPHA_OP: " + token->AsString());
1950       pipeline->GetPipelineData()->SetAlphaBlendOp(
1951           NameToBlendOp(token->AsString()));
1952     } else {
1953       return Result("BLEND invalid value for BLEND: " + token->AsString());
1954     }
1955   }
1956 
1957   return ValidateEndOfStatement("BLEND command");
1958 }
1959 
ParseStruct()1960 Result Parser::ParseStruct() {
1961   auto token = tokenizer_->NextToken();
1962   if (!token->IsIdentifier())
1963     return Result("invalid STRUCT name provided");
1964 
1965   auto struct_name = token->AsString();
1966   if (struct_name == "STRIDE")
1967     return Result("missing STRUCT name");
1968 
1969   auto s = MakeUnique<type::Struct>();
1970   auto type = s.get();
1971 
1972   Result r = script_->AddType(struct_name, std::move(s));
1973   if (!r.IsSuccess())
1974     return r;
1975 
1976   token = tokenizer_->NextToken();
1977   if (token->IsIdentifier()) {
1978     if (token->AsString() != "STRIDE")
1979       return Result("invalid token in STRUCT definition");
1980 
1981     token = tokenizer_->NextToken();
1982     if (token->IsEOL() || token->IsEOS())
1983       return Result("missing value for STRIDE");
1984     if (!token->IsInteger())
1985       return Result("invalid value for STRIDE");
1986 
1987     type->SetStrideInBytes(token->AsUint32());
1988     token = tokenizer_->NextToken();
1989   }
1990   if (!token->IsEOL()) {
1991     return Result("extra token " + token->ToOriginalString() +
1992                   " after STRUCT header");
1993   }
1994 
1995   std::map<std::string, bool> seen;
1996   for (;;) {
1997     token = tokenizer_->NextToken();
1998     if (!token->IsIdentifier())
1999       return Result("invalid type for STRUCT member");
2000     if (token->AsString() == "END")
2001       break;
2002 
2003     if (token->AsString() == struct_name)
2004       return Result("recursive types are not allowed");
2005 
2006     type::Type* member_type = script_->GetType(token->AsString());
2007     if (!member_type) {
2008       auto t = ToType(token->AsString());
2009       if (!t) {
2010         return Result("unknown type '" + token->AsString() +
2011                       "' for STRUCT member");
2012       }
2013 
2014       member_type = t.get();
2015       script_->RegisterType(std::move(t));
2016     }
2017 
2018     token = tokenizer_->NextToken();
2019     if (token->IsEOL())
2020       return Result("missing name for STRUCT member");
2021     if (!token->IsIdentifier())
2022       return Result("invalid name for STRUCT member");
2023 
2024     auto member_name = token->AsString();
2025     if (seen.find(member_name) != seen.end())
2026       return Result("duplicate name for STRUCT member");
2027 
2028     seen[member_name] = true;
2029 
2030     auto m = type->AddMember(member_type);
2031     m->name = member_name;
2032 
2033     token = tokenizer_->NextToken();
2034     while (token->IsIdentifier()) {
2035       if (token->AsString() == "OFFSET") {
2036         token = tokenizer_->NextToken();
2037         if (token->IsEOL())
2038           return Result("missing value for STRUCT member OFFSET");
2039         if (!token->IsInteger())
2040           return Result("invalid value for STRUCT member OFFSET");
2041 
2042         m->offset_in_bytes = token->AsInt32();
2043       } else if (token->AsString() == "ARRAY_STRIDE") {
2044         token = tokenizer_->NextToken();
2045         if (token->IsEOL())
2046           return Result("missing value for STRUCT member ARRAY_STRIDE");
2047         if (!token->IsInteger())
2048           return Result("invalid value for STRUCT member ARRAY_STRIDE");
2049         if (!member_type->IsArray())
2050           return Result("ARRAY_STRIDE only valid on array members");
2051 
2052         m->array_stride_in_bytes = token->AsInt32();
2053       } else if (token->AsString() == "MATRIX_STRIDE") {
2054         token = tokenizer_->NextToken();
2055         if (token->IsEOL())
2056           return Result("missing value for STRUCT member MATRIX_STRIDE");
2057         if (!token->IsInteger())
2058           return Result("invalid value for STRUCT member MATRIX_STRIDE");
2059         if (!member_type->IsMatrix())
2060           return Result("MATRIX_STRIDE only valid on matrix members");
2061 
2062         m->matrix_stride_in_bytes = token->AsInt32();
2063       } else {
2064         return Result("unknown param '" + token->AsString() +
2065                       "' for STRUCT member");
2066       }
2067 
2068       token = tokenizer_->NextToken();
2069     }
2070 
2071     if (!token->IsEOL())
2072       return Result("extra param for STRUCT member");
2073   }
2074 
2075   return {};
2076 }
2077 
ParseBuffer()2078 Result Parser::ParseBuffer() {
2079   auto token = tokenizer_->NextToken();
2080   if (!token->IsIdentifier())
2081     return Result("invalid BUFFER name provided");
2082 
2083   auto name = token->AsString();
2084   if (name == "DATA_TYPE" || name == "FORMAT")
2085     return Result("missing BUFFER name");
2086 
2087   token = tokenizer_->NextToken();
2088   if (!token->IsIdentifier())
2089     return Result("invalid BUFFER command provided");
2090 
2091   std::unique_ptr<Buffer> buffer;
2092   auto& cmd = token->AsString();
2093   if (cmd == "DATA_TYPE") {
2094     buffer = MakeUnique<Buffer>();
2095 
2096     Result r = ParseBufferInitializer(buffer.get());
2097     if (!r.IsSuccess())
2098       return r;
2099   } else if (cmd == "FORMAT") {
2100     token = tokenizer_->NextToken();
2101     if (!token->IsIdentifier())
2102       return Result("BUFFER FORMAT must be an identifier");
2103 
2104     buffer = MakeUnique<Buffer>();
2105 
2106     auto type = script_->ParseType(token->AsString());
2107     if (!type)
2108       return Result("invalid BUFFER FORMAT");
2109 
2110     auto fmt = MakeUnique<Format>(type);
2111     buffer->SetFormat(fmt.get());
2112     script_->RegisterFormat(std::move(fmt));
2113 
2114     token = tokenizer_->PeekNextToken();
2115     while (token->IsIdentifier()) {
2116       if (token->AsString() == "MIP_LEVELS") {
2117         tokenizer_->NextToken();
2118         token = tokenizer_->NextToken();
2119 
2120         if (!token->IsInteger())
2121           return Result("invalid value for MIP_LEVELS");
2122 
2123         buffer->SetMipLevels(token->AsUint32());
2124       } else if (token->AsString() == "FILE") {
2125         tokenizer_->NextToken();
2126         Result r = ParseBufferInitializerFile(buffer.get());
2127 
2128         if (!r.IsSuccess())
2129           return r;
2130       } else if (token->AsString() == "SAMPLES") {
2131         tokenizer_->NextToken();
2132         token = tokenizer_->NextToken();
2133         if (!token->IsInteger())
2134           return Result("expected integer value for SAMPLES");
2135 
2136         const uint32_t samples = token->AsUint32();
2137         if (!IsValidSampleCount(samples))
2138           return Result("invalid sample count: " + token->ToOriginalString());
2139 
2140         buffer->SetSamples(samples);
2141       } else {
2142         break;
2143       }
2144       token = tokenizer_->PeekNextToken();
2145     }
2146   } else {
2147     return Result("unknown BUFFER command provided: " + cmd);
2148   }
2149   buffer->SetName(name);
2150 
2151   Result r = script_->AddBuffer(std::move(buffer));
2152   if (!r.IsSuccess())
2153     return r;
2154 
2155   return {};
2156 }
2157 
ParseImage()2158 Result Parser::ParseImage() {
2159   auto token = tokenizer_->NextToken();
2160   if (!token->IsIdentifier())
2161     return Result("invalid IMAGE name provided");
2162 
2163   auto name = token->AsString();
2164   if (name == "DATA_TYPE" || name == "FORMAT")
2165     return Result("missing IMAGE name");
2166 
2167   std::unique_ptr<Buffer> buffer = MakeUnique<Buffer>();
2168   buffer->SetName(name);
2169   bool width_set = false;
2170   bool height_set = false;
2171   bool depth_set = false;
2172 
2173   token = tokenizer_->PeekNextToken();
2174   while (token->IsIdentifier()) {
2175     if (token->AsString() == "FILL" || token->AsString() == "SERIES_FROM" ||
2176         token->AsString() == "DATA") {
2177       break;
2178     }
2179 
2180     tokenizer_->NextToken();
2181 
2182     if (token->AsString() == "DATA_TYPE") {
2183       token = tokenizer_->NextToken();
2184       if (!token->IsIdentifier())
2185         return Result("IMAGE invalid data type");
2186 
2187       auto type = script_->ParseType(token->AsString());
2188       std::unique_ptr<Format> fmt;
2189       if (type != nullptr) {
2190         fmt = MakeUnique<Format>(type);
2191         buffer->SetFormat(fmt.get());
2192       } else {
2193         auto new_type = ToType(token->AsString());
2194         if (!new_type) {
2195           return Result("invalid data type '" + token->AsString() +
2196                         "' provided");
2197         }
2198 
2199         fmt = MakeUnique<Format>(new_type.get());
2200         buffer->SetFormat(fmt.get());
2201         script_->RegisterType(std::move(new_type));
2202       }
2203       script_->RegisterFormat(std::move(fmt));
2204     } else if (token->AsString() == "FORMAT") {
2205       token = tokenizer_->NextToken();
2206       if (!token->IsIdentifier())
2207         return Result("IMAGE FORMAT must be an identifier");
2208 
2209       auto type = script_->ParseType(token->AsString());
2210       if (!type)
2211         return Result("invalid IMAGE FORMAT");
2212 
2213       auto fmt = MakeUnique<Format>(type);
2214       buffer->SetFormat(fmt.get());
2215       script_->RegisterFormat(std::move(fmt));
2216     } else if (token->AsString() == "MIP_LEVELS") {
2217       token = tokenizer_->NextToken();
2218 
2219       if (!token->IsInteger())
2220         return Result("invalid value for MIP_LEVELS");
2221 
2222       buffer->SetMipLevels(token->AsUint32());
2223     } else if (token->AsString() == "DIM_1D") {
2224       buffer->SetImageDimension(ImageDimension::k1D);
2225     } else if (token->AsString() == "DIM_2D") {
2226       buffer->SetImageDimension(ImageDimension::k2D);
2227     } else if (token->AsString() == "DIM_3D") {
2228       buffer->SetImageDimension(ImageDimension::k3D);
2229     } else if (token->AsString() == "WIDTH") {
2230       token = tokenizer_->NextToken();
2231       if (!token->IsInteger() || token->AsUint32() == 0)
2232         return Result("expected positive IMAGE WIDTH");
2233 
2234       buffer->SetWidth(token->AsUint32());
2235       width_set = true;
2236     } else if (token->AsString() == "HEIGHT") {
2237       token = tokenizer_->NextToken();
2238       if (!token->IsInteger() || token->AsUint32() == 0)
2239         return Result("expected positive IMAGE HEIGHT");
2240 
2241       buffer->SetHeight(token->AsUint32());
2242       height_set = true;
2243     } else if (token->AsString() == "DEPTH") {
2244       token = tokenizer_->NextToken();
2245       if (!token->IsInteger() || token->AsUint32() == 0)
2246         return Result("expected positive IMAGE DEPTH");
2247 
2248       buffer->SetDepth(token->AsUint32());
2249       depth_set = true;
2250     } else if (token->AsString() == "SAMPLES") {
2251       token = tokenizer_->NextToken();
2252       if (!token->IsInteger())
2253         return Result("expected integer value for SAMPLES");
2254 
2255       const uint32_t samples = token->AsUint32();
2256       if (!IsValidSampleCount(samples))
2257         return Result("invalid sample count: " + token->ToOriginalString());
2258 
2259       buffer->SetSamples(samples);
2260     } else {
2261       return Result("unknown IMAGE command provided: " +
2262                     token->ToOriginalString());
2263     }
2264     token = tokenizer_->PeekNextToken();
2265   }
2266 
2267   if (buffer->GetImageDimension() == ImageDimension::k3D && !depth_set)
2268     return Result("expected IMAGE DEPTH");
2269 
2270   if ((buffer->GetImageDimension() == ImageDimension::k3D ||
2271        buffer->GetImageDimension() == ImageDimension::k2D) &&
2272       !height_set) {
2273     return Result("expected IMAGE HEIGHT");
2274   }
2275   if (!width_set)
2276     return Result("expected IMAGE WIDTH");
2277 
2278   const uint32_t size_in_items =
2279       buffer->GetWidth() * buffer->GetHeight() * buffer->GetDepth();
2280   buffer->SetElementCount(size_in_items);
2281 
2282   // Parse initializers.
2283   token = tokenizer_->NextToken();
2284   if (token->IsIdentifier()) {
2285     if (token->AsString() == "DATA") {
2286       Result r = ParseBufferInitializerData(buffer.get());
2287       if (!r.IsSuccess())
2288         return r;
2289 
2290       if (size_in_items != buffer->ElementCount()) {
2291         return Result(
2292             "Elements provided in data does not match size specified: " +
2293             std::to_string(size_in_items) + " specified vs " +
2294             std::to_string(buffer->ElementCount()) + " provided");
2295       }
2296     } else if (token->AsString() == "FILL") {
2297       Result r = ParseBufferInitializerFill(buffer.get(), size_in_items);
2298       if (!r.IsSuccess())
2299         return r;
2300     } else if (token->AsString() == "SERIES_FROM") {
2301       Result r = ParseBufferInitializerSeries(buffer.get(), size_in_items);
2302       if (!r.IsSuccess())
2303         return r;
2304     } else {
2305       return Result("unexpected IMAGE token: " + token->AsString());
2306     }
2307   } else if (!token->IsEOL() && !token->IsEOS()) {
2308     return Result("unexpected IMAGE token: " + token->ToOriginalString());
2309   }
2310 
2311   Result r = script_->AddBuffer(std::move(buffer));
2312   if (!r.IsSuccess())
2313     return r;
2314 
2315   return {};
2316 }
2317 
ParseBufferInitializer(Buffer * buffer)2318 Result Parser::ParseBufferInitializer(Buffer* buffer) {
2319   auto token = tokenizer_->NextToken();
2320   if (!token->IsIdentifier())
2321     return Result("BUFFER invalid data type");
2322 
2323   auto type = script_->ParseType(token->AsString());
2324   std::unique_ptr<Format> fmt;
2325   if (type != nullptr) {
2326     fmt = MakeUnique<Format>(type);
2327     buffer->SetFormat(fmt.get());
2328   } else {
2329     auto new_type = ToType(token->AsString());
2330     if (!new_type)
2331       return Result("invalid data type '" + token->AsString() + "' provided");
2332 
2333     fmt = MakeUnique<Format>(new_type.get());
2334     buffer->SetFormat(fmt.get());
2335     type = new_type.get();
2336     script_->RegisterType(std::move(new_type));
2337   }
2338   script_->RegisterFormat(std::move(fmt));
2339 
2340   token = tokenizer_->NextToken();
2341   if (!token->IsIdentifier())
2342     return Result("BUFFER missing initializer");
2343 
2344   if (token->AsString() == "STD140") {
2345     buffer->GetFormat()->SetLayout(Format::Layout::kStd140);
2346     token = tokenizer_->NextToken();
2347   } else if (token->AsString() == "STD430") {
2348     buffer->GetFormat()->SetLayout(Format::Layout::kStd430);
2349     token = tokenizer_->NextToken();
2350   }
2351 
2352   if (!token->IsIdentifier())
2353     return Result("BUFFER missing initializer");
2354 
2355   if (token->AsString() == "SIZE")
2356     return ParseBufferInitializerSize(buffer);
2357   if (token->AsString() == "WIDTH") {
2358     token = tokenizer_->NextToken();
2359     if (!token->IsInteger())
2360       return Result("expected an integer for WIDTH");
2361     const uint32_t width = token->AsUint32();
2362     if (width == 0)
2363       return Result("expected WIDTH to be positive");
2364     buffer->SetWidth(width);
2365     buffer->SetImageDimension(ImageDimension::k2D);
2366 
2367     token = tokenizer_->NextToken();
2368     if (token->AsString() != "HEIGHT")
2369       return Result("BUFFER HEIGHT missing");
2370     token = tokenizer_->NextToken();
2371     if (!token->IsInteger())
2372       return Result("expected an integer for HEIGHT");
2373     const uint32_t height = token->AsUint32();
2374     if (height == 0)
2375       return Result("expected HEIGHT to be positive");
2376     buffer->SetHeight(height);
2377 
2378     token = tokenizer_->NextToken();
2379     uint32_t size_in_items = width * height;
2380     buffer->SetElementCount(size_in_items);
2381     if (token->AsString() == "FILL")
2382       return ParseBufferInitializerFill(buffer, size_in_items);
2383     if (token->AsString() == "SERIES_FROM")
2384       return ParseBufferInitializerSeries(buffer, size_in_items);
2385     return {};
2386   }
2387   if (token->AsString() == "DATA")
2388     return ParseBufferInitializerData(buffer);
2389 
2390   return Result("unknown initializer for BUFFER");
2391 }
2392 
ParseBufferInitializerSize(Buffer * buffer)2393 Result Parser::ParseBufferInitializerSize(Buffer* buffer) {
2394   auto token = tokenizer_->NextToken();
2395   if (token->IsEOS() || token->IsEOL())
2396     return Result("BUFFER size missing");
2397   if (!token->IsInteger())
2398     return Result("BUFFER size invalid");
2399 
2400   uint32_t size_in_items = token->AsUint32();
2401   buffer->SetElementCount(size_in_items);
2402 
2403   token = tokenizer_->NextToken();
2404   if (!token->IsIdentifier())
2405     return Result("BUFFER invalid initializer");
2406 
2407   if (token->AsString() == "FILL")
2408     return ParseBufferInitializerFill(buffer, size_in_items);
2409   if (token->AsString() == "SERIES_FROM")
2410     return ParseBufferInitializerSeries(buffer, size_in_items);
2411   if (token->AsString() == "FILE")
2412     return ParseBufferInitializerFile(buffer);
2413 
2414   return Result("invalid BUFFER initializer provided");
2415 }
2416 
ParseBufferInitializerFill(Buffer * buffer,uint32_t size_in_items)2417 Result Parser::ParseBufferInitializerFill(Buffer* buffer,
2418                                           uint32_t size_in_items) {
2419   auto token = tokenizer_->NextToken();
2420   if (token->IsEOS() || token->IsEOL())
2421     return Result("missing BUFFER fill value");
2422   if (!token->IsInteger() && !token->IsDouble())
2423     return Result("invalid BUFFER fill value");
2424 
2425   auto fmt = buffer->GetFormat();
2426   bool is_double_data = fmt->IsFloat32() || fmt->IsFloat64();
2427 
2428   // Inflate the size because our items are multi-dimensional.
2429   size_in_items = size_in_items * fmt->InputNeededPerElement();
2430 
2431   std::vector<Value> values;
2432   values.resize(size_in_items);
2433   for (size_t i = 0; i < size_in_items; ++i) {
2434     if (is_double_data)
2435       values[i].SetDoubleValue(token->AsDouble());
2436     else
2437       values[i].SetIntValue(token->AsUint64());
2438   }
2439   Result r = buffer->SetData(std::move(values));
2440   if (!r.IsSuccess())
2441     return r;
2442 
2443   return ValidateEndOfStatement("BUFFER fill command");
2444 }
2445 
ParseBufferInitializerSeries(Buffer * buffer,uint32_t size_in_items)2446 Result Parser::ParseBufferInitializerSeries(Buffer* buffer,
2447                                             uint32_t size_in_items) {
2448   auto token = tokenizer_->NextToken();
2449   if (token->IsEOS() || token->IsEOL())
2450     return Result("missing BUFFER series_from value");
2451   if (!token->IsInteger() && !token->IsDouble())
2452     return Result("invalid BUFFER series_from value");
2453 
2454   auto type = buffer->GetFormat()->GetType();
2455   if (type->IsMatrix() || type->IsVec())
2456     return Result("BUFFER series_from must not be multi-row/column types");
2457 
2458   Value counter;
2459 
2460   auto n = type->AsNumber();
2461   FormatMode mode = n->GetFormatMode();
2462   uint32_t num_bits = n->NumBits();
2463   if (type::Type::IsFloat32(mode, num_bits) ||
2464       type::Type::IsFloat64(mode, num_bits)) {
2465     counter.SetDoubleValue(token->AsDouble());
2466   } else {
2467     counter.SetIntValue(token->AsUint64());
2468   }
2469 
2470   token = tokenizer_->NextToken();
2471   if (!token->IsIdentifier())
2472     return Result("missing BUFFER series_from inc_by");
2473   if (token->AsString() != "INC_BY")
2474     return Result("BUFFER series_from invalid command");
2475 
2476   token = tokenizer_->NextToken();
2477   if (token->IsEOS() || token->IsEOL())
2478     return Result("missing BUFFER series_from inc_by value");
2479   if (!token->IsInteger() && !token->IsDouble())
2480     return Result("invalid BUFFER series_from inc_by value");
2481 
2482   std::vector<Value> values;
2483   values.resize(size_in_items);
2484   for (size_t i = 0; i < size_in_items; ++i) {
2485     if (type::Type::IsFloat32(mode, num_bits) ||
2486         type::Type::IsFloat64(mode, num_bits)) {
2487       double value = counter.AsDouble();
2488       values[i].SetDoubleValue(value);
2489       counter.SetDoubleValue(value + token->AsDouble());
2490     } else {
2491       uint64_t value = counter.AsUint64();
2492       values[i].SetIntValue(value);
2493       counter.SetIntValue(value + token->AsUint64());
2494     }
2495   }
2496   Result r = buffer->SetData(std::move(values));
2497   if (!r.IsSuccess())
2498     return r;
2499 
2500   return ValidateEndOfStatement("BUFFER series_from command");
2501 }
2502 
ParseBufferInitializerData(Buffer * buffer)2503 Result Parser::ParseBufferInitializerData(Buffer* buffer) {
2504   Result r = ParseBufferData(buffer, tokenizer_.get(), false);
2505 
2506   if (!r.IsSuccess())
2507     return r;
2508 
2509   return ValidateEndOfStatement("BUFFER data command");
2510 }
2511 
ParseBufferInitializerFile(Buffer * buffer)2512 Result Parser::ParseBufferInitializerFile(Buffer* buffer) {
2513   auto token = tokenizer_->NextToken();
2514 
2515   if (!token->IsIdentifier())
2516     return Result("invalid value for FILE");
2517 
2518   BufferDataFileType file_type = BufferDataFileType::kPng;
2519 
2520   if (token->AsString() == "TEXT") {
2521     file_type = BufferDataFileType::kText;
2522     token = tokenizer_->NextToken();
2523   } else if (token->AsString() == "BINARY") {
2524     file_type = BufferDataFileType::kBinary;
2525     token = tokenizer_->NextToken();
2526   } else if (token->AsString() == "PNG") {
2527     token = tokenizer_->NextToken();
2528   }
2529 
2530   if (!token->IsIdentifier())
2531     return Result("missing file name for FILE");
2532 
2533   if (!delegate_)
2534     return Result("missing delegate");
2535 
2536   BufferInfo info;
2537   Result r = delegate_->LoadBufferData(token->AsString(), file_type, &info);
2538 
2539   if (!r.IsSuccess())
2540     return r;
2541 
2542   std::vector<uint8_t>* data = buffer->ValuePtr();
2543 
2544   data->clear();
2545   data->reserve(info.values.size());
2546   for (auto v : info.values) {
2547     data->push_back(v.AsUint8());
2548   }
2549 
2550   if (file_type == BufferDataFileType::kText) {
2551     auto s = std::string(data->begin(), data->end());
2552     Tokenizer tok(s);
2553     r = ParseBufferData(buffer, &tok, true);
2554     if (!r.IsSuccess())
2555       return r;
2556   } else {
2557     buffer->SetElementCount(static_cast<uint32_t>(data->size()) /
2558                             buffer->GetFormat()->SizeInBytes());
2559     buffer->SetWidth(info.width);
2560     buffer->SetHeight(info.height);
2561   }
2562 
2563   return {};
2564 }
2565 
ParseRun()2566 Result Parser::ParseRun() {
2567   auto token = tokenizer_->NextToken();
2568   if (!token->IsIdentifier())
2569     return Result("missing pipeline name for RUN command");
2570 
2571   size_t line = tokenizer_->GetCurrentLine();
2572 
2573   auto* pipeline = script_->GetPipeline(token->AsString());
2574   if (!pipeline)
2575     return Result("unknown pipeline for RUN command: " + token->AsString());
2576 
2577   token = tokenizer_->NextToken();
2578   if (token->IsEOL() || token->IsEOS())
2579     return Result("RUN command requires parameters");
2580 
2581   if (token->IsInteger()) {
2582     if (!pipeline->IsCompute())
2583       return Result("RUN command requires compute pipeline");
2584 
2585     auto cmd = MakeUnique<ComputeCommand>(pipeline);
2586     cmd->SetLine(line);
2587     cmd->SetX(token->AsUint32());
2588 
2589     token = tokenizer_->NextToken();
2590     if (!token->IsInteger()) {
2591       return Result("invalid parameter for RUN command: " +
2592                     token->ToOriginalString());
2593     }
2594     cmd->SetY(token->AsUint32());
2595 
2596     token = tokenizer_->NextToken();
2597     if (!token->IsInteger()) {
2598       return Result("invalid parameter for RUN command: " +
2599                     token->ToOriginalString());
2600     }
2601     cmd->SetZ(token->AsUint32());
2602 
2603     command_list_.push_back(std::move(cmd));
2604     return ValidateEndOfStatement("RUN command");
2605   }
2606 
2607   if (!token->IsIdentifier())
2608     return Result("invalid token in RUN command: " + token->ToOriginalString());
2609 
2610   if (token->AsString() == "DRAW_RECT") {
2611     if (!pipeline->IsGraphics())
2612       return Result("RUN command requires graphics pipeline");
2613 
2614     if (pipeline->GetVertexBuffers().size() > 1) {
2615       return Result(
2616           "RUN DRAW_RECT is not supported in a pipeline with more than one "
2617           "vertex buffer attached");
2618     }
2619 
2620     token = tokenizer_->NextToken();
2621     if (token->IsEOS() || token->IsEOL())
2622       return Result("RUN DRAW_RECT command requires parameters");
2623 
2624     if (!token->IsIdentifier() || token->AsString() != "POS") {
2625       return Result("invalid token in RUN command: " +
2626                     token->ToOriginalString() + "; expected POS");
2627     }
2628 
2629     token = tokenizer_->NextToken();
2630     if (!token->IsInteger())
2631       return Result("missing X position for RUN command");
2632 
2633     auto cmd =
2634         MakeUnique<DrawRectCommand>(pipeline, *pipeline->GetPipelineData());
2635     cmd->SetLine(line);
2636     cmd->EnableOrtho();
2637 
2638     Result r = token->ConvertToDouble();
2639     if (!r.IsSuccess())
2640       return r;
2641     cmd->SetX(token->AsFloat());
2642 
2643     token = tokenizer_->NextToken();
2644     if (!token->IsInteger())
2645       return Result("missing Y position for RUN command");
2646 
2647     r = token->ConvertToDouble();
2648     if (!r.IsSuccess())
2649       return r;
2650     cmd->SetY(token->AsFloat());
2651 
2652     token = tokenizer_->NextToken();
2653     if (!token->IsIdentifier() || token->AsString() != "SIZE") {
2654       return Result("invalid token in RUN command: " +
2655                     token->ToOriginalString() + "; expected SIZE");
2656     }
2657 
2658     token = tokenizer_->NextToken();
2659     if (!token->IsInteger())
2660       return Result("missing width value for RUN command");
2661 
2662     r = token->ConvertToDouble();
2663     if (!r.IsSuccess())
2664       return r;
2665     cmd->SetWidth(token->AsFloat());
2666 
2667     token = tokenizer_->NextToken();
2668     if (!token->IsInteger())
2669       return Result("missing height value for RUN command");
2670 
2671     r = token->ConvertToDouble();
2672     if (!r.IsSuccess())
2673       return r;
2674     cmd->SetHeight(token->AsFloat());
2675 
2676     command_list_.push_back(std::move(cmd));
2677     return ValidateEndOfStatement("RUN command");
2678   }
2679 
2680   if (token->AsString() == "DRAW_GRID") {
2681     if (!pipeline->IsGraphics())
2682       return Result("RUN command requires graphics pipeline");
2683 
2684     if (pipeline->GetVertexBuffers().size() > 0) {
2685       return Result(
2686           "RUN DRAW_GRID is not supported in a pipeline with "
2687           "vertex buffers attached");
2688     }
2689 
2690     token = tokenizer_->NextToken();
2691     if (token->IsEOS() || token->IsEOL())
2692       return Result("RUN DRAW_GRID command requires parameters");
2693 
2694     if (!token->IsIdentifier() || token->AsString() != "POS") {
2695       return Result("invalid token in RUN command: " +
2696                     token->ToOriginalString() + "; expected POS");
2697     }
2698 
2699     token = tokenizer_->NextToken();
2700     if (!token->IsInteger())
2701       return Result("missing X position for RUN command");
2702 
2703     auto cmd =
2704         MakeUnique<DrawGridCommand>(pipeline, *pipeline->GetPipelineData());
2705     cmd->SetLine(line);
2706 
2707     Result r = token->ConvertToDouble();
2708     if (!r.IsSuccess())
2709       return r;
2710     cmd->SetX(token->AsFloat());
2711 
2712     token = tokenizer_->NextToken();
2713     if (!token->IsInteger())
2714       return Result("missing Y position for RUN command");
2715 
2716     r = token->ConvertToDouble();
2717     if (!r.IsSuccess())
2718       return r;
2719     cmd->SetY(token->AsFloat());
2720 
2721     token = tokenizer_->NextToken();
2722     if (!token->IsIdentifier() || token->AsString() != "SIZE") {
2723       return Result("invalid token in RUN command: " +
2724                     token->ToOriginalString() + "; expected SIZE");
2725     }
2726 
2727     token = tokenizer_->NextToken();
2728     if (!token->IsInteger())
2729       return Result("missing width value for RUN command");
2730 
2731     r = token->ConvertToDouble();
2732     if (!r.IsSuccess())
2733       return r;
2734     cmd->SetWidth(token->AsFloat());
2735 
2736     token = tokenizer_->NextToken();
2737     if (!token->IsInteger())
2738       return Result("missing height value for RUN command");
2739 
2740     r = token->ConvertToDouble();
2741     if (!r.IsSuccess())
2742       return r;
2743     cmd->SetHeight(token->AsFloat());
2744 
2745     token = tokenizer_->NextToken();
2746     if (!token->IsIdentifier() || token->AsString() != "CELLS") {
2747       return Result("invalid token in RUN command: " +
2748                     token->ToOriginalString() + "; expected CELLS");
2749     }
2750 
2751     token = tokenizer_->NextToken();
2752     if (!token->IsInteger())
2753       return Result("missing columns value for RUN command");
2754 
2755     cmd->SetColumns(token->AsUint32());
2756 
2757     token = tokenizer_->NextToken();
2758     if (!token->IsInteger())
2759       return Result("missing rows value for RUN command");
2760 
2761     cmd->SetRows(token->AsUint32());
2762 
2763     command_list_.push_back(std::move(cmd));
2764     return ValidateEndOfStatement("RUN command");
2765   }
2766 
2767   if (token->AsString() == "DRAW_ARRAY") {
2768     if (!pipeline->IsGraphics())
2769       return Result("RUN command requires graphics pipeline");
2770 
2771     if (pipeline->GetVertexBuffers().empty())
2772       return Result("RUN DRAW_ARRAY requires attached vertex buffer");
2773 
2774     token = tokenizer_->NextToken();
2775     if (!token->IsIdentifier() || token->AsString() != "AS")
2776       return Result("missing AS for RUN command");
2777 
2778     token = tokenizer_->NextToken();
2779     if (!token->IsIdentifier()) {
2780       return Result("invalid topology for RUN command: " +
2781                     token->ToOriginalString());
2782     }
2783 
2784     Topology topo = NameToTopology(token->AsString());
2785     if (topo == Topology::kUnknown)
2786       return Result("invalid topology for RUN command: " + token->AsString());
2787 
2788     bool indexed = false;
2789     uint32_t start_idx = 0;
2790     uint32_t count = 0;
2791     uint32_t start_instance = 0;
2792     uint32_t instance_count = 1;
2793 
2794     token = tokenizer_->PeekNextToken();
2795 
2796     while (!token->IsEOS() && !token->IsEOL()) {
2797       token = tokenizer_->NextToken();
2798 
2799       if (!token->IsIdentifier())
2800         return Result("expecting identifier for RUN command");
2801 
2802       if (token->AsString() == "INDEXED") {
2803         if (!pipeline->GetIndexBuffer()) {
2804           return Result(
2805               "RUN DRAW_ARRAYS INDEXED requires attached index buffer");
2806         }
2807 
2808         indexed = true;
2809       } else if (token->AsString() == "START_IDX") {
2810         token = tokenizer_->NextToken();
2811         if (!token->IsInteger()) {
2812           return Result("invalid START_IDX value for RUN command: " +
2813                         token->ToOriginalString());
2814         }
2815         if (token->AsInt32() < 0)
2816           return Result("START_IDX value must be >= 0 for RUN command");
2817         start_idx = token->AsUint32();
2818       } else if (token->AsString() == "COUNT") {
2819         token = tokenizer_->NextToken();
2820         if (!token->IsInteger()) {
2821           return Result("invalid COUNT value for RUN command: " +
2822                         token->ToOriginalString());
2823         }
2824         if (token->AsInt32() <= 0)
2825           return Result("COUNT value must be > 0 for RUN command");
2826 
2827         count = token->AsUint32();
2828       } else if (token->AsString() == "INSTANCE_COUNT") {
2829         token = tokenizer_->NextToken();
2830         if (!token->IsInteger()) {
2831           return Result("invalid INSTANCE_COUNT value for RUN command: " +
2832                         token->ToOriginalString());
2833         }
2834         if (token->AsInt32() <= 0)
2835           return Result("INSTANCE_COUNT value must be > 0 for RUN command");
2836 
2837         instance_count = token->AsUint32();
2838       } else if (token->AsString() == "START_INSTANCE") {
2839         token = tokenizer_->NextToken();
2840         if (!token->IsInteger()) {
2841           return Result("invalid START_INSTANCE value for RUN command: " +
2842                         token->ToOriginalString());
2843         }
2844         if (token->AsInt32() < 0)
2845           return Result("START_INSTANCE value must be >= 0 for RUN command");
2846         start_instance = token->AsUint32();
2847       } else {
2848         return Result("Unexpected identifier for RUN command: " +
2849                       token->ToOriginalString());
2850       }
2851 
2852       token = tokenizer_->PeekNextToken();
2853     }
2854 
2855     uint32_t vertex_count =
2856         indexed ? pipeline->GetIndexBuffer()->ElementCount()
2857                 : pipeline->GetVertexBuffers()[0].buffer->ElementCount();
2858 
2859     // If we get here then we never set count, as if count was set it must
2860     // be > 0.
2861     if (count == 0)
2862       count = vertex_count - start_idx;
2863 
2864     if (start_idx + count > vertex_count) {
2865       if (indexed)
2866         return Result("START_IDX plus COUNT exceeds index buffer data size");
2867       else
2868         return Result("START_IDX plus COUNT exceeds vertex buffer data size");
2869     }
2870 
2871     auto cmd =
2872         MakeUnique<DrawArraysCommand>(pipeline, *pipeline->GetPipelineData());
2873     cmd->SetLine(line);
2874     cmd->SetTopology(topo);
2875     cmd->SetFirstVertexIndex(start_idx);
2876     cmd->SetVertexCount(count);
2877     cmd->SetInstanceCount(instance_count);
2878     cmd->SetFirstInstance(start_instance);
2879 
2880     if (indexed)
2881       cmd->EnableIndexed();
2882 
2883     command_list_.push_back(std::move(cmd));
2884     return ValidateEndOfStatement("RUN command");
2885   }
2886 
2887   return Result("invalid token in RUN command: " + token->AsString());
2888 }
2889 
ParseDebug()2890 Result Parser::ParseDebug() {
2891   // DEBUG extends a RUN with debugger test cases
2892   auto res = ParseRun();
2893   if (!res.IsSuccess()) {
2894     return res;
2895   }
2896 
2897   // As ParseRun() succeeded, we know it emplaced a run command at the back of
2898   // the command_list_.
2899   auto cmd = command_list_.back().get();
2900 
2901   // We also know this command must derive from PipelineCommand, as it is
2902   // runnable.
2903   auto pipeline_cmd = static_cast<PipelineCommand*>(cmd);
2904   auto pipeline = pipeline_cmd->GetPipeline();
2905 
2906   auto dbg = debug::Script::Create();
2907   for (auto token = tokenizer_->NextToken();; token = tokenizer_->NextToken()) {
2908     if (token->IsEOL())
2909       continue;
2910     if (token->IsEOS())
2911       return Result("missing DEBUG END command");
2912     if (token->IsIdentifier() && token->AsString() == "END")
2913       break;
2914 
2915     if (token->AsString() == "THREAD") {
2916       res = ParseDebugThread(dbg.get(), pipeline);
2917       if (!res.IsSuccess()) {
2918         return res;
2919       }
2920     } else {
2921       return Result("invalid token in DEBUG command: " + token->AsString());
2922     }
2923   }
2924 
2925   cmd->SetDebugScript(std::move(dbg));
2926 
2927   return Result();
2928 }
2929 
ParseDebugThread(debug::Events * dbg,Pipeline * pipeline)2930 Result Parser::ParseDebugThread(debug::Events* dbg, Pipeline* pipeline) {
2931   auto token = tokenizer_->NextToken();
2932   if (token->AsString() == "GLOBAL_INVOCATION_ID") {
2933     for (auto& shader : pipeline->GetShaders()) {
2934       shader.SetEmitDebugInfo(true);
2935     }
2936 
2937     uint32_t invocation[3] = {};
2938     for (int i = 0; i < 3; i++) {
2939       token = tokenizer_->NextToken();
2940       if (!token->IsInteger())
2941         return Result("expected invocation index");
2942       invocation[i] = token->AsUint32();
2943     }
2944 
2945     auto thread = debug::ThreadScript::Create();
2946     auto result = ParseDebugThreadBody(thread.get());
2947     if (!result.IsSuccess()) {
2948       return result;
2949     }
2950 
2951     dbg->BreakOnComputeGlobalInvocation(invocation[0], invocation[1],
2952                                         invocation[2], thread);
2953   } else if (token->AsString() == "VERTEX_INDEX") {
2954     for (auto& shader : pipeline->GetShaders()) {
2955       if (shader.GetShaderType() == kShaderTypeVertex) {
2956         shader.SetEmitDebugInfo(true);
2957       }
2958     }
2959 
2960     token = tokenizer_->NextToken();
2961     if (!token->IsInteger())
2962       return Result("expected vertex index");
2963     auto vertex_index = token->AsUint32();
2964 
2965     auto thread = debug::ThreadScript::Create();
2966     auto result = ParseDebugThreadBody(thread.get());
2967     if (!result.IsSuccess()) {
2968       return result;
2969     }
2970 
2971     dbg->BreakOnVertexIndex(vertex_index, thread);
2972   } else if (token->AsString() == "FRAGMENT_WINDOW_SPACE_POSITION") {
2973     for (auto& shader : pipeline->GetShaders()) {
2974       if (shader.GetShaderType() == kShaderTypeFragment) {
2975         shader.SetEmitDebugInfo(true);
2976       }
2977     }
2978 
2979     token = tokenizer_->NextToken();
2980     if (!token->IsInteger())
2981       return Result("expected x unsigned integer coordinate");
2982     auto x = token->AsUint32();
2983 
2984     token = tokenizer_->NextToken();
2985     if (!token->IsInteger())
2986       return Result("expected y unsigned integer coordinate");
2987     auto y = token->AsUint32();
2988 
2989     auto thread = debug::ThreadScript::Create();
2990     auto result = ParseDebugThreadBody(thread.get());
2991     if (!result.IsSuccess()) {
2992       return result;
2993     }
2994 
2995     dbg->BreakOnFragmentWindowSpacePosition(x, y, thread);
2996   } else {
2997     return Result("expected GLOBAL_INVOCATION_ID or VERTEX_INDEX");
2998   }
2999 
3000   return Result();
3001 }
3002 
ParseDebugThreadBody(debug::Thread * thread)3003 Result Parser::ParseDebugThreadBody(debug::Thread* thread) {
3004   for (auto token = tokenizer_->NextToken();; token = tokenizer_->NextToken()) {
3005     if (token->IsEOL()) {
3006       continue;
3007     }
3008     if (token->IsEOS()) {
3009       return Result("missing THREAD END command");
3010     }
3011     if (token->IsIdentifier() && token->AsString() == "END") {
3012       break;
3013     }
3014 
3015     if (token->AsString() == "EXPECT") {
3016       token = tokenizer_->NextToken();
3017       if (token->AsString() == "LOCATION") {
3018         debug::Location location;
3019         token = tokenizer_->NextToken();
3020         if (!token->IsString()) {
3021           return Result("expected file name string");
3022         }
3023         location.file = token->AsString();
3024 
3025         token = tokenizer_->NextToken();
3026         if (!token->IsInteger()) {
3027           return Result("expected line number");
3028         }
3029         location.line = token->AsUint32();
3030 
3031         std::string line_source;
3032         token = tokenizer_->NextToken();
3033         if (token->IsString()) {
3034           line_source = token->AsString();
3035         }
3036 
3037         thread->ExpectLocation(location, line_source);
3038       } else if (token->AsString() == "LOCAL") {
3039         auto name = tokenizer_->NextToken();
3040         if (!name->IsString()) {
3041           return Result("expected variable name");
3042         }
3043 
3044         if (tokenizer_->NextToken()->AsString() != "EQ") {
3045           return Result("expected EQ");
3046         }
3047 
3048         auto value = tokenizer_->NextToken();
3049         if (value->IsHex() || value->IsInteger()) {
3050           thread->ExpectLocal(name->AsString(), value->AsInt64());
3051         } else if (value->IsDouble()) {
3052           thread->ExpectLocal(name->AsString(), value->AsDouble());
3053         } else if (value->IsString()) {
3054           thread->ExpectLocal(name->AsString(), value->AsString());
3055         } else {
3056           return Result("expected variable value");
3057         }
3058       } else if (token->AsString() == "CALLSTACK") {
3059         std::vector<debug::StackFrame> stack;
3060         for (auto tok = tokenizer_->NextToken(); tok->AsString() != "END";
3061              tok = tokenizer_->NextToken()) {
3062           if (tok->IsEOL()) {
3063             continue;
3064           }
3065           debug::StackFrame frame;
3066           if (!tok->IsString()) {
3067             return Result("expected stack frame name");
3068           }
3069           frame.name = tok->AsString();
3070 
3071           tok = tokenizer_->NextToken();
3072           if (tok->IsString()) {
3073             frame.location.file = tok->AsString();
3074             tok = tokenizer_->NextToken();
3075             if (tok->IsInteger()) {
3076               frame.location.line = tok->AsUint32();
3077             } else if (!tok->IsEOL()) {
3078               return Result(
3079                   "expected end of line or stack frame location number");
3080             }
3081           } else if (!tok->IsEOL()) {
3082             return Result(
3083                 "expected end of line or stack frame location file name");
3084           }
3085 
3086           stack.emplace_back(frame);
3087         }
3088         thread->ExpectCallstack(stack);
3089       } else {
3090         return Result("expected LOCATION or LOCAL");
3091       }
3092     } else if (token->AsString() == "STEP_IN") {
3093       thread->StepIn();
3094     } else if (token->AsString() == "STEP_OUT") {
3095       thread->StepOut();
3096     } else if (token->AsString() == "STEP_OVER") {
3097       thread->StepOver();
3098     } else if (token->AsString() == "CONTINUE") {
3099       thread->Continue();
3100     } else {
3101       return Result("invalid token in THREAD block: " + token->AsString());
3102     }
3103   }
3104   return Result();
3105 }
3106 
ParseClear()3107 Result Parser::ParseClear() {
3108   auto token = tokenizer_->NextToken();
3109   if (!token->IsIdentifier())
3110     return Result("missing pipeline name for CLEAR command");
3111 
3112   size_t line = tokenizer_->GetCurrentLine();
3113 
3114   auto* pipeline = script_->GetPipeline(token->AsString());
3115   if (!pipeline)
3116     return Result("unknown pipeline for CLEAR command: " + token->AsString());
3117   if (!pipeline->IsGraphics())
3118     return Result("CLEAR command requires graphics pipeline");
3119 
3120   auto cmd = MakeUnique<ClearCommand>(pipeline);
3121   cmd->SetLine(line);
3122   command_list_.push_back(std::move(cmd));
3123 
3124   return ValidateEndOfStatement("CLEAR command");
3125 }
3126 
ParseValues(const std::string & name,Format * fmt,std::vector<Value> * values)3127 Result Parser::ParseValues(const std::string& name,
3128                            Format* fmt,
3129                            std::vector<Value>* values) {
3130   assert(values);
3131 
3132   auto token = tokenizer_->NextToken();
3133   const auto& segs = fmt->GetSegments();
3134   size_t seg_idx = 0;
3135   while (!token->IsEOL() && !token->IsEOS()) {
3136     Value v;
3137 
3138     while (segs[seg_idx].IsPadding()) {
3139       ++seg_idx;
3140       if (seg_idx >= segs.size())
3141         seg_idx = 0;
3142     }
3143 
3144     if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
3145       if (!token->IsInteger() && !token->IsDouble() && !token->IsHex()) {
3146         return Result(std::string("Invalid value provided to ") + name +
3147                       " command: " + token->ToOriginalString());
3148       }
3149 
3150       Result r = token->ConvertToDouble();
3151       if (!r.IsSuccess())
3152         return r;
3153 
3154       v.SetDoubleValue(token->AsDouble());
3155     } else {
3156       if (!token->IsInteger() && !token->IsHex()) {
3157         return Result(std::string("Invalid value provided to ") + name +
3158                       " command: " + token->ToOriginalString());
3159       }
3160 
3161       uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
3162       v.SetIntValue(val);
3163     }
3164     ++seg_idx;
3165     if (seg_idx >= segs.size())
3166       seg_idx = 0;
3167 
3168     values->push_back(v);
3169     token = tokenizer_->NextToken();
3170   }
3171   return {};
3172 }
3173 
ParseExpect()3174 Result Parser::ParseExpect() {
3175   auto token = tokenizer_->NextToken();
3176   if (!token->IsIdentifier())
3177     return Result("invalid buffer name in EXPECT command");
3178 
3179   if (token->AsString() == "IDX")
3180     return Result("missing buffer name between EXPECT and IDX");
3181   if (token->AsString() == "EQ_BUFFER")
3182     return Result("missing buffer name between EXPECT and EQ_BUFFER");
3183   if (token->AsString() == "RMSE_BUFFER")
3184     return Result("missing buffer name between EXPECT and RMSE_BUFFER");
3185   if (token->AsString() == "EQ_HISTOGRAM_EMD_BUFFER") {
3186     return Result(
3187         "missing buffer name between EXPECT and EQ_HISTOGRAM_EMD_BUFFER");
3188   }
3189 
3190   size_t line = tokenizer_->GetCurrentLine();
3191   auto* buffer = script_->GetBuffer(token->AsString());
3192   if (!buffer)
3193     return Result("unknown buffer name for EXPECT command: " +
3194                   token->AsString());
3195 
3196   token = tokenizer_->NextToken();
3197 
3198   if (!token->IsIdentifier())
3199     return Result("invalid comparator in EXPECT command");
3200 
3201   if (token->AsString() == "EQ_BUFFER" || token->AsString() == "RMSE_BUFFER" ||
3202       token->AsString() == "EQ_HISTOGRAM_EMD_BUFFER") {
3203     auto type = token->AsString();
3204 
3205     token = tokenizer_->NextToken();
3206     if (!token->IsIdentifier())
3207       return Result("invalid buffer name in EXPECT " + type + " command");
3208 
3209     auto* buffer_2 = script_->GetBuffer(token->AsString());
3210     if (!buffer_2) {
3211       return Result("unknown buffer name for EXPECT " + type +
3212                     " command: " + token->AsString());
3213     }
3214 
3215     if (!buffer->GetFormat()->Equal(buffer_2->GetFormat())) {
3216       return Result("EXPECT " + type +
3217                     " command cannot compare buffers of differing format");
3218     }
3219     if (buffer->ElementCount() != buffer_2->ElementCount()) {
3220       return Result("EXPECT " + type +
3221                     " command cannot compare buffers of different size: " +
3222                     std::to_string(buffer->ElementCount()) + " vs " +
3223                     std::to_string(buffer_2->ElementCount()));
3224     }
3225     if (buffer->GetWidth() != buffer_2->GetWidth()) {
3226       return Result("EXPECT " + type +
3227                     " command cannot compare buffers of different width");
3228     }
3229     if (buffer->GetHeight() != buffer_2->GetHeight()) {
3230       return Result("EXPECT " + type +
3231                     " command cannot compare buffers of different height");
3232     }
3233 
3234     auto cmd = MakeUnique<CompareBufferCommand>(buffer, buffer_2);
3235     if (type == "RMSE_BUFFER") {
3236       cmd->SetComparator(CompareBufferCommand::Comparator::kRmse);
3237 
3238       token = tokenizer_->NextToken();
3239       if (!token->IsIdentifier() && token->AsString() == "TOLERANCE")
3240         return Result("missing TOLERANCE for EXPECT RMSE_BUFFER");
3241 
3242       token = tokenizer_->NextToken();
3243       if (!token->IsInteger() && !token->IsDouble())
3244         return Result("invalid TOLERANCE for EXPECT RMSE_BUFFER");
3245 
3246       Result r = token->ConvertToDouble();
3247       if (!r.IsSuccess())
3248         return r;
3249 
3250       cmd->SetTolerance(token->AsFloat());
3251     } else if (type == "EQ_HISTOGRAM_EMD_BUFFER") {
3252       cmd->SetComparator(CompareBufferCommand::Comparator::kHistogramEmd);
3253 
3254       token = tokenizer_->NextToken();
3255       if (!token->IsIdentifier() && token->AsString() == "TOLERANCE")
3256         return Result("missing TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
3257 
3258       token = tokenizer_->NextToken();
3259       if (!token->IsInteger() && !token->IsDouble())
3260         return Result("invalid TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
3261 
3262       Result r = token->ConvertToDouble();
3263       if (!r.IsSuccess())
3264         return r;
3265 
3266       cmd->SetTolerance(token->AsFloat());
3267     }
3268 
3269     command_list_.push_back(std::move(cmd));
3270 
3271     // Early return
3272     return ValidateEndOfStatement("EXPECT " + type + " command");
3273   }
3274 
3275   if (token->AsString() != "IDX")
3276     return Result("missing IDX in EXPECT command");
3277 
3278   token = tokenizer_->NextToken();
3279   if (!token->IsInteger() || token->AsInt32() < 0)
3280     return Result("invalid X value in EXPECT command");
3281   token->ConvertToDouble();
3282   float x = token->AsFloat();
3283 
3284   bool has_y_val = false;
3285   float y = 0;
3286   token = tokenizer_->NextToken();
3287   if (token->IsInteger()) {
3288     has_y_val = true;
3289 
3290     if (token->AsInt32() < 0)
3291       return Result("invalid Y value in EXPECT command");
3292     token->ConvertToDouble();
3293     y = token->AsFloat();
3294 
3295     token = tokenizer_->NextToken();
3296   }
3297 
3298   if (token->IsIdentifier() && token->AsString() == "SIZE") {
3299     if (!has_y_val)
3300       return Result("invalid Y value in EXPECT command");
3301 
3302     auto probe = MakeUnique<ProbeCommand>(buffer);
3303     probe->SetLine(line);
3304     probe->SetX(x);
3305     probe->SetY(y);
3306     probe->SetProbeRect();
3307 
3308     token = tokenizer_->NextToken();
3309     if (!token->IsInteger() || token->AsInt32() <= 0)
3310       return Result("invalid width in EXPECT command");
3311     token->ConvertToDouble();
3312     probe->SetWidth(token->AsFloat());
3313 
3314     token = tokenizer_->NextToken();
3315     if (!token->IsInteger() || token->AsInt32() <= 0)
3316       return Result("invalid height in EXPECT command");
3317     token->ConvertToDouble();
3318     probe->SetHeight(token->AsFloat());
3319 
3320     token = tokenizer_->NextToken();
3321     if (!token->IsIdentifier()) {
3322       return Result("invalid token in EXPECT command:" +
3323                     token->ToOriginalString());
3324     }
3325 
3326     if (token->AsString() == "EQ_RGBA") {
3327       probe->SetIsRGBA();
3328     } else if (token->AsString() != "EQ_RGB") {
3329       return Result("unknown comparator type in EXPECT: " +
3330                     token->ToOriginalString());
3331     }
3332 
3333     token = tokenizer_->NextToken();
3334     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3335       return Result("invalid R value in EXPECT command");
3336     token->ConvertToDouble();
3337     probe->SetR(token->AsFloat() / 255.f);
3338 
3339     token = tokenizer_->NextToken();
3340     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3341       return Result("invalid G value in EXPECT command");
3342     token->ConvertToDouble();
3343     probe->SetG(token->AsFloat() / 255.f);
3344 
3345     token = tokenizer_->NextToken();
3346     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3347       return Result("invalid B value in EXPECT command");
3348     token->ConvertToDouble();
3349     probe->SetB(token->AsFloat() / 255.f);
3350 
3351     if (probe->IsRGBA()) {
3352       token = tokenizer_->NextToken();
3353       if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3354         return Result("invalid A value in EXPECT command");
3355       token->ConvertToDouble();
3356       probe->SetA(token->AsFloat() / 255.f);
3357     }
3358 
3359     token = tokenizer_->NextToken();
3360     if (token->IsIdentifier() && token->AsString() == "TOLERANCE") {
3361       std::vector<Probe::Tolerance> tolerances;
3362 
3363       Result r = ParseTolerances(&tolerances);
3364 
3365       if (!r.IsSuccess())
3366         return r;
3367 
3368       if (tolerances.empty())
3369         return Result("TOLERANCE specified but no tolerances provided");
3370 
3371       if (!probe->IsRGBA() && tolerances.size() > 3) {
3372         return Result(
3373             "TOLERANCE for an RGB comparison has a maximum of 3 values");
3374       }
3375 
3376       if (tolerances.size() > 4) {
3377         return Result(
3378             "TOLERANCE for an RGBA comparison has a maximum of 4 values");
3379       }
3380 
3381       probe->SetTolerances(std::move(tolerances));
3382       token = tokenizer_->NextToken();
3383     }
3384 
3385     if (!token->IsEOL() && !token->IsEOS()) {
3386       return Result("extra parameters after EXPECT command: " +
3387                     token->ToOriginalString());
3388     }
3389 
3390     command_list_.push_back(std::move(probe));
3391 
3392     return {};
3393   }
3394 
3395   auto probe = MakeUnique<ProbeSSBOCommand>(buffer);
3396   probe->SetLine(line);
3397 
3398   if (token->IsIdentifier() && token->AsString() == "TOLERANCE") {
3399     std::vector<Probe::Tolerance> tolerances;
3400 
3401     Result r = ParseTolerances(&tolerances);
3402 
3403     if (!r.IsSuccess())
3404       return r;
3405 
3406     if (tolerances.empty())
3407       return Result("TOLERANCE specified but no tolerances provided");
3408     if (tolerances.size() > 4)
3409       return Result("TOLERANCE has a maximum of 4 values");
3410 
3411     probe->SetTolerances(std::move(tolerances));
3412     token = tokenizer_->NextToken();
3413   }
3414 
3415   if (!token->IsIdentifier() || !IsComparator(token->AsString())) {
3416     return Result("unexpected token in EXPECT command: " +
3417                   token->ToOriginalString());
3418   }
3419 
3420   if (has_y_val)
3421     return Result("Y value not needed for non-color comparator");
3422 
3423   auto cmp = ToComparator(token->AsString());
3424   if (probe->HasTolerances()) {
3425     if (cmp != ProbeSSBOCommand::Comparator::kEqual)
3426       return Result("TOLERANCE only available with EQ probes");
3427 
3428     cmp = ProbeSSBOCommand::Comparator::kFuzzyEqual;
3429   }
3430 
3431   probe->SetComparator(cmp);
3432   probe->SetFormat(buffer->GetFormat());
3433   probe->SetOffset(static_cast<uint32_t>(x));
3434 
3435   std::vector<Value> values;
3436   Result r = ParseValues("EXPECT", buffer->GetFormat(), &values);
3437   if (!r.IsSuccess())
3438     return r;
3439 
3440   if (values.empty())
3441     return Result("missing comparison values for EXPECT command");
3442 
3443   probe->SetValues(std::move(values));
3444   command_list_.push_back(std::move(probe));
3445 
3446   return {};
3447 }
3448 
ParseCopy()3449 Result Parser::ParseCopy() {
3450   auto token = tokenizer_->NextToken();
3451   if (token->IsEOL() || token->IsEOS())
3452     return Result("missing buffer name after COPY");
3453   if (!token->IsIdentifier())
3454     return Result("invalid buffer name after COPY");
3455 
3456   size_t line = tokenizer_->GetCurrentLine();
3457 
3458   auto name = token->AsString();
3459   if (name == "TO")
3460     return Result("missing buffer name between COPY and TO");
3461 
3462   Buffer* buffer_from = script_->GetBuffer(name);
3463   if (!buffer_from)
3464     return Result("COPY origin buffer was not declared");
3465 
3466   token = tokenizer_->NextToken();
3467   if (token->IsEOL() || token->IsEOS())
3468     return Result("missing 'TO' after COPY and buffer name");
3469   if (!token->IsIdentifier())
3470     return Result("expected 'TO' after COPY and buffer name");
3471 
3472   name = token->AsString();
3473   if (name != "TO")
3474     return Result("expected 'TO' after COPY and buffer name");
3475 
3476   token = tokenizer_->NextToken();
3477   if (token->IsEOL() || token->IsEOS())
3478     return Result("missing buffer name after TO");
3479   if (!token->IsIdentifier())
3480     return Result("invalid buffer name after TO");
3481 
3482   name = token->AsString();
3483   Buffer* buffer_to = script_->GetBuffer(name);
3484   if (!buffer_to)
3485     return Result("COPY destination buffer was not declared");
3486 
3487   // Set destination buffer to mirror origin buffer
3488   buffer_to->SetWidth(buffer_from->GetWidth());
3489   buffer_to->SetHeight(buffer_from->GetHeight());
3490   buffer_to->SetElementCount(buffer_from->ElementCount());
3491 
3492   if (buffer_from == buffer_to)
3493     return Result("COPY origin and destination buffers are identical");
3494 
3495   auto cmd = MakeUnique<CopyCommand>(buffer_from, buffer_to);
3496   cmd->SetLine(line);
3497   command_list_.push_back(std::move(cmd));
3498 
3499   return ValidateEndOfStatement("COPY command");
3500 }
3501 
ParseClearColor()3502 Result Parser::ParseClearColor() {
3503   auto token = tokenizer_->NextToken();
3504   if (!token->IsIdentifier())
3505     return Result("missing pipeline name for CLEAR_COLOR command");
3506 
3507   size_t line = tokenizer_->GetCurrentLine();
3508 
3509   auto* pipeline = script_->GetPipeline(token->AsString());
3510   if (!pipeline) {
3511     return Result("unknown pipeline for CLEAR_COLOR command: " +
3512                   token->AsString());
3513   }
3514   if (!pipeline->IsGraphics()) {
3515     return Result("CLEAR_COLOR command requires graphics pipeline");
3516   }
3517 
3518   auto cmd = MakeUnique<ClearColorCommand>(pipeline);
3519   cmd->SetLine(line);
3520 
3521   token = tokenizer_->NextToken();
3522   if (token->IsEOL() || token->IsEOS())
3523     return Result("missing R value for CLEAR_COLOR command");
3524   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3525     return Result("invalid R value for CLEAR_COLOR command: " +
3526                   token->ToOriginalString());
3527   }
3528   token->ConvertToDouble();
3529   cmd->SetR(token->AsFloat() / 255.f);
3530 
3531   token = tokenizer_->NextToken();
3532   if (token->IsEOL() || token->IsEOS())
3533     return Result("missing G value for CLEAR_COLOR command");
3534   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3535     return Result("invalid G value for CLEAR_COLOR command: " +
3536                   token->ToOriginalString());
3537   }
3538   token->ConvertToDouble();
3539   cmd->SetG(token->AsFloat() / 255.f);
3540 
3541   token = tokenizer_->NextToken();
3542   if (token->IsEOL() || token->IsEOS())
3543     return Result("missing B value for CLEAR_COLOR command");
3544   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3545     return Result("invalid B value for CLEAR_COLOR command: " +
3546                   token->ToOriginalString());
3547   }
3548   token->ConvertToDouble();
3549   cmd->SetB(token->AsFloat() / 255.f);
3550 
3551   token = tokenizer_->NextToken();
3552   if (token->IsEOL() || token->IsEOS())
3553     return Result("missing A value for CLEAR_COLOR command");
3554   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3555     return Result("invalid A value for CLEAR_COLOR command: " +
3556                   token->ToOriginalString());
3557   }
3558   token->ConvertToDouble();
3559   cmd->SetA(token->AsFloat() / 255.f);
3560 
3561   command_list_.push_back(std::move(cmd));
3562   return ValidateEndOfStatement("CLEAR_COLOR command");
3563 }
3564 
ParseClearDepth()3565 Result Parser::ParseClearDepth() {
3566   auto token = tokenizer_->NextToken();
3567   if (!token->IsIdentifier())
3568     return Result("missing pipeline name for CLEAR_DEPTH command");
3569 
3570   size_t line = tokenizer_->GetCurrentLine();
3571 
3572   auto* pipeline = script_->GetPipeline(token->AsString());
3573   if (!pipeline) {
3574     return Result("unknown pipeline for CLEAR_DEPTH command: " +
3575                   token->AsString());
3576   }
3577   if (!pipeline->IsGraphics()) {
3578     return Result("CLEAR_DEPTH command requires graphics pipeline");
3579   }
3580 
3581   auto cmd = MakeUnique<ClearDepthCommand>(pipeline);
3582   cmd->SetLine(line);
3583 
3584   token = tokenizer_->NextToken();
3585   if (token->IsEOL() || token->IsEOS())
3586     return Result("missing value for CLEAR_DEPTH command");
3587   if (!token->IsDouble()) {
3588     return Result("invalid value for CLEAR_DEPTH command: " +
3589                   token->ToOriginalString());
3590   }
3591   cmd->SetValue(token->AsFloat());
3592 
3593   command_list_.push_back(std::move(cmd));
3594   return ValidateEndOfStatement("CLEAR_DEPTH command");
3595 }
3596 
ParseClearStencil()3597 Result Parser::ParseClearStencil() {
3598   auto token = tokenizer_->NextToken();
3599   if (!token->IsIdentifier())
3600     return Result("missing pipeline name for CLEAR_STENCIL command");
3601 
3602   size_t line = tokenizer_->GetCurrentLine();
3603 
3604   auto* pipeline = script_->GetPipeline(token->AsString());
3605   if (!pipeline) {
3606     return Result("unknown pipeline for CLEAR_STENCIL command: " +
3607                   token->AsString());
3608   }
3609   if (!pipeline->IsGraphics()) {
3610     return Result("CLEAR_STENCIL command requires graphics pipeline");
3611   }
3612 
3613   auto cmd = MakeUnique<ClearStencilCommand>(pipeline);
3614   cmd->SetLine(line);
3615 
3616   token = tokenizer_->NextToken();
3617   if (token->IsEOL() || token->IsEOS())
3618     return Result("missing value for CLEAR_STENCIL command");
3619   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3620     return Result("invalid value for CLEAR_STENCIL command: " +
3621                   token->ToOriginalString());
3622   }
3623   cmd->SetValue(token->AsUint32());
3624 
3625   command_list_.push_back(std::move(cmd));
3626   return ValidateEndOfStatement("CLEAR_STENCIL command");
3627 }
3628 
ParseDeviceFeature()3629 Result Parser::ParseDeviceFeature() {
3630   auto token = tokenizer_->NextToken();
3631   if (token->IsEOS() || token->IsEOL())
3632     return Result("missing feature name for DEVICE_FEATURE command");
3633   if (!token->IsIdentifier())
3634     return Result("invalid feature name for DEVICE_FEATURE command");
3635   if (!script_->IsKnownFeature(token->AsString()))
3636     return Result("unknown feature name for DEVICE_FEATURE command");
3637 
3638   script_->AddRequiredFeature(token->AsString());
3639 
3640   return ValidateEndOfStatement("DEVICE_FEATURE command");
3641 }
3642 
ParseRepeat()3643 Result Parser::ParseRepeat() {
3644   auto token = tokenizer_->NextToken();
3645   if (token->IsEOL() || token->IsEOL())
3646     return Result("missing count parameter for REPEAT command");
3647   if (!token->IsInteger()) {
3648     return Result("invalid count parameter for REPEAT command: " +
3649                   token->ToOriginalString());
3650   }
3651   if (token->AsInt32() <= 0)
3652     return Result("count parameter must be > 0 for REPEAT command");
3653 
3654   uint32_t count = token->AsUint32();
3655 
3656   std::vector<std::unique_ptr<Command>> cur_commands;
3657   std::swap(cur_commands, command_list_);
3658 
3659   for (token = tokenizer_->NextToken(); !token->IsEOS();
3660        token = tokenizer_->NextToken()) {
3661     if (token->IsEOL())
3662       continue;
3663     if (!token->IsIdentifier())
3664       return Result("expected identifier");
3665 
3666     std::string tok = token->AsString();
3667     if (tok == "END")
3668       break;
3669     if (!IsRepeatable(tok))
3670       return Result("unknown token: " + tok);
3671 
3672     Result r = ParseRepeatableCommand(tok);
3673     if (!r.IsSuccess())
3674       return r;
3675   }
3676   if (!token->IsIdentifier() || token->AsString() != "END")
3677     return Result("missing END for REPEAT command");
3678 
3679   auto cmd = MakeUnique<RepeatCommand>(count);
3680   cmd->SetCommands(std::move(command_list_));
3681 
3682   std::swap(cur_commands, command_list_);
3683   command_list_.push_back(std::move(cmd));
3684 
3685   return ValidateEndOfStatement("REPEAT command");
3686 }
3687 
ParseDerivePipelineBlock()3688 Result Parser::ParseDerivePipelineBlock() {
3689   auto token = tokenizer_->NextToken();
3690   if (!token->IsIdentifier() || token->AsString() == "FROM")
3691     return Result("missing pipeline name for DERIVE_PIPELINE command");
3692 
3693   std::string name = token->AsString();
3694   if (script_->GetPipeline(name) != nullptr)
3695     return Result("duplicate pipeline name for DERIVE_PIPELINE command");
3696 
3697   token = tokenizer_->NextToken();
3698   if (!token->IsIdentifier() || token->AsString() != "FROM")
3699     return Result("missing FROM in DERIVE_PIPELINE command");
3700 
3701   token = tokenizer_->NextToken();
3702   if (!token->IsIdentifier())
3703     return Result("missing parent pipeline name in DERIVE_PIPELINE command");
3704 
3705   Pipeline* parent = script_->GetPipeline(token->AsString());
3706   if (!parent)
3707     return Result("unknown parent pipeline in DERIVE_PIPELINE command");
3708 
3709   Result r = ValidateEndOfStatement("DERIVE_PIPELINE command");
3710   if (!r.IsSuccess())
3711     return r;
3712 
3713   auto pipeline = parent->Clone();
3714   pipeline->SetName(name);
3715 
3716   return ParsePipelineBody("DERIVE_PIPELINE", std::move(pipeline));
3717 }
3718 
ParseDeviceExtension()3719 Result Parser::ParseDeviceExtension() {
3720   auto token = tokenizer_->NextToken();
3721   if (token->IsEOL() || token->IsEOS())
3722     return Result("DEVICE_EXTENSION missing name");
3723   if (!token->IsIdentifier()) {
3724     return Result("DEVICE_EXTENSION invalid name: " +
3725                   token->ToOriginalString());
3726   }
3727 
3728   script_->AddRequiredDeviceExtension(token->AsString());
3729 
3730   return ValidateEndOfStatement("DEVICE_EXTENSION command");
3731 }
3732 
ParseInstanceExtension()3733 Result Parser::ParseInstanceExtension() {
3734   auto token = tokenizer_->NextToken();
3735   if (token->IsEOL() || token->IsEOS())
3736     return Result("INSTANCE_EXTENSION missing name");
3737   if (!token->IsIdentifier()) {
3738     return Result("INSTANCE_EXTENSION invalid name: " +
3739                   token->ToOriginalString());
3740   }
3741 
3742   script_->AddRequiredInstanceExtension(token->AsString());
3743 
3744   return ValidateEndOfStatement("INSTANCE_EXTENSION command");
3745 }
3746 
ParseSet()3747 Result Parser::ParseSet() {
3748   auto token = tokenizer_->NextToken();
3749   if (!token->IsIdentifier() || token->AsString() != "ENGINE_DATA")
3750     return Result("SET missing ENGINE_DATA");
3751 
3752   token = tokenizer_->NextToken();
3753   if (token->IsEOS() || token->IsEOL())
3754     return Result("SET missing variable to be set");
3755 
3756   if (!token->IsIdentifier())
3757     return Result("SET invalid variable to set: " + token->ToOriginalString());
3758 
3759   if (token->AsString() != "fence_timeout_ms")
3760     return Result("SET unknown variable provided: " + token->AsString());
3761 
3762   token = tokenizer_->NextToken();
3763   if (token->IsEOS() || token->IsEOL())
3764     return Result("SET missing value for fence_timeout_ms");
3765   if (!token->IsInteger())
3766     return Result("SET invalid value for fence_timeout_ms, must be uint32");
3767 
3768   script_->GetEngineData().fence_timeout_ms = token->AsUint32();
3769 
3770   return ValidateEndOfStatement("SET command");
3771 }
3772 
ParseSampler()3773 Result Parser::ParseSampler() {
3774   auto token = tokenizer_->NextToken();
3775   if (!token->IsIdentifier())
3776     return Result("invalid token when looking for sampler name");
3777 
3778   auto sampler = MakeUnique<Sampler>();
3779   sampler->SetName(token->AsString());
3780 
3781   token = tokenizer_->NextToken();
3782   while (!token->IsEOS() && !token->IsEOL()) {
3783     if (!token->IsIdentifier())
3784       return Result("invalid token when looking for sampler parameters");
3785 
3786     auto param = token->AsString();
3787     if (param == "MAG_FILTER") {
3788       token = tokenizer_->NextToken();
3789 
3790       if (!token->IsIdentifier())
3791         return Result("invalid token when looking for MAG_FILTER value");
3792 
3793       auto filter = token->AsString();
3794 
3795       if (filter == "linear")
3796         sampler->SetMagFilter(FilterType::kLinear);
3797       else if (filter == "nearest")
3798         sampler->SetMagFilter(FilterType::kNearest);
3799       else
3800         return Result("invalid MAG_FILTER value " + filter);
3801     } else if (param == "MIN_FILTER") {
3802       token = tokenizer_->NextToken();
3803 
3804       if (!token->IsIdentifier())
3805         return Result("invalid token when looking for MIN_FILTER value");
3806 
3807       auto filter = token->AsString();
3808 
3809       if (filter == "linear")
3810         sampler->SetMinFilter(FilterType::kLinear);
3811       else if (filter == "nearest")
3812         sampler->SetMinFilter(FilterType::kNearest);
3813       else
3814         return Result("invalid MIN_FILTER value " + filter);
3815     } else if (param == "ADDRESS_MODE_U") {
3816       token = tokenizer_->NextToken();
3817 
3818       if (!token->IsIdentifier())
3819         return Result("invalid token when looking for ADDRESS_MODE_U value");
3820 
3821       auto mode_str = token->AsString();
3822       auto mode = StrToAddressMode(mode_str);
3823 
3824       if (mode == AddressMode::kUnknown)
3825         return Result("invalid ADDRESS_MODE_U value " + mode_str);
3826 
3827       sampler->SetAddressModeU(mode);
3828     } else if (param == "ADDRESS_MODE_V") {
3829       token = tokenizer_->NextToken();
3830 
3831       if (!token->IsIdentifier())
3832         return Result("invalid token when looking for ADDRESS_MODE_V value");
3833 
3834       auto mode_str = token->AsString();
3835       auto mode = StrToAddressMode(mode_str);
3836 
3837       if (mode == AddressMode::kUnknown)
3838         return Result("invalid ADDRESS_MODE_V value " + mode_str);
3839 
3840       sampler->SetAddressModeV(mode);
3841     } else if (param == "ADDRESS_MODE_W") {
3842       token = tokenizer_->NextToken();
3843 
3844       if (!token->IsIdentifier())
3845         return Result("invalid token when looking for ADDRESS_MODE_W value");
3846 
3847       auto mode_str = token->AsString();
3848       auto mode = StrToAddressMode(mode_str);
3849 
3850       if (mode == AddressMode::kUnknown)
3851         return Result("invalid ADDRESS_MODE_W value " + mode_str);
3852 
3853       sampler->SetAddressModeW(mode);
3854     } else if (param == "BORDER_COLOR") {
3855       token = tokenizer_->NextToken();
3856 
3857       if (!token->IsIdentifier())
3858         return Result("invalid token when looking for BORDER_COLOR value");
3859 
3860       auto color_str = token->AsString();
3861 
3862       if (color_str == "float_transparent_black")
3863         sampler->SetBorderColor(BorderColor::kFloatTransparentBlack);
3864       else if (color_str == "int_transparent_black")
3865         sampler->SetBorderColor(BorderColor::kIntTransparentBlack);
3866       else if (color_str == "float_opaque_black")
3867         sampler->SetBorderColor(BorderColor::kFloatOpaqueBlack);
3868       else if (color_str == "int_opaque_black")
3869         sampler->SetBorderColor(BorderColor::kIntOpaqueBlack);
3870       else if (color_str == "float_opaque_white")
3871         sampler->SetBorderColor(BorderColor::kFloatOpaqueWhite);
3872       else if (color_str == "int_opaque_white")
3873         sampler->SetBorderColor(BorderColor::kIntOpaqueWhite);
3874       else
3875         return Result("invalid BORDER_COLOR value " + color_str);
3876     } else if (param == "MIN_LOD") {
3877       token = tokenizer_->NextToken();
3878 
3879       if (!token->IsDouble())
3880         return Result("invalid token when looking for MIN_LOD value");
3881 
3882       sampler->SetMinLOD(token->AsFloat());
3883     } else if (param == "MAX_LOD") {
3884       token = tokenizer_->NextToken();
3885 
3886       if (!token->IsDouble())
3887         return Result("invalid token when looking for MAX_LOD value");
3888 
3889       sampler->SetMaxLOD(token->AsFloat());
3890     } else if (param == "NORMALIZED_COORDS") {
3891       sampler->SetNormalizedCoords(true);
3892     } else if (param == "UNNORMALIZED_COORDS") {
3893       sampler->SetNormalizedCoords(false);
3894       sampler->SetMinLOD(0.0f);
3895       sampler->SetMaxLOD(0.0f);
3896     } else {
3897       return Result("unexpected sampler parameter " + param);
3898     }
3899 
3900     token = tokenizer_->NextToken();
3901   }
3902 
3903   if (sampler->GetMaxLOD() < sampler->GetMinLOD()) {
3904     return Result("max LOD needs to be greater than or equal to min LOD");
3905   }
3906 
3907   return script_->AddSampler(std::move(sampler));
3908 }
3909 
ParseTolerances(std::vector<Probe::Tolerance> * tolerances)3910 Result Parser::ParseTolerances(std::vector<Probe::Tolerance>* tolerances) {
3911   auto token = tokenizer_->PeekNextToken();
3912   while (!token->IsEOL() && !token->IsEOS()) {
3913     if (!token->IsInteger() && !token->IsDouble())
3914       break;
3915 
3916     token = tokenizer_->NextToken();
3917     Result r = token->ConvertToDouble();
3918     if (!r.IsSuccess())
3919       return r;
3920 
3921     double value = token->AsDouble();
3922     token = tokenizer_->PeekNextToken();
3923     if (token->IsIdentifier() && token->AsString() == "%") {
3924       tolerances->push_back(Probe::Tolerance{true, value});
3925       tokenizer_->NextToken();
3926       token = tokenizer_->PeekNextToken();
3927     } else {
3928       tolerances->push_back(Probe::Tolerance{false, value});
3929     }
3930   }
3931 
3932   return {};
3933 }
3934 
ParseVirtualFile()3935 Result Parser::ParseVirtualFile() {
3936   auto token = tokenizer_->NextToken();
3937   if (!token->IsIdentifier() && !token->IsString())
3938     return Result("invalid virtual file path");
3939 
3940   auto path = token->AsString();
3941 
3942   auto r = ValidateEndOfStatement("VIRTUAL_FILE command");
3943   if (!r.IsSuccess())
3944     return r;
3945 
3946   auto data = tokenizer_->ExtractToNext("END");
3947 
3948   token = tokenizer_->NextToken();
3949   if (!token->IsIdentifier() || token->AsString() != "END")
3950     return Result("VIRTUAL_FILE missing END command");
3951 
3952   return script_->AddVirtualFile(path, data);
3953 }
3954 
3955 }  // namespace amberscript
3956 }  // namespace amber
3957