• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Amber Authors.
2 // Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "src/amberscript/parser.h"
17 
18 #include <algorithm>
19 #include <cassert>
20 #include <limits>
21 #include <map>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "amber/vulkan_header.h"
27 #include "src/image.h"
28 #include "src/make_unique.h"
29 #include "src/sampler.h"
30 #include "src/shader_data.h"
31 #include "src/tokenizer.h"
32 #include "src/type_parser.h"
33 
34 namespace amber {
35 namespace amberscript {
36 namespace {
37 
IsComparator(const std::string & in)38 bool IsComparator(const std::string& in) {
39   return in == "EQ" || in == "NE" || in == "GT" || in == "LT" || in == "GE" ||
40          in == "LE";
41 }
42 
ToComparator(const std::string & in)43 ProbeSSBOCommand::Comparator ToComparator(const std::string& in) {
44   if (in == "EQ")
45     return ProbeSSBOCommand::Comparator::kEqual;
46   if (in == "NE")
47     return ProbeSSBOCommand::Comparator::kNotEqual;
48   if (in == "GT")
49     return ProbeSSBOCommand::Comparator::kGreater;
50   if (in == "LT")
51     return ProbeSSBOCommand::Comparator::kLess;
52   if (in == "GE")
53     return ProbeSSBOCommand::Comparator::kGreaterOrEqual;
54 
55   assert(in == "LE");
56   return ProbeSSBOCommand::Comparator::kLessOrEqual;
57 }
58 
ToType(const std::string & str_in)59 std::unique_ptr<type::Type> ToType(const std::string& str_in) {
60   std::string str = str_in;
61 
62   bool is_array = false;
63   if (str.length() > 2 && str[str.length() - 2] == '[' &&
64       str[str.length() - 1] == ']') {
65     is_array = true;
66     str = str.substr(0, str.length() - 2);
67   }
68 
69   TypeParser parser;
70   std::unique_ptr<type::Type> type;
71   if (str == "int8") {
72     type = parser.Parse("R8_SINT");
73   } else if (str == "int16") {
74     type = parser.Parse("R16_SINT");
75   } else if (str == "int32") {
76     type = parser.Parse("R32_SINT");
77   } else if (str == "int64") {
78     type = parser.Parse("R64_SINT");
79   } else if (str == "uint8") {
80     type = parser.Parse("R8_UINT");
81   } else if (str == "uint16") {
82     type = parser.Parse("R16_UINT");
83   } else if (str == "uint32") {
84     type = parser.Parse("R32_UINT");
85   } else if (str == "uint64") {
86     type = parser.Parse("R64_UINT");
87   } else if (str == "float16") {
88     type = parser.Parse("R16_SFLOAT");
89   } else if (str == "float") {
90     type = parser.Parse("R32_SFLOAT");
91   } else if (str == "double") {
92     type = parser.Parse("R64_SFLOAT");
93   } else if (str.length() > 7 && str.substr(0, 3) == "vec") {
94     if (str[4] != '<' || str[str.length() - 1] != '>')
95       return nullptr;
96 
97     int component_count = str[3] - '0';
98     if (component_count < 2 || component_count > 4)
99       return nullptr;
100 
101     type = ToType(str.substr(5, str.length() - 6));
102     if (!type)
103       return nullptr;
104 
105     if (!type->IsNumber() || type->IsArray() || type->IsVec() ||
106         type->IsMatrix()) {
107       return nullptr;
108     }
109 
110     type->SetRowCount(static_cast<uint32_t>(component_count));
111   } else if (str.length() > 9 && str.substr(0, 3) == "mat") {
112     if (str[4] != 'x' || str[6] != '<' || str[str.length() - 1] != '>')
113       return nullptr;
114 
115     int column_count = str[3] - '0';
116     if (column_count < 2 || column_count > 4)
117       return nullptr;
118 
119     int row_count = str[5] - '0';
120     if (row_count < 2 || row_count > 4)
121       return nullptr;
122 
123     type = ToType(str.substr(7, str.length() - 8));
124     if (!type)
125       return nullptr;
126     if (!type->IsNumber() || type->IsArray() || type->IsVec() ||
127         type->IsMatrix()) {
128       return nullptr;
129     }
130 
131     type->SetRowCount(static_cast<uint32_t>(row_count));
132     type->SetColumnCount(static_cast<uint32_t>(column_count));
133   }
134 
135   if (!type)
136     return nullptr;
137   if (is_array)
138     type->SetIsRuntimeArray();
139 
140   return type;
141 }
142 
StrToAddressMode(std::string str)143 AddressMode StrToAddressMode(std::string str) {
144   if (str == "repeat")
145     return AddressMode::kRepeat;
146   if (str == "mirrored_repeat")
147     return AddressMode::kMirroredRepeat;
148   if (str == "clamp_to_edge")
149     return AddressMode::kClampToEdge;
150   if (str == "clamp_to_border")
151     return AddressMode::kClampToBorder;
152   if (str == "mirror_clamp_to_edge")
153     return AddressMode::kMirrorClampToEdge;
154 
155   return AddressMode::kUnknown;
156 }
157 
StrToCompareOp(const std::string & str)158 CompareOp StrToCompareOp(const std::string& str) {
159   if (str == "never")
160     return CompareOp::kNever;
161   if (str == "less")
162     return CompareOp::kLess;
163   if (str == "equal")
164     return CompareOp::kEqual;
165   if (str == "less_or_equal")
166     return CompareOp::kLessOrEqual;
167   if (str == "greater")
168     return CompareOp::kGreater;
169   if (str == "not_equal")
170     return CompareOp::kNotEqual;
171   if (str == "greater_or_equal")
172     return CompareOp::kGreaterOrEqual;
173   if (str == "always")
174     return CompareOp::kAlways;
175 
176   return CompareOp::kUnknown;
177 }
178 
StrToStencilOp(const std::string & str)179 StencilOp StrToStencilOp(const std::string& str) {
180   if (str == "keep")
181     return StencilOp::kKeep;
182   if (str == "zero")
183     return StencilOp::kZero;
184   if (str == "replace")
185     return StencilOp::kReplace;
186   if (str == "increment_and_clamp")
187     return StencilOp::kIncrementAndClamp;
188   if (str == "decrement_and_clamp")
189     return StencilOp::kDecrementAndClamp;
190   if (str == "invert")
191     return StencilOp::kInvert;
192   if (str == "increment_and_wrap")
193     return StencilOp::kIncrementAndWrap;
194   if (str == "decrement_and_wrap")
195     return StencilOp::kDecrementAndWrap;
196 
197   return StencilOp::kUnknown;
198 }
199 
ParseBufferData(Buffer * buffer,Tokenizer * tokenizer,bool from_data_file)200 Result ParseBufferData(Buffer* buffer,
201                        Tokenizer* tokenizer,
202                        bool from_data_file) {
203   auto fmt = buffer->GetFormat();
204   const auto& segs = fmt->GetSegments();
205   size_t seg_idx = 0;
206   uint32_t value_count = 0;
207 
208   std::vector<Value> values;
209   for (auto token = tokenizer->NextToken();; token = tokenizer->NextToken()) {
210     if (token->IsEOL())
211       continue;
212     if (token->IsEOS()) {
213       if (from_data_file) {
214         break;
215       } else {
216         return Result("missing BUFFER END command");
217       }
218     }
219     if (token->IsIdentifier() && token->AsString() == "END")
220       break;
221     if (!token->IsInteger() && !token->IsDouble() && !token->IsHex())
222       return Result("invalid BUFFER data value: " + token->ToOriginalString());
223 
224     while (segs[seg_idx].IsPadding()) {
225       ++seg_idx;
226       if (seg_idx >= segs.size())
227         seg_idx = 0;
228     }
229 
230     Value v;
231     if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
232       token->ConvertToDouble();
233 
234       double val = token->IsHex() ? static_cast<double>(token->AsHex())
235                                   : token->AsDouble();
236       v.SetDoubleValue(val);
237       ++value_count;
238     } else {
239       if (token->IsDouble()) {
240         return Result("invalid BUFFER data value: " +
241                       token->ToOriginalString());
242       }
243 
244       uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
245       v.SetIntValue(val);
246       ++value_count;
247     }
248     ++seg_idx;
249     if (seg_idx >= segs.size())
250       seg_idx = 0;
251 
252     values.emplace_back(v);
253   }
254   // Write final padding bytes
255   while (segs[seg_idx].IsPadding()) {
256     ++seg_idx;
257     if (seg_idx >= segs.size())
258       break;
259   }
260 
261   buffer->SetValueCount(value_count);
262   Result r = buffer->SetData(std::move(values));
263   if (!r.IsSuccess())
264     return r;
265 
266   return {};
267 }
268 
269 constexpr uint32_t valid_samples[] = {1, 2, 4, 8, 16, 32, 64};
270 
IsValidSampleCount(uint32_t samples)271 bool IsValidSampleCount(uint32_t samples) {
272   return (std::find(std::begin(valid_samples), std::end(valid_samples),
273                     samples) != std::end(valid_samples));
274 }
275 
276 }  // namespace
277 
Parser()278 Parser::Parser() : amber::Parser(nullptr) {}
Parser(Delegate * delegate)279 Parser::Parser(Delegate* delegate) : amber::Parser(delegate) {}
280 
281 Parser::~Parser() = default;
282 
make_error(const std::string & err)283 std::string Parser::make_error(const std::string& err) {
284   return std::to_string(tokenizer_->GetCurrentLine()) + ": " + err;
285 }
286 
Parse(const std::string & data)287 Result Parser::Parse(const std::string& data) {
288   tokenizer_ = MakeUnique<Tokenizer>(data);
289 
290   for (auto token = tokenizer_->NextToken(); !token->IsEOS();
291        token = tokenizer_->NextToken()) {
292     if (token->IsEOL())
293       continue;
294     if (!token->IsIdentifier())
295       return Result(make_error("expected identifier"));
296 
297     Result r;
298     std::string tok = token->AsString();
299     if (IsRepeatable(tok)) {
300       r = ParseRepeatableCommand(tok);
301     } else if (tok == "BUFFER") {
302       r = ParseBuffer();
303     } else if (tok == "DERIVE_PIPELINE") {
304       r = ParseDerivePipelineBlock();
305     } else if (tok == "DEVICE_FEATURE") {
306       r = ParseDeviceFeature();
307     } else if (tok == "DEVICE_EXTENSION") {
308       r = ParseDeviceExtension();
309     } else if (tok == "DEVICE_PROPERTY") {
310       r = ParseDeviceProperty();
311     } else if (tok == "IMAGE") {
312       r = ParseImage();
313     } else if (tok == "INSTANCE_EXTENSION") {
314       r = ParseInstanceExtension();
315     } else if (tok == "PIPELINE") {
316       r = ParsePipelineBlock();
317     } else if (tok == "REPEAT") {
318       r = ParseRepeat();
319     } else if (tok == "SET") {
320       r = ParseSet();
321     } else if (tok == "SHADER") {
322       r = ParseShaderBlock();
323     } else if (tok == "STRUCT") {
324       r = ParseStruct();
325     } else if (tok == "SAMPLER") {
326       r = ParseSampler();
327     } else if (tok == "VIRTUAL_FILE") {
328       r = ParseVirtualFile();
329     } else if (tok == "ACCELERATION_STRUCTURE") {
330       r = ParseAS();
331     } else {
332       r = Result("unknown token: " + tok);
333     }
334     if (!r.IsSuccess())
335       return Result(make_error(r.Error()));
336   }
337   script_->SetCommands(std::move(command_list_));
338 
339   // Generate any needed color attachments. This is done before
340   // validating in case one of the pipelines specifies the framebuffer size
341   // it needs to be verified against all other pipelines.
342   for (const auto& pipeline : script_->GetPipelines()) {
343     // Add a color attachment if needed
344     if (pipeline->GetColorAttachments().empty()) {
345       auto* buf = script_->GetBuffer(Pipeline::kGeneratedColorBuffer);
346       if (!buf) {
347         auto color_buf = pipeline->GenerateDefaultColorAttachmentBuffer();
348         buf = color_buf.get();
349 
350         Result r = script_->AddBuffer(std::move(color_buf));
351         if (!r.IsSuccess())
352           return r;
353       }
354       Result r = pipeline->AddColorAttachment(buf, 0, 0);
355       if (!r.IsSuccess())
356         return r;
357     }
358   }
359 
360   // Validate all the pipelines at the end. This allows us to verify the
361   // framebuffer sizes are consistent over pipelines.
362   for (const auto& pipeline : script_->GetPipelines()) {
363     Result r = pipeline->Validate();
364     if (!r.IsSuccess())
365       return r;
366   }
367 
368   return {};
369 }
370 
IsRepeatable(const std::string & name) const371 bool Parser::IsRepeatable(const std::string& name) const {
372   return name == "CLEAR" || name == "CLEAR_COLOR" || name == "CLEAR_DEPTH" ||
373          name == "CLEAR_STENCIL" || name == "COPY" || name == "EXPECT" ||
374          name == "RUN";
375 }
376 
377 // The given |name| must be one of the repeatable commands or this method
378 // returns an error result.
ParseRepeatableCommand(const std::string & name)379 Result Parser::ParseRepeatableCommand(const std::string& name) {
380   if (name == "CLEAR")
381     return ParseClear();
382   if (name == "CLEAR_COLOR")
383     return ParseClearColor();
384   if (name == "CLEAR_DEPTH")
385     return ParseClearDepth();
386   if (name == "CLEAR_STENCIL")
387     return ParseClearStencil();
388   if (name == "COPY")
389     return ParseCopy();
390   if (name == "EXPECT")
391     return ParseExpect();
392   if (name == "RUN")
393     return ParseRun();
394 
395   return Result("invalid repeatable command: " + name);
396 }
397 
ToShaderType(const std::string & str,ShaderType * type)398 Result Parser::ToShaderType(const std::string& str, ShaderType* type) {
399   assert(type);
400 
401   if (str == "vertex")
402     *type = kShaderTypeVertex;
403   else if (str == "fragment")
404     *type = kShaderTypeFragment;
405   else if (str == "geometry")
406     *type = kShaderTypeGeometry;
407   else if (str == "tessellation_evaluation")
408     *type = kShaderTypeTessellationEvaluation;
409   else if (str == "tessellation_control")
410     *type = kShaderTypeTessellationControl;
411   else if (str == "compute")
412     *type = kShaderTypeCompute;
413   else if (str == "ray_generation")
414     *type = kShaderTypeRayGeneration;
415   else if (str == "any_hit")
416     *type = kShaderTypeAnyHit;
417   else if (str == "closest_hit")
418     *type = kShaderTypeClosestHit;
419   else if (str == "miss")
420     *type = kShaderTypeMiss;
421   else if (str == "intersection")
422     *type = kShaderTypeIntersection;
423   else if (str == "callable")
424     *type = kShaderTypeCall;
425   else if (str == "multi")
426     *type = kShaderTypeMulti;
427   else
428     return Result("unknown shader type: " + str);
429   return {};
430 }
431 
ToShaderFormat(const std::string & str,ShaderFormat * fmt)432 Result Parser::ToShaderFormat(const std::string& str, ShaderFormat* fmt) {
433   assert(fmt);
434 
435   if (str == "GLSL")
436     *fmt = kShaderFormatGlsl;
437   else if (str == "HLSL")
438     *fmt = kShaderFormatHlsl;
439   else if (str == "SPIRV-ASM")
440     *fmt = kShaderFormatSpirvAsm;
441   else if (str == "SPIRV-HEX")
442     *fmt = kShaderFormatSpirvHex;
443   else if (str == "SPIRV-BIN")
444     *fmt = kShaderFormatSpirvBin;
445   else if (str == "OPENCL-C")
446     *fmt = kShaderFormatOpenCLC;
447   else
448     return Result("unknown shader format: " + str);
449   return {};
450 }
451 
ToPipelineType(const std::string & str,PipelineType * type)452 Result Parser::ToPipelineType(const std::string& str, PipelineType* type) {
453   assert(type);
454 
455   if (str == "compute")
456     *type = PipelineType::kCompute;
457   else if (str == "graphics")
458     *type = PipelineType::kGraphics;
459   else if (str == "raytracing")
460     *type = PipelineType::kRayTracing;
461   else
462     return Result("unknown pipeline type: " + str);
463   return {};
464 }
465 
ValidateEndOfStatement(const std::string & name)466 Result Parser::ValidateEndOfStatement(const std::string& name) {
467   auto token = tokenizer_->NextToken();
468   if (token->IsEOL() || token->IsEOS())
469     return {};
470   return Result("extra parameters after " + name + ": " +
471                 token->ToOriginalString());
472 }
473 
ParseShaderBlock()474 Result Parser::ParseShaderBlock() {
475   auto token = tokenizer_->NextToken();
476   if (!token->IsIdentifier())
477     return Result("invalid token when looking for shader type");
478 
479   ShaderType type = kShaderTypeVertex;
480   Result r = ToShaderType(token->AsString(), &type);
481   if (!r.IsSuccess())
482     return r;
483 
484   auto shader = MakeUnique<Shader>(type);
485 
486   token = tokenizer_->NextToken();
487   if (!token->IsIdentifier())
488     return Result("invalid token when looking for shader name");
489 
490   shader->SetName(token->AsString());
491 
492   token = tokenizer_->NextToken();
493   if (!token->IsIdentifier())
494     return Result("invalid token when looking for shader format");
495 
496   std::string fmt = token->AsString();
497   if (fmt == "PASSTHROUGH") {
498     if (type != kShaderTypeVertex) {
499       return Result(
500           "invalid shader type for PASSTHROUGH. Only vertex "
501           "PASSTHROUGH allowed");
502     }
503     shader->SetFormat(kShaderFormatSpirvAsm);
504     shader->SetData(kPassThroughShader);
505     shader->SetTargetEnv("spv1.0");
506 
507     r = script_->AddShader(std::move(shader));
508     if (!r.IsSuccess())
509       return r;
510 
511     return ValidateEndOfStatement("SHADER PASSTHROUGH");
512   }
513 
514   ShaderFormat format = kShaderFormatGlsl;
515   r = ToShaderFormat(fmt, &format);
516   if (!r.IsSuccess())
517     return r;
518 
519   shader->SetFormat(format);
520 
521   token = tokenizer_->PeekNextToken();
522   if (token->IsIdentifier() && token->AsString() == "TARGET_ENV") {
523     tokenizer_->NextToken();
524     token = tokenizer_->NextToken();
525     if (!token->IsIdentifier() && !token->IsString())
526       return Result("expected target environment after TARGET_ENV");
527     shader->SetTargetEnv(token->AsString());
528   }
529 
530   token = tokenizer_->PeekNextToken();
531   if (token->IsIdentifier() &&
532       (token->AsString() == "VIRTUAL_FILE" || token->AsString() == "FILE")) {
533     bool isVirtual = token->AsString() == "VIRTUAL_FILE";
534     tokenizer_->NextToken();  // Skip VIRTUAL_FILE or FILE
535 
536     token = tokenizer_->NextToken();
537     if (!token->IsIdentifier() && !token->IsString())
538       return Result("expected file path after VIRTUAL_FILE or FILE");
539 
540     auto path = token->AsString();
541 
542     std::string data;
543     if (isVirtual) {
544       r = script_->GetVirtualFile(path, &data);
545       if (!r.IsSuccess())
546         return r;
547     } else {
548       if (!delegate_)
549         return Result("missing delegate for loading shader file");
550 
551       std::vector<char> buffer;
552       r = delegate_->LoadFile(path, &buffer);
553       if (!r.IsSuccess())
554         return r;
555 
556       data.insert(data.begin(), buffer.begin(), buffer.end());
557     }
558 
559     shader->SetData(data);
560     shader->SetFilePath(path);
561 
562     r = script_->AddShader(std::move(shader));
563     if (!r.IsSuccess())
564       return r;
565 
566     return ValidateEndOfStatement("SHADER command");
567   }
568 
569   r = ValidateEndOfStatement("SHADER command");
570   if (!r.IsSuccess())
571     return r;
572 
573   std::string data = tokenizer_->ExtractToNext("END");
574   if (data.empty())
575     return Result("SHADER must not be empty");
576 
577   shader->SetData(data);
578 
579   auto path = "embedded-shaders/" + shader->GetName();
580   script_->AddVirtualFile(path, data);
581   shader->SetFilePath(path);
582 
583   token = tokenizer_->NextToken();
584   if (!token->IsIdentifier() || token->AsString() != "END")
585     return Result("SHADER missing END command");
586 
587   if (shader->GetTargetEnv().empty() && IsRayTracingShader(type))
588     shader->SetTargetEnv("spv1.4");
589 
590   r = script_->AddShader(std::move(shader));
591   if (!r.IsSuccess())
592     return r;
593 
594   return ValidateEndOfStatement("END");
595 }
596 
ParsePipelineBlock()597 Result Parser::ParsePipelineBlock() {
598   auto token = tokenizer_->NextToken();
599   if (!token->IsIdentifier())
600     return Result("invalid token when looking for pipeline type");
601 
602   PipelineType type = PipelineType::kCompute;
603   Result r = ToPipelineType(token->AsString(), &type);
604   if (!r.IsSuccess())
605     return r;
606 
607   auto pipeline = MakeUnique<Pipeline>(type);
608 
609   token = tokenizer_->NextToken();
610   if (!token->IsIdentifier())
611     return Result("invalid token when looking for pipeline name");
612 
613   pipeline->SetName(token->AsString());
614 
615   r = ValidateEndOfStatement("PIPELINE command");
616   if (!r.IsSuccess())
617     return r;
618 
619   return ParsePipelineBody("PIPELINE", std::move(pipeline));
620 }
621 
ParsePipelineBody(const std::string & cmd_name,std::unique_ptr<Pipeline> pipeline)622 Result Parser::ParsePipelineBody(const std::string& cmd_name,
623                                  std::unique_ptr<Pipeline> pipeline) {
624   std::unique_ptr<Token> token;
625   for (token = tokenizer_->NextToken(); !token->IsEOS();
626        token = tokenizer_->NextToken()) {
627     if (token->IsEOL())
628       continue;
629     if (!token->IsIdentifier())
630       return Result("expected identifier");
631 
632     Result r;
633     std::string tok = token->AsString();
634     if (tok == "END") {
635       break;
636     } else if (tok == "ATTACH") {
637       r = ParsePipelineAttach(pipeline.get());
638     } else if (tok == "SHADER_OPTIMIZATION") {
639       r = ParsePipelineShaderOptimizations(pipeline.get());
640     } else if (tok == "FRAMEBUFFER_SIZE") {
641       r = ParsePipelineFramebufferSize(pipeline.get());
642     } else if (tok == "VIEWPORT") {
643       r = ParsePipelineViewport(pipeline.get());
644     } else if (tok == "BIND") {
645       r = ParsePipelineBind(pipeline.get());
646     } else if (tok == "VERTEX_DATA") {
647       r = ParsePipelineVertexData(pipeline.get());
648     } else if (tok == "INDEX_DATA") {
649       r = ParsePipelineIndexData(pipeline.get());
650     } else if (tok == "SET") {
651       r = ParsePipelineSet(pipeline.get());
652     } else if (tok == "COMPILE_OPTIONS") {
653       r = ParsePipelineShaderCompileOptions(pipeline.get());
654     } else if (tok == "POLYGON_MODE") {
655       r = ParsePipelinePolygonMode(pipeline.get());
656     } else if (tok == "DEPTH") {
657       r = ParsePipelineDepth(pipeline.get());
658     } else if (tok == "STENCIL") {
659       r = ParsePipelineStencil(pipeline.get());
660     } else if (tok == "SUBGROUP") {
661       r = ParsePipelineSubgroup(pipeline.get());
662     } else if (tok == "PATCH_CONTROL_POINTS") {
663       r = ParsePipelinePatchControlPoints(pipeline.get());
664     } else if (tok == "BLEND") {
665       r = ParsePipelineBlend(pipeline.get());
666     } else if (tok == "SHADER_GROUP") {
667       r = ParsePipelineShaderGroup(pipeline.get());
668     } else if (tok == "SHADER_BINDING_TABLE") {
669       r = ParseSBT(pipeline.get());
670     } else if (tok == "MAX_RAY_PAYLOAD_SIZE") {
671       r = ParseMaxRayPayloadSize(pipeline.get());
672     } else if (tok == "MAX_RAY_HIT_ATTRIBUTE_SIZE") {
673       r = ParseMaxRayHitAttributeSize(pipeline.get());
674     } else if (tok == "MAX_RAY_RECURSION_DEPTH") {
675       r = ParseMaxRayRecursionDepth(pipeline.get());
676     } else if (tok == "FLAGS") {
677       r = ParseFlags(pipeline.get());
678     } else if (tok == "USE_LIBRARY") {
679       r = ParseUseLibrary(pipeline.get());
680     } else {
681       r = Result("unknown token in pipeline block: " + tok);
682     }
683     if (!r.IsSuccess())
684       return r;
685   }
686 
687   if (!token->IsIdentifier() || token->AsString() != "END")
688     return Result(cmd_name + " missing END command");
689 
690   Result r = script_->AddPipeline(std::move(pipeline));
691   if (!r.IsSuccess())
692     return r;
693 
694   return ValidateEndOfStatement("END");
695 }
696 
ParsePipelineAttach(Pipeline * pipeline)697 Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
698   auto token = tokenizer_->NextToken();
699   if (!token->IsIdentifier())
700     return Result("invalid token in ATTACH command");
701 
702   auto* shader = script_->GetShader(token->AsString());
703   if (!shader)
704     return Result("unknown shader in ATTACH command");
705 
706   token = tokenizer_->NextToken();
707   if (token->IsEOL() || token->IsEOS()) {
708     if (shader->GetType() == kShaderTypeMulti)
709       return Result("multi shader ATTACH requires TYPE");
710 
711     Result r = pipeline->AddShader(shader, shader->GetType());
712     if (!r.IsSuccess())
713       return r;
714     return {};
715   }
716   if (!token->IsIdentifier())
717     return Result("invalid token after ATTACH");
718 
719   bool set_shader_type = false;
720   ShaderType shader_type = shader->GetType();
721   auto type = token->AsString();
722   if (type == "TYPE") {
723     token = tokenizer_->NextToken();
724     if (!token->IsIdentifier())
725       return Result("invalid type in ATTACH");
726 
727     Result r = ToShaderType(token->AsString(), &shader_type);
728     if (!r.IsSuccess())
729       return r;
730 
731     set_shader_type = true;
732 
733     token = tokenizer_->NextToken();
734     if (!token->IsIdentifier())
735       return Result("ATTACH TYPE requires an ENTRY_POINT");
736 
737     type = token->AsString();
738   }
739   if (set_shader_type && type != "ENTRY_POINT")
740     return Result("unknown ATTACH parameter: " + type);
741 
742   if (shader->GetType() == ShaderType::kShaderTypeMulti && !set_shader_type)
743     return Result("ATTACH missing TYPE for multi shader");
744 
745   Result r = pipeline->AddShader(shader, shader_type);
746   if (!r.IsSuccess())
747     return r;
748 
749   if (type == "ENTRY_POINT") {
750     token = tokenizer_->NextToken();
751     if (!token->IsIdentifier())
752       return Result("missing shader name in ATTACH ENTRY_POINT command");
753 
754     r = pipeline->SetShaderEntryPoint(shader, token->AsString());
755     if (!r.IsSuccess())
756       return r;
757 
758     token = tokenizer_->NextToken();
759   }
760 
761   while (true) {
762     if (token->IsIdentifier() && token->AsString() == "SPECIALIZE") {
763       r = ParseShaderSpecialization(pipeline);
764       if (!r.IsSuccess())
765         return r;
766 
767       token = tokenizer_->NextToken();
768     } else {
769       if (token->IsEOL() || token->IsEOS())
770         return {};
771       if (token->IsIdentifier())
772         return Result("unknown ATTACH parameter: " + token->AsString());
773       return Result("extra parameters after ATTACH command: " +
774                     token->ToOriginalString());
775     }
776   }
777 }
778 
ParseShaderSpecialization(Pipeline * pipeline)779 Result Parser::ParseShaderSpecialization(Pipeline* pipeline) {
780   auto token = tokenizer_->NextToken();
781   if (!token->IsInteger())
782     return Result("specialization ID must be an integer");
783 
784   auto spec_id = token->AsUint32();
785 
786   token = tokenizer_->NextToken();
787   if (!token->IsIdentifier() || token->AsString() != "AS")
788     return Result("expected AS as next token");
789 
790   token = tokenizer_->NextToken();
791   if (!token->IsIdentifier())
792     return Result("expected data type in SPECIALIZE subcommand");
793 
794   auto type = ToType(token->AsString());
795   if (!type)
796     return Result("invalid data type '" + token->AsString() + "' provided");
797   if (!type->IsNumber())
798     return Result("only numeric types are accepted for specialization values");
799 
800   auto num = type->AsNumber();
801 
802   token = tokenizer_->NextToken();
803   uint32_t value = 0;
804   if (type::Type::IsUint32(num->GetFormatMode(), num->NumBits()) ||
805       type::Type::IsInt32(num->GetFormatMode(), num->NumBits())) {
806     value = token->AsUint32();
807   } else if (type::Type::IsFloat32(num->GetFormatMode(), num->NumBits())) {
808     Result r = token->ConvertToDouble();
809     if (!r.IsSuccess())
810       return Result("value is not a floating point value");
811 
812     union {
813       uint32_t u;
814       float f;
815     } u;
816     u.f = token->AsFloat();
817     value = u.u;
818   } else {
819     return Result(
820         "only 32-bit types are currently accepted for specialization values");
821   }
822 
823   auto& shader = pipeline->GetShaders()[pipeline->GetShaders().size() - 1];
824   shader.AddSpecialization(spec_id, value);
825   return {};
826 }
827 
ParsePipelineShaderOptimizations(Pipeline * pipeline)828 Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) {
829   auto token = tokenizer_->NextToken();
830   if (!token->IsIdentifier())
831     return Result("missing shader name in SHADER_OPTIMIZATION command");
832 
833   auto* shader = script_->GetShader(token->AsString());
834   if (!shader)
835     return Result("unknown shader in SHADER_OPTIMIZATION command");
836 
837   token = tokenizer_->NextToken();
838   if (!token->IsEOL())
839     return Result("extra parameters after SHADER_OPTIMIZATION command: " +
840                   token->ToOriginalString());
841 
842   std::vector<std::string> optimizations;
843   while (true) {
844     token = tokenizer_->NextToken();
845     if (token->IsEOL())
846       continue;
847     if (token->IsEOS())
848       return Result("SHADER_OPTIMIZATION missing END command");
849     if (!token->IsIdentifier())
850       return Result("SHADER_OPTIMIZATION options must be identifiers");
851     if (token->AsString() == "END")
852       break;
853 
854     optimizations.push_back(token->AsString());
855   }
856 
857   Result r = pipeline->SetShaderOptimizations(shader, optimizations);
858   if (!r.IsSuccess())
859     return r;
860 
861   return ValidateEndOfStatement("SHADER_OPTIMIZATION command");
862 }
863 
ParsePipelineShaderCompileOptions(Pipeline * pipeline)864 Result Parser::ParsePipelineShaderCompileOptions(Pipeline* pipeline) {
865   auto token = tokenizer_->NextToken();
866   if (!token->IsIdentifier())
867     return Result("missing shader name in COMPILE_OPTIONS command");
868 
869   auto* shader = script_->GetShader(token->AsString());
870   if (!shader)
871     return Result("unknown shader in COMPILE_OPTIONS command");
872 
873   if (shader->GetFormat() != kShaderFormatOpenCLC) {
874     return Result("COMPILE_OPTIONS currently only supports OPENCL-C shaders");
875   }
876 
877   token = tokenizer_->NextToken();
878   if (!token->IsEOL())
879     return Result("extra parameters after COMPILE_OPTIONS command: " +
880                   token->ToOriginalString());
881 
882   std::vector<std::string> options;
883   while (true) {
884     token = tokenizer_->NextToken();
885     if (token->IsEOL())
886       continue;
887     if (token->IsEOS())
888       return Result("COMPILE_OPTIONS missing END command");
889     if (token->AsString() == "END")
890       break;
891 
892     options.push_back(token->AsString());
893   }
894 
895   Result r = pipeline->SetShaderCompileOptions(shader, options);
896   if (!r.IsSuccess())
897     return r;
898 
899   return ValidateEndOfStatement("COMPILE_OPTIONS command");
900 }
901 
ParsePipelineSubgroup(Pipeline * pipeline)902 Result Parser::ParsePipelineSubgroup(Pipeline* pipeline) {
903   auto token = tokenizer_->NextToken();
904   if (!token->IsIdentifier())
905     return Result("missing shader name in SUBGROUP command");
906 
907   auto* shader = script_->GetShader(token->AsString());
908   if (!shader)
909     return Result("unknown shader in SUBGROUP command");
910 
911   while (true) {
912     token = tokenizer_->NextToken();
913     if (token->IsEOL())
914       continue;
915     if (token->IsEOS())
916       return Result("SUBGROUP missing END command");
917     if (!token->IsIdentifier())
918       return Result("SUBGROUP options must be identifiers");
919     if (token->AsString() == "END")
920       break;
921 
922     if (token->AsString() == "FULLY_POPULATED") {
923       if (!script_->IsRequiredFeature(
924               "SubgroupSizeControl.computeFullSubgroups"))
925         return Result(
926             "missing DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups");
927       token = tokenizer_->NextToken();
928       if (token->IsEOL() || token->IsEOS())
929         return Result("missing value for FULLY_POPULATED command");
930       bool isOn = false;
931       if (token->AsString() == "on") {
932         isOn = true;
933       } else if (token->AsString() == "off") {
934         isOn = false;
935       } else {
936         return Result("invalid value for FULLY_POPULATED command");
937       }
938       Result r = pipeline->SetShaderRequireFullSubgroups(shader, isOn);
939       if (!r.IsSuccess())
940         return r;
941 
942     } else if (token->AsString() == "VARYING_SIZE") {
943       if (!script_->IsRequiredFeature(
944               "SubgroupSizeControl.subgroupSizeControl"))
945         return Result(
946             "missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl");
947       token = tokenizer_->NextToken();
948       if (token->IsEOL() || token->IsEOS())
949         return Result("missing value for VARYING_SIZE command");
950       bool isOn = false;
951       if (token->AsString() == "on") {
952         isOn = true;
953       } else if (token->AsString() == "off") {
954         isOn = false;
955       } else {
956         return Result("invalid value for VARYING_SIZE command");
957       }
958       Result r = pipeline->SetShaderVaryingSubgroupSize(shader, isOn);
959       if (!r.IsSuccess())
960         return r;
961     } else if (token->AsString() == "REQUIRED_SIZE") {
962       if (!script_->IsRequiredFeature(
963               "SubgroupSizeControl.subgroupSizeControl"))
964         return Result(
965             "missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl");
966       token = tokenizer_->NextToken();
967       if (token->IsEOL() || token->IsEOS())
968         return Result("missing size for REQUIRED_SIZE command");
969       Result r;
970       if (token->IsInteger()) {
971         r = pipeline->SetShaderRequiredSubgroupSize(shader, token->AsUint32());
972       } else if (token->AsString() == "MIN") {
973         r = pipeline->SetShaderRequiredSubgroupSizeToMinimum(shader);
974       } else if (token->AsString() == "MAX") {
975         r = pipeline->SetShaderRequiredSubgroupSizeToMaximum(shader);
976       } else {
977         return Result("invalid size for REQUIRED_SIZE command");
978       }
979       if (!r.IsSuccess())
980         return r;
981     } else {
982       return Result("SUBGROUP invalid value for SUBGROUP " + token->AsString());
983     }
984   }
985 
986   return ValidateEndOfStatement("SUBGROUP command");
987 }
988 
ParsePipelinePatchControlPoints(Pipeline * pipeline)989 Result Parser::ParsePipelinePatchControlPoints(Pipeline* pipeline) {
990   auto token = tokenizer_->NextToken();
991   if (token->IsEOL() || token->IsEOS())
992     return Result(
993         "missing number of control points in PATCH_CONTROL_POINTS command");
994 
995   if (!token->IsInteger())
996     return Result("expecting integer for the number of control points");
997 
998   pipeline->GetPipelineData()->SetPatchControlPoints(token->AsUint32());
999 
1000   return ValidateEndOfStatement("PATCH_CONTROL_POINTS command");
1001 }
1002 
ParsePipelineFramebufferSize(Pipeline * pipeline)1003 Result Parser::ParsePipelineFramebufferSize(Pipeline* pipeline) {
1004   auto token = tokenizer_->NextToken();
1005   if (token->IsEOL() || token->IsEOS())
1006     return Result("missing size for FRAMEBUFFER_SIZE command");
1007   if (!token->IsInteger())
1008     return Result("invalid width for FRAMEBUFFER_SIZE command");
1009 
1010   pipeline->SetFramebufferWidth(token->AsUint32());
1011 
1012   token = tokenizer_->NextToken();
1013   if (token->IsEOL() || token->IsEOS())
1014     return Result("missing height for FRAMEBUFFER_SIZE command");
1015   if (!token->IsInteger())
1016     return Result("invalid height for FRAMEBUFFER_SIZE command");
1017 
1018   pipeline->SetFramebufferHeight(token->AsUint32());
1019 
1020   return ValidateEndOfStatement("FRAMEBUFFER_SIZE command");
1021 }
1022 
ParsePipelineViewport(Pipeline * pipeline)1023 Result Parser::ParsePipelineViewport(Pipeline* pipeline) {
1024   Viewport vp;
1025   vp.mind = 0.0f;
1026   vp.maxd = 1.0f;
1027 
1028   float val[2];
1029   for (int i = 0; i < 2; i++) {
1030     auto token = tokenizer_->NextToken();
1031     if (token->IsEOL() || token->IsEOS())
1032       return Result("missing offset for VIEWPORT command");
1033     Result r = token->ConvertToDouble();
1034     if (!r.IsSuccess())
1035       return Result("invalid offset for VIEWPORT command");
1036 
1037     val[i] = token->AsFloat();
1038   }
1039   vp.x = val[0];
1040   vp.y = val[1];
1041 
1042   auto token = tokenizer_->NextToken();
1043   if (!token->IsIdentifier() || token->AsString() != "SIZE")
1044     return Result("missing SIZE for VIEWPORT command");
1045 
1046   for (int i = 0; i < 2; i++) {
1047     token = tokenizer_->NextToken();
1048     if (token->IsEOL() || token->IsEOS())
1049       return Result("missing size for VIEWPORT command");
1050     Result r = token->ConvertToDouble();
1051     if (!r.IsSuccess())
1052       return Result("invalid size for VIEWPORT command");
1053 
1054     val[i] = token->AsFloat();
1055   }
1056   vp.w = val[0];
1057   vp.h = val[1];
1058 
1059   token = tokenizer_->PeekNextToken();
1060   while (token->IsIdentifier()) {
1061     if (token->AsString() == "MIN_DEPTH") {
1062       tokenizer_->NextToken();
1063       token = tokenizer_->NextToken();
1064       if (token->IsEOL() || token->IsEOS())
1065         return Result("missing min_depth for VIEWPORT command");
1066       Result r = token->ConvertToDouble();
1067       if (!r.IsSuccess())
1068         return Result("invalid min_depth for VIEWPORT command");
1069 
1070       vp.mind = token->AsFloat();
1071     }
1072     if (token->AsString() == "MAX_DEPTH") {
1073       tokenizer_->NextToken();
1074       token = tokenizer_->NextToken();
1075       if (token->IsEOL() || token->IsEOS())
1076         return Result("missing max_depth for VIEWPORT command");
1077       Result r = token->ConvertToDouble();
1078       if (!r.IsSuccess())
1079         return Result("invalid max_depth for VIEWPORT command");
1080 
1081       vp.maxd = token->AsFloat();
1082     }
1083 
1084     token = tokenizer_->PeekNextToken();
1085   }
1086 
1087   pipeline->GetPipelineData()->SetViewport(vp);
1088 
1089   return ValidateEndOfStatement("VIEWPORT command");
1090 }
1091 
ToBufferType(const std::string & name,BufferType * type)1092 Result Parser::ToBufferType(const std::string& name, BufferType* type) {
1093   assert(type);
1094   if (name == "color")
1095     *type = BufferType::kColor;
1096   else if (name == "depth_stencil")
1097     *type = BufferType::kDepthStencil;
1098   else if (name == "push_constant")
1099     *type = BufferType::kPushConstant;
1100   else if (name == "uniform")
1101     *type = BufferType::kUniform;
1102   else if (name == "uniform_dynamic")
1103     *type = BufferType::kUniformDynamic;
1104   else if (name == "storage")
1105     *type = BufferType::kStorage;
1106   else if (name == "storage_dynamic")
1107     *type = BufferType::kStorageDynamic;
1108   else if (name == "storage_image")
1109     *type = BufferType::kStorageImage;
1110   else if (name == "sampled_image")
1111     *type = BufferType::kSampledImage;
1112   else if (name == "combined_image_sampler")
1113     *type = BufferType::kCombinedImageSampler;
1114   else if (name == "uniform_texel_buffer")
1115     *type = BufferType::kUniformTexelBuffer;
1116   else if (name == "storage_texel_buffer")
1117     *type = BufferType::kStorageTexelBuffer;
1118   else if (name == "resolve")
1119     *type = BufferType::kResolve;
1120   else
1121     return Result("unknown buffer_type: " + name);
1122 
1123   return {};
1124 }
1125 
ParsePipelineBind(Pipeline * pipeline)1126 Result Parser::ParsePipelineBind(Pipeline* pipeline) {
1127   auto token = tokenizer_->NextToken();
1128 
1129   if (!token->IsIdentifier()) {
1130     return Result(
1131         "missing BUFFER, BUFFER_ARRAY, SAMPLER, SAMPLER_ARRAY, or "
1132         "ACCELERATION_STRUCTURE in BIND command");
1133   }
1134 
1135   auto object_type = token->AsString();
1136 
1137   if (object_type == "BUFFER" || object_type == "BUFFER_ARRAY") {
1138     bool is_buffer_array = object_type == "BUFFER_ARRAY";
1139     token = tokenizer_->NextToken();
1140     if (!token->IsIdentifier())
1141       return Result("missing buffer name in BIND command");
1142 
1143     auto* buffer = script_->GetBuffer(token->AsString());
1144     if (!buffer)
1145       return Result("unknown buffer: " + token->AsString());
1146     std::vector<Buffer*> buffers = {buffer};
1147 
1148     if (is_buffer_array) {
1149       // Check for additional buffer names
1150       token = tokenizer_->PeekNextToken();
1151       while (token->IsIdentifier() && token->AsString() != "AS" &&
1152              token->AsString() != "KERNEL" &&
1153              token->AsString() != "DESCRIPTOR_SET") {
1154         tokenizer_->NextToken();
1155         buffer = script_->GetBuffer(token->AsString());
1156         if (!buffer)
1157           return Result("unknown buffer: " + token->AsString());
1158         buffers.push_back(buffer);
1159         token = tokenizer_->PeekNextToken();
1160       }
1161 
1162       if (buffers.size() < 2)
1163         return Result("expecting multiple buffer names for BUFFER_ARRAY");
1164     }
1165 
1166     BufferType buffer_type = BufferType::kUnknown;
1167     token = tokenizer_->NextToken();
1168     if (token->IsIdentifier() && token->AsString() == "AS") {
1169       token = tokenizer_->NextToken();
1170       if (!token->IsIdentifier())
1171         return Result("invalid token for BUFFER type");
1172 
1173       Result r = ToBufferType(token->AsString(), &buffer_type);
1174       if (!r.IsSuccess())
1175         return r;
1176 
1177       if (buffer_type == BufferType::kColor) {
1178         token = tokenizer_->NextToken();
1179         if (!token->IsIdentifier() || token->AsString() != "LOCATION")
1180           return Result("BIND missing LOCATION");
1181 
1182         token = tokenizer_->NextToken();
1183         if (!token->IsInteger())
1184           return Result("invalid value for BIND LOCATION");
1185         auto location = token->AsUint32();
1186 
1187         uint32_t base_mip_level = 0;
1188         token = tokenizer_->PeekNextToken();
1189         if (token->IsIdentifier() && token->AsString() == "BASE_MIP_LEVEL") {
1190           tokenizer_->NextToken();
1191           token = tokenizer_->NextToken();
1192 
1193           if (!token->IsInteger())
1194             return Result("invalid value for BASE_MIP_LEVEL");
1195 
1196           base_mip_level = token->AsUint32();
1197 
1198           if (base_mip_level >= buffer->GetMipLevels())
1199             return Result(
1200                 "base mip level (now " + token->AsString() +
1201                 ") needs to be larger than the number of buffer mip maps (" +
1202                 std::to_string(buffer->GetMipLevels()) + ")");
1203         }
1204 
1205         r = pipeline->AddColorAttachment(buffer, location, base_mip_level);
1206         if (!r.IsSuccess())
1207           return r;
1208 
1209       } else if (buffer_type == BufferType::kDepthStencil) {
1210         r = pipeline->SetDepthStencilBuffer(buffer);
1211         if (!r.IsSuccess())
1212           return r;
1213 
1214       } else if (buffer_type == BufferType::kPushConstant) {
1215         r = pipeline->SetPushConstantBuffer(buffer);
1216         if (!r.IsSuccess())
1217           return r;
1218 
1219       } else if (buffer_type == BufferType::kCombinedImageSampler) {
1220         token = tokenizer_->NextToken();
1221         if (!token->IsIdentifier() || token->AsString() != "SAMPLER")
1222           return Result("expecting SAMPLER for combined image sampler");
1223 
1224         token = tokenizer_->NextToken();
1225         if (!token->IsIdentifier())
1226           return Result("missing sampler name in BIND command");
1227 
1228         auto* sampler = script_->GetSampler(token->AsString());
1229         if (!sampler)
1230           return Result("unknown sampler: " + token->AsString());
1231 
1232         for (auto& buf : buffers)
1233           buf->SetSampler(sampler);
1234       } else if (buffer_type == BufferType::kResolve) {
1235         r = pipeline->AddResolveTarget(buffer);
1236       }
1237     }
1238 
1239     // The OpenCL bindings can be typeless which allows for the kUnknown
1240     // buffer type.
1241     if (buffer_type == BufferType::kUnknown ||
1242         buffer_type == BufferType::kStorage ||
1243         buffer_type == BufferType::kUniform ||
1244         buffer_type == BufferType::kStorageDynamic ||
1245         buffer_type == BufferType::kUniformDynamic ||
1246         buffer_type == BufferType::kStorageImage ||
1247         buffer_type == BufferType::kSampledImage ||
1248         buffer_type == BufferType::kCombinedImageSampler ||
1249         buffer_type == BufferType::kUniformTexelBuffer ||
1250         buffer_type == BufferType::kStorageTexelBuffer) {
1251       // If the buffer type is known, then we proccessed the AS block above
1252       // and have to advance to the next token. Otherwise, we're already on
1253       // the next token and don't want to advance.
1254       if (buffer_type != BufferType::kUnknown)
1255         token = tokenizer_->NextToken();
1256 
1257       // DESCRIPTOR_SET requires a buffer type to have been specified.
1258       if (token->IsIdentifier() && token->AsString() == "DESCRIPTOR_SET") {
1259         token = tokenizer_->NextToken();
1260         if (!token->IsInteger())
1261           return Result("invalid value for DESCRIPTOR_SET in BIND command");
1262         uint32_t descriptor_set = token->AsUint32();
1263 
1264         token = tokenizer_->NextToken();
1265         if (!token->IsIdentifier() || token->AsString() != "BINDING")
1266           return Result("missing BINDING for BIND command");
1267 
1268         token = tokenizer_->NextToken();
1269         if (!token->IsInteger())
1270           return Result("invalid value for BINDING in BIND command");
1271 
1272         auto binding = token->AsUint32();
1273         uint32_t base_mip_level = 0;
1274 
1275         if (buffer_type == BufferType::kStorageImage ||
1276             buffer_type == BufferType::kSampledImage ||
1277             buffer_type == BufferType::kCombinedImageSampler) {
1278           token = tokenizer_->PeekNextToken();
1279           if (token->IsIdentifier() && token->AsString() == "BASE_MIP_LEVEL") {
1280             tokenizer_->NextToken();
1281             token = tokenizer_->NextToken();
1282 
1283             if (!token->IsInteger())
1284               return Result("invalid value for BASE_MIP_LEVEL");
1285 
1286             base_mip_level = token->AsUint32();
1287 
1288             if (base_mip_level >= buffer->GetMipLevels())
1289               return Result("base mip level (now " + token->AsString() +
1290                             ") needs to be larger than the number of buffer "
1291                             "mip maps (" +
1292                             std::to_string(buffer->GetMipLevels()) + ")");
1293           }
1294         }
1295 
1296         std::vector<uint32_t> dynamic_offsets(buffers.size(), 0);
1297         if (buffer_type == BufferType::kUniformDynamic ||
1298             buffer_type == BufferType::kStorageDynamic) {
1299           token = tokenizer_->NextToken();
1300           if (!token->IsIdentifier() || token->AsString() != "OFFSET")
1301             return Result("expecting an OFFSET for dynamic buffer type");
1302 
1303           for (size_t i = 0; i < buffers.size(); i++) {
1304             token = tokenizer_->NextToken();
1305 
1306             if (!token->IsInteger()) {
1307               if (i > 0) {
1308                 return Result(
1309                     "expecting an OFFSET value for each buffer in the array");
1310               } else {
1311                 return Result("expecting an integer value for OFFSET");
1312               }
1313             }
1314 
1315             dynamic_offsets[i] = token->AsUint32();
1316           }
1317         }
1318 
1319         // Set default descriptor buffer offsets to 0 and descriptor buffer
1320         // ranges to VK_WHOLE_SIZE (~0ULL).
1321         std::vector<uint64_t> descriptor_offsets(buffers.size(), 0);
1322         std::vector<uint64_t> descriptor_ranges(buffers.size(), ~0ULL);
1323         if (buffer_type == BufferType::kUniformDynamic ||
1324             buffer_type == BufferType::kStorageDynamic ||
1325             buffer_type == BufferType::kStorage ||
1326             buffer_type == BufferType::kUniform) {
1327           token = tokenizer_->PeekNextToken();
1328           if (token->IsIdentifier() &&
1329               token->AsString() == "DESCRIPTOR_OFFSET") {
1330             token = tokenizer_->NextToken();
1331             for (size_t i = 0; i < buffers.size(); i++) {
1332               token = tokenizer_->NextToken();
1333               if (!token->IsInteger()) {
1334                 if (i > 0) {
1335                   return Result(
1336                       "expecting a DESCRIPTOR_OFFSET value for each buffer in "
1337                       "the array");
1338                 } else {
1339                   return Result(
1340                       "expecting an integer value for DESCRIPTOR_OFFSET");
1341                 }
1342               }
1343               descriptor_offsets[i] = token->AsUint64();
1344             }
1345           }
1346 
1347           token = tokenizer_->PeekNextToken();
1348           if (token->IsIdentifier() &&
1349               token->AsString() == "DESCRIPTOR_RANGE") {
1350             token = tokenizer_->NextToken();
1351             for (size_t i = 0; i < buffers.size(); i++) {
1352               token = tokenizer_->NextToken();
1353               if (!token->IsInteger()) {
1354                 if (i > 0) {
1355                   return Result(
1356                       "expecting a DESCRIPTOR_RANGE value for each buffer in "
1357                       "the array");
1358                 } else {
1359                   return Result(
1360                       "expecting an integer value for DESCRIPTOR_RANGE");
1361                 }
1362               }
1363               descriptor_ranges[i] = token->AsUint64();
1364             }
1365           }
1366         }
1367 
1368         pipeline->ClearBuffers(descriptor_set, binding);
1369         for (size_t i = 0; i < buffers.size(); i++) {
1370           pipeline->AddBuffer(buffers[i], buffer_type, descriptor_set, binding,
1371                               base_mip_level, dynamic_offsets[i],
1372                               descriptor_offsets[i], descriptor_ranges[i]);
1373         }
1374       } else if (token->IsIdentifier() && token->AsString() == "KERNEL") {
1375         token = tokenizer_->NextToken();
1376         if (!token->IsIdentifier())
1377           return Result("missing kernel arg identifier");
1378 
1379         if (token->AsString() == "ARG_NAME") {
1380           token = tokenizer_->NextToken();
1381           if (!token->IsIdentifier())
1382             return Result("expected argument identifier");
1383 
1384           pipeline->AddBuffer(buffer, buffer_type, token->AsString());
1385         } else if (token->AsString() == "ARG_NUMBER") {
1386           token = tokenizer_->NextToken();
1387           if (!token->IsInteger())
1388             return Result("expected argument number");
1389 
1390           pipeline->AddBuffer(buffer, buffer_type, token->AsUint32());
1391         } else {
1392           return Result("missing ARG_NAME or ARG_NUMBER keyword");
1393         }
1394       } else {
1395         return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
1396       }
1397     }
1398   } else if (object_type == "SAMPLER" || object_type == "SAMPLER_ARRAY") {
1399     bool is_sampler_array = object_type == "SAMPLER_ARRAY";
1400     token = tokenizer_->NextToken();
1401     if (!token->IsIdentifier())
1402       return Result("missing sampler name in BIND command");
1403 
1404     auto* sampler = script_->GetSampler(token->AsString());
1405     if (!sampler)
1406       return Result("unknown sampler: " + token->AsString());
1407     std::vector<Sampler*> samplers = {sampler};
1408 
1409     if (is_sampler_array) {
1410       // Check for additional sampler names
1411       token = tokenizer_->PeekNextToken();
1412       while (token->IsIdentifier() && token->AsString() != "KERNEL" &&
1413              token->AsString() != "DESCRIPTOR_SET") {
1414         tokenizer_->NextToken();
1415         sampler = script_->GetSampler(token->AsString());
1416         if (!sampler)
1417           return Result("unknown sampler: " + token->AsString());
1418         samplers.push_back(sampler);
1419         token = tokenizer_->PeekNextToken();
1420       }
1421 
1422       if (samplers.size() < 2)
1423         return Result("expecting multiple sampler names for SAMPLER_ARRAY");
1424     }
1425 
1426     token = tokenizer_->NextToken();
1427     if (!token->IsIdentifier())
1428       return Result("expected a string token for BIND command");
1429 
1430     if (token->AsString() == "DESCRIPTOR_SET") {
1431       token = tokenizer_->NextToken();
1432       if (!token->IsInteger())
1433         return Result("invalid value for DESCRIPTOR_SET in BIND command");
1434       uint32_t descriptor_set = token->AsUint32();
1435 
1436       token = tokenizer_->NextToken();
1437       if (!token->IsIdentifier() || token->AsString() != "BINDING")
1438         return Result("missing BINDING for BIND command");
1439 
1440       token = tokenizer_->NextToken();
1441       if (!token->IsInteger())
1442         return Result("invalid value for BINDING in BIND command");
1443 
1444       uint32_t binding = token->AsUint32();
1445       pipeline->ClearSamplers(descriptor_set, binding);
1446       for (const auto& s : samplers) {
1447         pipeline->AddSampler(s, descriptor_set, binding);
1448       }
1449     } else if (token->AsString() == "KERNEL") {
1450       token = tokenizer_->NextToken();
1451       if (!token->IsIdentifier())
1452         return Result("missing kernel arg identifier");
1453 
1454       if (token->AsString() == "ARG_NAME") {
1455         token = tokenizer_->NextToken();
1456         if (!token->IsIdentifier())
1457           return Result("expected argument identifier");
1458 
1459         pipeline->AddSampler(sampler, token->AsString());
1460       } else if (token->AsString() == "ARG_NUMBER") {
1461         token = tokenizer_->NextToken();
1462         if (!token->IsInteger())
1463           return Result("expected argument number");
1464 
1465         pipeline->AddSampler(sampler, token->AsUint32());
1466       } else {
1467         return Result("missing ARG_NAME or ARG_NUMBER keyword");
1468       }
1469     } else {
1470       return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
1471     }
1472   } else if (object_type == "ACCELERATION_STRUCTURE") {
1473     token = tokenizer_->NextToken();
1474     if (!token->IsIdentifier())
1475       return Result(
1476           "missing top level acceleration structure name in BIND command");
1477 
1478     TLAS* tlas = script_->GetTLAS(token->AsString());
1479     if (!tlas)
1480       return Result("unknown top level acceleration structure: " +
1481                     token->AsString());
1482 
1483     token = tokenizer_->NextToken();
1484     if (token->AsString() == "DESCRIPTOR_SET") {
1485       token = tokenizer_->NextToken();
1486       if (!token->IsInteger())
1487         return Result("invalid value for DESCRIPTOR_SET in BIND command");
1488       uint32_t descriptor_set = token->AsUint32();
1489 
1490       token = tokenizer_->NextToken();
1491       if (!token->IsIdentifier() || token->AsString() != "BINDING")
1492         return Result("missing BINDING for BIND command");
1493 
1494       token = tokenizer_->NextToken();
1495       if (!token->IsInteger())
1496         return Result("invalid value for BINDING in BIND command");
1497 
1498       uint32_t binding = token->AsUint32();
1499 
1500       pipeline->AddTLAS(tlas, descriptor_set, binding);
1501     } else {
1502       return Result("missing DESCRIPTOR_SET or BINDING in BIND command");
1503     }
1504   } else {
1505     return Result("missing BUFFER or SAMPLER in BIND command");
1506   }
1507 
1508   return ValidateEndOfStatement("BIND command");
1509 }
1510 
ParsePipelineVertexData(Pipeline * pipeline)1511 Result Parser::ParsePipelineVertexData(Pipeline* pipeline) {
1512   auto token = tokenizer_->NextToken();
1513   if (!token->IsIdentifier())
1514     return Result("missing buffer name in VERTEX_DATA command");
1515 
1516   auto* buffer = script_->GetBuffer(token->AsString());
1517   if (!buffer)
1518     return Result("unknown buffer: " + token->AsString());
1519 
1520   token = tokenizer_->NextToken();
1521   if (!token->IsIdentifier() || token->AsString() != "LOCATION")
1522     return Result("VERTEX_DATA missing LOCATION");
1523 
1524   token = tokenizer_->NextToken();
1525   if (!token->IsInteger())
1526     return Result("invalid value for VERTEX_DATA LOCATION");
1527   const uint32_t location = token->AsUint32();
1528 
1529   InputRate rate = InputRate::kVertex;
1530   uint32_t offset = 0;
1531   Format* format = buffer->GetFormat();
1532   uint32_t stride = 0;
1533 
1534   token = tokenizer_->PeekNextToken();
1535   while (token->IsIdentifier()) {
1536     if (token->AsString() == "RATE") {
1537       tokenizer_->NextToken();
1538       token = tokenizer_->NextToken();
1539       if (!token->IsIdentifier())
1540         return Result("missing input rate value for RATE");
1541       if (token->AsString() == "instance") {
1542         rate = InputRate::kInstance;
1543       } else if (token->AsString() != "vertex") {
1544         return Result("expecting 'vertex' or 'instance' for RATE value");
1545       }
1546     } else if (token->AsString() == "OFFSET") {
1547       tokenizer_->NextToken();
1548       token = tokenizer_->NextToken();
1549       if (!token->IsInteger())
1550         return Result("expected unsigned integer for OFFSET");
1551       offset = token->AsUint32();
1552     } else if (token->AsString() == "STRIDE") {
1553       tokenizer_->NextToken();
1554       token = tokenizer_->NextToken();
1555       if (!token->IsInteger())
1556         return Result("expected unsigned integer for STRIDE");
1557       stride = token->AsUint32();
1558       if (stride == 0)
1559         return Result("STRIDE needs to be larger than zero");
1560     } else if (token->AsString() == "FORMAT") {
1561       tokenizer_->NextToken();
1562       token = tokenizer_->NextToken();
1563       if (!token->IsIdentifier())
1564         return Result("vertex data FORMAT must be an identifier");
1565       auto type = script_->ParseType(token->AsString());
1566       if (!type)
1567         return Result("invalid vertex data FORMAT");
1568       auto fmt = MakeUnique<Format>(type);
1569       format = fmt.get();
1570       script_->RegisterFormat(std::move(fmt));
1571     } else {
1572       return Result("unexpected identifier for VERTEX_DATA command: " +
1573                     token->ToOriginalString());
1574     }
1575 
1576     token = tokenizer_->PeekNextToken();
1577   }
1578 
1579   if (stride == 0)
1580     stride = format->SizeInBytes();
1581 
1582   Result r =
1583       pipeline->AddVertexBuffer(buffer, location, rate, format, offset, stride);
1584   if (!r.IsSuccess())
1585     return r;
1586 
1587   return ValidateEndOfStatement("VERTEX_DATA command");
1588 }
1589 
ParsePipelineIndexData(Pipeline * pipeline)1590 Result Parser::ParsePipelineIndexData(Pipeline* pipeline) {
1591   auto token = tokenizer_->NextToken();
1592   if (!token->IsIdentifier())
1593     return Result("missing buffer name in INDEX_DATA command");
1594 
1595   auto* buffer = script_->GetBuffer(token->AsString());
1596   if (!buffer)
1597     return Result("unknown buffer: " + token->AsString());
1598 
1599   Result r = pipeline->SetIndexBuffer(buffer);
1600   if (!r.IsSuccess())
1601     return r;
1602 
1603   return ValidateEndOfStatement("INDEX_DATA command");
1604 }
1605 
ParsePipelineSet(Pipeline * pipeline)1606 Result Parser::ParsePipelineSet(Pipeline* pipeline) {
1607   if (pipeline->GetShaders().empty() ||
1608       pipeline->GetShaders()[0].GetShader()->GetFormat() !=
1609           kShaderFormatOpenCLC) {
1610     return Result("SET can only be used with OPENCL-C shaders");
1611   }
1612 
1613   auto token = tokenizer_->NextToken();
1614   if (!token->IsIdentifier() || token->AsString() != "KERNEL")
1615     return Result("missing KERNEL in SET command");
1616 
1617   token = tokenizer_->NextToken();
1618   if (!token->IsIdentifier())
1619     return Result("expected ARG_NAME or ARG_NUMBER");
1620 
1621   std::string arg_name = "";
1622   uint32_t arg_no = std::numeric_limits<uint32_t>::max();
1623   if (token->AsString() == "ARG_NAME") {
1624     token = tokenizer_->NextToken();
1625     if (!token->IsIdentifier())
1626       return Result("expected argument identifier");
1627 
1628     arg_name = token->AsString();
1629   } else if (token->AsString() == "ARG_NUMBER") {
1630     token = tokenizer_->NextToken();
1631     if (!token->IsInteger())
1632       return Result("expected argument number");
1633 
1634     arg_no = token->AsUint32();
1635   } else {
1636     return Result("expected ARG_NAME or ARG_NUMBER");
1637   }
1638 
1639   token = tokenizer_->NextToken();
1640   if (!token->IsIdentifier() || token->AsString() != "AS")
1641     return Result("missing AS in SET command");
1642 
1643   token = tokenizer_->NextToken();
1644   if (!token->IsIdentifier())
1645     return Result("expected data type");
1646 
1647   auto type = ToType(token->AsString());
1648   if (!type)
1649     return Result("invalid data type '" + token->AsString() + "' provided");
1650 
1651   if (type->IsVec() || type->IsMatrix() || type->IsArray() || type->IsStruct())
1652     return Result("data type must be a scalar type");
1653 
1654   token = tokenizer_->NextToken();
1655   if (!token->IsInteger() && !token->IsDouble())
1656     return Result("expected data value");
1657 
1658   auto fmt = MakeUnique<Format>(type.get());
1659   Value value;
1660   if (fmt->IsFloat32() || fmt->IsFloat64())
1661     value.SetDoubleValue(token->AsDouble());
1662   else
1663     value.SetIntValue(token->AsUint64());
1664 
1665   Pipeline::ArgSetInfo info;
1666   info.name = arg_name;
1667   info.ordinal = arg_no;
1668   info.fmt = fmt.get();
1669   info.value = value;
1670   pipeline->SetArg(std::move(info));
1671   script_->RegisterFormat(std::move(fmt));
1672   script_->RegisterType(std::move(type));
1673 
1674   return ValidateEndOfStatement("SET command");
1675 }
1676 
ParsePipelinePolygonMode(Pipeline * pipeline)1677 Result Parser::ParsePipelinePolygonMode(Pipeline* pipeline) {
1678   auto token = tokenizer_->NextToken();
1679   if (!token->IsIdentifier())
1680     return Result("missing mode in POLYGON_MODE command");
1681 
1682   auto mode = token->AsString();
1683 
1684   if (mode == "fill")
1685     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kFill);
1686   else if (mode == "line")
1687     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kLine);
1688   else if (mode == "point")
1689     pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kPoint);
1690   else
1691     return Result("invalid polygon mode: " + mode);
1692 
1693   return ValidateEndOfStatement("POLYGON_MODE command");
1694 }
1695 
ParsePipelineDepth(Pipeline * pipeline)1696 Result Parser::ParsePipelineDepth(Pipeline* pipeline) {
1697   while (true) {
1698     auto token = tokenizer_->NextToken();
1699     if (token->IsEOL())
1700       continue;
1701     if (token->IsEOS())
1702       return Result("DEPTH missing END command");
1703     if (!token->IsIdentifier())
1704       return Result("DEPTH options must be identifiers");
1705     if (token->AsString() == "END")
1706       break;
1707 
1708     if (token->AsString() == "TEST") {
1709       token = tokenizer_->NextToken();
1710 
1711       if (!token->IsIdentifier())
1712         return Result("invalid value for TEST");
1713 
1714       if (token->AsString() == "on")
1715         pipeline->GetPipelineData()->SetEnableDepthTest(true);
1716       else if (token->AsString() == "off")
1717         pipeline->GetPipelineData()->SetEnableDepthTest(false);
1718       else
1719         return Result("invalid value for TEST: " + token->AsString());
1720     } else if (token->AsString() == "CLAMP") {
1721       token = tokenizer_->NextToken();
1722 
1723       if (!token->IsIdentifier())
1724         return Result("invalid value for CLAMP");
1725 
1726       if (token->AsString() == "on")
1727         pipeline->GetPipelineData()->SetEnableDepthClamp(true);
1728       else if (token->AsString() == "off")
1729         pipeline->GetPipelineData()->SetEnableDepthClamp(false);
1730       else
1731         return Result("invalid value for CLAMP: " + token->AsString());
1732     } else if (token->AsString() == "WRITE") {
1733       token = tokenizer_->NextToken();
1734 
1735       if (!token->IsIdentifier())
1736         return Result("invalid value for WRITE");
1737 
1738       if (token->AsString() == "on")
1739         pipeline->GetPipelineData()->SetEnableDepthWrite(true);
1740       else if (token->AsString() == "off")
1741         pipeline->GetPipelineData()->SetEnableDepthWrite(false);
1742       else
1743         return Result("invalid value for WRITE: " + token->AsString());
1744     } else if (token->AsString() == "COMPARE_OP") {
1745       token = tokenizer_->NextToken();
1746 
1747       if (!token->IsIdentifier())
1748         return Result("invalid value for COMPARE_OP");
1749 
1750       CompareOp compare_op = StrToCompareOp(token->AsString());
1751       if (compare_op != CompareOp::kUnknown) {
1752         pipeline->GetPipelineData()->SetDepthCompareOp(compare_op);
1753       } else {
1754         return Result("invalid value for COMPARE_OP: " + token->AsString());
1755       }
1756     } else if (token->AsString() == "BOUNDS") {
1757       token = tokenizer_->NextToken();
1758       if (!token->IsIdentifier() || token->AsString() != "min")
1759         return Result("BOUNDS expecting min");
1760 
1761       token = tokenizer_->NextToken();
1762       if (!token->IsDouble())
1763         return Result("BOUNDS invalid value for min");
1764       pipeline->GetPipelineData()->SetMinDepthBounds(token->AsFloat());
1765 
1766       token = tokenizer_->NextToken();
1767       if (!token->IsIdentifier() || token->AsString() != "max")
1768         return Result("BOUNDS expecting max");
1769 
1770       token = tokenizer_->NextToken();
1771       if (!token->IsDouble())
1772         return Result("BOUNDS invalid value for max");
1773       pipeline->GetPipelineData()->SetMaxDepthBounds(token->AsFloat());
1774     } else if (token->AsString() == "BIAS") {
1775       pipeline->GetPipelineData()->SetEnableDepthBias(true);
1776 
1777       token = tokenizer_->NextToken();
1778       if (!token->IsIdentifier() || token->AsString() != "constant")
1779         return Result("BIAS expecting constant");
1780 
1781       token = tokenizer_->NextToken();
1782       if (!token->IsDouble())
1783         return Result("BIAS invalid value for constant");
1784       pipeline->GetPipelineData()->SetDepthBiasConstantFactor(token->AsFloat());
1785 
1786       token = tokenizer_->NextToken();
1787       if (!token->IsIdentifier() || token->AsString() != "clamp")
1788         return Result("BIAS expecting clamp");
1789 
1790       token = tokenizer_->NextToken();
1791       if (!token->IsDouble())
1792         return Result("BIAS invalid value for clamp");
1793       pipeline->GetPipelineData()->SetDepthBiasClamp(token->AsFloat());
1794 
1795       token = tokenizer_->NextToken();
1796       if (!token->IsIdentifier() || token->AsString() != "slope")
1797         return Result("BIAS expecting slope");
1798 
1799       token = tokenizer_->NextToken();
1800       if (!token->IsDouble())
1801         return Result("BIAS invalid value for slope");
1802       pipeline->GetPipelineData()->SetDepthBiasSlopeFactor(token->AsFloat());
1803     } else {
1804       return Result("invalid value for DEPTH: " + token->AsString());
1805     }
1806   }
1807 
1808   return ValidateEndOfStatement("DEPTH command");
1809 }
1810 
ParsePipelineStencil(Pipeline * pipeline)1811 Result Parser::ParsePipelineStencil(Pipeline* pipeline) {
1812   auto token = tokenizer_->NextToken();
1813   if (!token->IsIdentifier())
1814     return Result("STENCIL missing face");
1815 
1816   bool setFront = false;
1817   bool setBack = false;
1818 
1819   if (token->AsString() == "front") {
1820     setFront = true;
1821   } else if (token->AsString() == "back") {
1822     setBack = true;
1823   } else if (token->AsString() == "front_and_back") {
1824     setFront = true;
1825     setBack = true;
1826   } else {
1827     return Result("STENCIL invalid face: " + token->AsString());
1828   }
1829 
1830   while (true) {
1831     token = tokenizer_->NextToken();
1832     if (token->IsEOL())
1833       continue;
1834     if (token->IsEOS())
1835       return Result("STENCIL missing END command");
1836     if (!token->IsIdentifier())
1837       return Result("STENCIL options must be identifiers");
1838     if (token->AsString() == "END")
1839       break;
1840 
1841     if (token->AsString() == "TEST") {
1842       token = tokenizer_->NextToken();
1843 
1844       if (!token->IsIdentifier())
1845         return Result("STENCIL invalid value for TEST");
1846 
1847       if (token->AsString() == "on")
1848         pipeline->GetPipelineData()->SetEnableStencilTest(true);
1849       else if (token->AsString() == "off")
1850         pipeline->GetPipelineData()->SetEnableStencilTest(false);
1851       else
1852         return Result("STENCIL invalid value for TEST: " + token->AsString());
1853     } else if (token->AsString() == "FAIL_OP") {
1854       token = tokenizer_->NextToken();
1855 
1856       if (!token->IsIdentifier())
1857         return Result("STENCIL invalid value for FAIL_OP");
1858 
1859       StencilOp stencil_op = StrToStencilOp(token->AsString());
1860       if (stencil_op == StencilOp::kUnknown) {
1861         return Result("STENCIL invalid value for FAIL_OP: " +
1862                       token->AsString());
1863       }
1864       if (setFront)
1865         pipeline->GetPipelineData()->SetFrontFailOp(stencil_op);
1866       if (setBack)
1867         pipeline->GetPipelineData()->SetBackFailOp(stencil_op);
1868     } else if (token->AsString() == "PASS_OP") {
1869       token = tokenizer_->NextToken();
1870 
1871       if (!token->IsIdentifier())
1872         return Result("STENCIL invalid value for PASS_OP");
1873 
1874       StencilOp stencil_op = StrToStencilOp(token->AsString());
1875       if (stencil_op == StencilOp::kUnknown) {
1876         return Result("STENCIL invalid value for PASS_OP: " +
1877                       token->AsString());
1878       }
1879       if (setFront)
1880         pipeline->GetPipelineData()->SetFrontPassOp(stencil_op);
1881       if (setBack)
1882         pipeline->GetPipelineData()->SetBackPassOp(stencil_op);
1883     } else if (token->AsString() == "DEPTH_FAIL_OP") {
1884       token = tokenizer_->NextToken();
1885 
1886       if (!token->IsIdentifier())
1887         return Result("STENCIL invalid value for DEPTH_FAIL_OP");
1888 
1889       StencilOp stencil_op = StrToStencilOp(token->AsString());
1890       if (stencil_op == StencilOp::kUnknown) {
1891         return Result("STENCIL invalid value for DEPTH_FAIL_OP: " +
1892                       token->AsString());
1893       }
1894       if (setFront)
1895         pipeline->GetPipelineData()->SetFrontDepthFailOp(stencil_op);
1896       if (setBack)
1897         pipeline->GetPipelineData()->SetBackDepthFailOp(stencil_op);
1898     } else if (token->AsString() == "COMPARE_OP") {
1899       token = tokenizer_->NextToken();
1900 
1901       if (!token->IsIdentifier())
1902         return Result("STENCIL invalid value for COMPARE_OP");
1903 
1904       CompareOp compare_op = StrToCompareOp(token->AsString());
1905       if (compare_op == CompareOp::kUnknown) {
1906         return Result("STENCIL invalid value for COMPARE_OP: " +
1907                       token->AsString());
1908       }
1909       if (setFront)
1910         pipeline->GetPipelineData()->SetFrontCompareOp(compare_op);
1911       if (setBack)
1912         pipeline->GetPipelineData()->SetBackCompareOp(compare_op);
1913     } else if (token->AsString() == "COMPARE_MASK") {
1914       token = tokenizer_->NextToken();
1915 
1916       if (!token->IsInteger())
1917         return Result("STENCIL invalid value for COMPARE_MASK");
1918 
1919       if (setFront)
1920         pipeline->GetPipelineData()->SetFrontCompareMask(token->AsUint32());
1921       if (setBack)
1922         pipeline->GetPipelineData()->SetBackCompareMask(token->AsUint32());
1923     } else if (token->AsString() == "WRITE_MASK") {
1924       token = tokenizer_->NextToken();
1925 
1926       if (!token->IsInteger())
1927         return Result("STENCIL invalid value for WRITE_MASK");
1928 
1929       if (setFront)
1930         pipeline->GetPipelineData()->SetFrontWriteMask(token->AsUint32());
1931       if (setBack)
1932         pipeline->GetPipelineData()->SetBackWriteMask(token->AsUint32());
1933     } else if (token->AsString() == "REFERENCE") {
1934       token = tokenizer_->NextToken();
1935 
1936       if (!token->IsInteger())
1937         return Result("STENCIL invalid value for REFERENCE");
1938 
1939       if (setFront)
1940         pipeline->GetPipelineData()->SetFrontReference(token->AsUint32());
1941       if (setBack)
1942         pipeline->GetPipelineData()->SetBackReference(token->AsUint32());
1943     } else {
1944       return Result("STENCIL invalid value for STENCIL: " + token->AsString());
1945     }
1946   }
1947 
1948   return ValidateEndOfStatement("STENCIL command");
1949 }
1950 
ParsePipelineBlend(Pipeline * pipeline)1951 Result Parser::ParsePipelineBlend(Pipeline* pipeline) {
1952   pipeline->GetPipelineData()->SetEnableBlend(true);
1953 
1954   while (true) {
1955     auto token = tokenizer_->NextToken();
1956     if (token->IsEOL())
1957       continue;
1958     if (token->IsEOS())
1959       return Result("BLEND missing END command");
1960     if (!token->IsIdentifier())
1961       return Result("BLEND options must be identifiers");
1962     if (token->AsString() == "END")
1963       break;
1964 
1965     if (token->AsString() == "SRC_COLOR_FACTOR") {
1966       token = tokenizer_->NextToken();
1967 
1968       if (!token->IsIdentifier())
1969         return Result("BLEND invalid value for SRC_COLOR_FACTOR");
1970 
1971       const auto factor = NameToBlendFactor(token->AsString());
1972       if (factor == BlendFactor::kUnknown)
1973         return Result("BLEND invalid value for SRC_COLOR_FACTOR: " +
1974                       token->AsString());
1975       pipeline->GetPipelineData()->SetSrcColorBlendFactor(
1976           NameToBlendFactor(token->AsString()));
1977     } else if (token->AsString() == "DST_COLOR_FACTOR") {
1978       token = tokenizer_->NextToken();
1979 
1980       if (!token->IsIdentifier())
1981         return Result("BLEND invalid value for DST_COLOR_FACTOR");
1982 
1983       const auto factor = NameToBlendFactor(token->AsString());
1984       if (factor == BlendFactor::kUnknown)
1985         return Result("BLEND invalid value for DST_COLOR_FACTOR: " +
1986                       token->AsString());
1987       pipeline->GetPipelineData()->SetDstColorBlendFactor(
1988           NameToBlendFactor(token->AsString()));
1989     } else if (token->AsString() == "SRC_ALPHA_FACTOR") {
1990       token = tokenizer_->NextToken();
1991 
1992       if (!token->IsIdentifier())
1993         return Result("BLEND invalid value for SRC_ALPHA_FACTOR");
1994 
1995       const auto factor = NameToBlendFactor(token->AsString());
1996       if (factor == BlendFactor::kUnknown)
1997         return Result("BLEND invalid value for SRC_ALPHA_FACTOR: " +
1998                       token->AsString());
1999       pipeline->GetPipelineData()->SetSrcAlphaBlendFactor(
2000           NameToBlendFactor(token->AsString()));
2001     } else if (token->AsString() == "DST_ALPHA_FACTOR") {
2002       token = tokenizer_->NextToken();
2003 
2004       if (!token->IsIdentifier())
2005         return Result("BLEND invalid value for DST_ALPHA_FACTOR");
2006 
2007       const auto factor = NameToBlendFactor(token->AsString());
2008       if (factor == BlendFactor::kUnknown)
2009         return Result("BLEND invalid value for DST_ALPHA_FACTOR: " +
2010                       token->AsString());
2011       pipeline->GetPipelineData()->SetDstAlphaBlendFactor(
2012           NameToBlendFactor(token->AsString()));
2013     } else if (token->AsString() == "COLOR_OP") {
2014       token = tokenizer_->NextToken();
2015 
2016       if (!token->IsIdentifier())
2017         return Result("BLEND invalid value for COLOR_OP");
2018 
2019       const auto op = NameToBlendOp(token->AsString());
2020       if (op == BlendOp::kUnknown)
2021         return Result("BLEND invalid value for COLOR_OP: " + token->AsString());
2022       pipeline->GetPipelineData()->SetColorBlendOp(
2023           NameToBlendOp(token->AsString()));
2024     } else if (token->AsString() == "ALPHA_OP") {
2025       token = tokenizer_->NextToken();
2026 
2027       if (!token->IsIdentifier())
2028         return Result("BLEND invalid value for ALPHA_OP");
2029 
2030       const auto op = NameToBlendOp(token->AsString());
2031       if (op == BlendOp::kUnknown)
2032         return Result("BLEND invalid value for ALPHA_OP: " + token->AsString());
2033       pipeline->GetPipelineData()->SetAlphaBlendOp(
2034           NameToBlendOp(token->AsString()));
2035     } else {
2036       return Result("BLEND invalid value for BLEND: " + token->AsString());
2037     }
2038   }
2039 
2040   return ValidateEndOfStatement("BLEND command");
2041 }
2042 
ParsePipelineShaderGroup(Pipeline * pipeline)2043 Result Parser::ParsePipelineShaderGroup(Pipeline* pipeline) {
2044   std::unique_ptr<Token> token = tokenizer_->NextToken();
2045   if (!token->IsIdentifier())
2046     return Result("Group name expected");
2047 
2048   auto tok = token->AsString();
2049   if (pipeline->GetShaderGroup(tok))
2050     return Result("Group name already exists");
2051   std::unique_ptr<ShaderGroup> group = MakeUnique<ShaderGroup>();
2052   group->SetName(tok);
2053 
2054   while (true) {
2055     token = tokenizer_->NextToken();
2056     if (token->IsEOL() || token->IsEOS())
2057       break;
2058     if (!token->IsIdentifier())
2059       return Result("Shader name expected");
2060 
2061     tok = token->AsString();
2062     Shader* shader = script_->GetShader(tok);
2063     if (shader == nullptr)
2064       return Result("Shader not found: " + tok);
2065 
2066     if (script_->FindShader(pipeline, shader) == nullptr) {
2067       Result r = pipeline->AddShader(shader, shader->GetType());
2068       if (!r.IsSuccess())
2069         return r;
2070     }
2071 
2072     switch (shader->GetType()) {
2073       case kShaderTypeRayGeneration:
2074       case kShaderTypeMiss:
2075       case kShaderTypeCall: {
2076         if (group->IsHitGroup())
2077           return Result("Hit group cannot contain general shaders");
2078         if (group->GetGeneralShader() != nullptr)
2079           return Result("Two general shaders cannot be in one group");
2080         group->SetGeneralShader(shader);
2081         break;
2082       }
2083       case kShaderTypeAnyHit: {
2084         if (group->IsGeneralGroup())
2085           return Result("General group cannot contain any hit shaders");
2086         if (group->GetAnyHitShader() != nullptr)
2087           return Result("Two any hit shaders cannot be in one group");
2088         group->SetAnyHitShader(shader);
2089         break;
2090       }
2091       case kShaderTypeClosestHit: {
2092         if (group->IsGeneralGroup())
2093           return Result("General group cannot contain closest hit shaders");
2094         if (group->GetClosestHitShader() != nullptr)
2095           return Result("Two closest hit shaders cannot be in one group");
2096         group->SetClosestHitShader(shader);
2097         break;
2098       }
2099       case kShaderTypeIntersection: {
2100         if (group->IsGeneralGroup())
2101           return Result("General group cannot contain intersection shaders");
2102         if (group->GetIntersectionShader() != nullptr)
2103           return Result("Two intersection shaders cannot be in one group");
2104         group->SetIntersectionShader(shader);
2105         break;
2106       }
2107       default:
2108         return Result("Shader must be of raytracing type");
2109     }
2110   }
2111 
2112   pipeline->AddShaderGroup(std::move(group));
2113 
2114   return {};
2115 }
2116 
ParseStruct()2117 Result Parser::ParseStruct() {
2118   auto token = tokenizer_->NextToken();
2119   if (!token->IsIdentifier())
2120     return Result("invalid STRUCT name provided");
2121 
2122   auto struct_name = token->AsString();
2123   if (struct_name == "STRIDE")
2124     return Result("missing STRUCT name");
2125 
2126   auto s = MakeUnique<type::Struct>();
2127   auto type = s.get();
2128 
2129   Result r = script_->AddType(struct_name, std::move(s));
2130   if (!r.IsSuccess())
2131     return r;
2132 
2133   token = tokenizer_->NextToken();
2134   if (token->IsIdentifier()) {
2135     if (token->AsString() != "STRIDE")
2136       return Result("invalid token in STRUCT definition");
2137 
2138     token = tokenizer_->NextToken();
2139     if (token->IsEOL() || token->IsEOS())
2140       return Result("missing value for STRIDE");
2141     if (!token->IsInteger())
2142       return Result("invalid value for STRIDE");
2143 
2144     type->SetStrideInBytes(token->AsUint32());
2145     token = tokenizer_->NextToken();
2146   }
2147   if (!token->IsEOL()) {
2148     return Result("extra token " + token->ToOriginalString() +
2149                   " after STRUCT header");
2150   }
2151 
2152   std::map<std::string, bool> seen;
2153   for (;;) {
2154     token = tokenizer_->NextToken();
2155     if (!token->IsIdentifier())
2156       return Result("invalid type for STRUCT member");
2157     if (token->AsString() == "END")
2158       break;
2159 
2160     if (token->AsString() == struct_name)
2161       return Result("recursive types are not allowed");
2162 
2163     type::Type* member_type = script_->GetType(token->AsString());
2164     if (!member_type) {
2165       auto t = ToType(token->AsString());
2166       if (!t) {
2167         return Result("unknown type '" + token->AsString() +
2168                       "' for STRUCT member");
2169       }
2170 
2171       member_type = t.get();
2172       script_->RegisterType(std::move(t));
2173     }
2174 
2175     token = tokenizer_->NextToken();
2176     if (token->IsEOL())
2177       return Result("missing name for STRUCT member");
2178     if (!token->IsIdentifier())
2179       return Result("invalid name for STRUCT member");
2180 
2181     auto member_name = token->AsString();
2182     if (seen.find(member_name) != seen.end())
2183       return Result("duplicate name for STRUCT member");
2184 
2185     seen[member_name] = true;
2186 
2187     auto m = type->AddMember(member_type);
2188     m->name = member_name;
2189 
2190     token = tokenizer_->NextToken();
2191     while (token->IsIdentifier()) {
2192       if (token->AsString() == "OFFSET") {
2193         token = tokenizer_->NextToken();
2194         if (token->IsEOL())
2195           return Result("missing value for STRUCT member OFFSET");
2196         if (!token->IsInteger())
2197           return Result("invalid value for STRUCT member OFFSET");
2198 
2199         m->offset_in_bytes = token->AsInt32();
2200       } else if (token->AsString() == "ARRAY_STRIDE") {
2201         token = tokenizer_->NextToken();
2202         if (token->IsEOL())
2203           return Result("missing value for STRUCT member ARRAY_STRIDE");
2204         if (!token->IsInteger())
2205           return Result("invalid value for STRUCT member ARRAY_STRIDE");
2206         if (!member_type->IsArray())
2207           return Result("ARRAY_STRIDE only valid on array members");
2208 
2209         m->array_stride_in_bytes = token->AsInt32();
2210       } else if (token->AsString() == "MATRIX_STRIDE") {
2211         token = tokenizer_->NextToken();
2212         if (token->IsEOL())
2213           return Result("missing value for STRUCT member MATRIX_STRIDE");
2214         if (!token->IsInteger())
2215           return Result("invalid value for STRUCT member MATRIX_STRIDE");
2216         if (!member_type->IsMatrix())
2217           return Result("MATRIX_STRIDE only valid on matrix members");
2218 
2219         m->matrix_stride_in_bytes = token->AsInt32();
2220       } else {
2221         return Result("unknown param '" + token->AsString() +
2222                       "' for STRUCT member");
2223       }
2224 
2225       token = tokenizer_->NextToken();
2226     }
2227 
2228     if (!token->IsEOL())
2229       return Result("extra param for STRUCT member");
2230   }
2231 
2232   return {};
2233 }
2234 
ParseBuffer()2235 Result Parser::ParseBuffer() {
2236   auto token = tokenizer_->NextToken();
2237   if (!token->IsIdentifier())
2238     return Result("invalid BUFFER name provided");
2239 
2240   auto name = token->AsString();
2241   if (name == "DATA_TYPE" || name == "FORMAT")
2242     return Result("missing BUFFER name");
2243 
2244   token = tokenizer_->NextToken();
2245   if (!token->IsIdentifier())
2246     return Result("invalid BUFFER command provided");
2247 
2248   std::unique_ptr<Buffer> buffer;
2249   auto& cmd = token->AsString();
2250   if (cmd == "DATA_TYPE") {
2251     buffer = MakeUnique<Buffer>();
2252 
2253     Result r = ParseBufferInitializer(buffer.get());
2254     if (!r.IsSuccess())
2255       return r;
2256   } else if (cmd == "FORMAT") {
2257     token = tokenizer_->NextToken();
2258     if (!token->IsIdentifier())
2259       return Result("BUFFER FORMAT must be an identifier");
2260 
2261     buffer = MakeUnique<Buffer>();
2262 
2263     auto type = script_->ParseType(token->AsString());
2264     if (!type)
2265       return Result("invalid BUFFER FORMAT");
2266 
2267     auto fmt = MakeUnique<Format>(type);
2268     buffer->SetFormat(fmt.get());
2269     script_->RegisterFormat(std::move(fmt));
2270 
2271     token = tokenizer_->PeekNextToken();
2272     while (token->IsIdentifier()) {
2273       if (token->AsString() == "MIP_LEVELS") {
2274         tokenizer_->NextToken();
2275         token = tokenizer_->NextToken();
2276 
2277         if (!token->IsInteger())
2278           return Result("invalid value for MIP_LEVELS");
2279 
2280         buffer->SetMipLevels(token->AsUint32());
2281       } else if (token->AsString() == "FILE") {
2282         tokenizer_->NextToken();
2283         Result r = ParseBufferInitializerFile(buffer.get());
2284 
2285         if (!r.IsSuccess())
2286           return r;
2287       } else if (token->AsString() == "SAMPLES") {
2288         tokenizer_->NextToken();
2289         token = tokenizer_->NextToken();
2290         if (!token->IsInteger())
2291           return Result("expected integer value for SAMPLES");
2292 
2293         const uint32_t samples = token->AsUint32();
2294         if (!IsValidSampleCount(samples))
2295           return Result("invalid sample count: " + token->ToOriginalString());
2296 
2297         buffer->SetSamples(samples);
2298       } else {
2299         break;
2300       }
2301       token = tokenizer_->PeekNextToken();
2302     }
2303   } else {
2304     return Result("unknown BUFFER command provided: " + cmd);
2305   }
2306   buffer->SetName(name);
2307 
2308   Result r = script_->AddBuffer(std::move(buffer));
2309   if (!r.IsSuccess())
2310     return r;
2311 
2312   return {};
2313 }
2314 
ParseImage()2315 Result Parser::ParseImage() {
2316   auto token = tokenizer_->NextToken();
2317   if (!token->IsIdentifier())
2318     return Result("invalid IMAGE name provided");
2319 
2320   auto name = token->AsString();
2321   if (name == "DATA_TYPE" || name == "FORMAT")
2322     return Result("missing IMAGE name");
2323 
2324   std::unique_ptr<Buffer> buffer = MakeUnique<Buffer>();
2325   buffer->SetName(name);
2326   bool width_set = false;
2327   bool height_set = false;
2328   bool depth_set = false;
2329 
2330   token = tokenizer_->PeekNextToken();
2331   while (token->IsIdentifier()) {
2332     if (token->AsString() == "FILL" || token->AsString() == "SERIES_FROM" ||
2333         token->AsString() == "DATA") {
2334       break;
2335     }
2336 
2337     tokenizer_->NextToken();
2338 
2339     if (token->AsString() == "DATA_TYPE") {
2340       token = tokenizer_->NextToken();
2341       if (!token->IsIdentifier())
2342         return Result("IMAGE invalid data type");
2343 
2344       auto type = script_->ParseType(token->AsString());
2345       std::unique_ptr<Format> fmt;
2346       if (type != nullptr) {
2347         fmt = MakeUnique<Format>(type);
2348         buffer->SetFormat(fmt.get());
2349       } else {
2350         auto new_type = ToType(token->AsString());
2351         if (!new_type) {
2352           return Result("invalid data type '" + token->AsString() +
2353                         "' provided");
2354         }
2355 
2356         fmt = MakeUnique<Format>(new_type.get());
2357         buffer->SetFormat(fmt.get());
2358         script_->RegisterType(std::move(new_type));
2359       }
2360       script_->RegisterFormat(std::move(fmt));
2361     } else if (token->AsString() == "FORMAT") {
2362       token = tokenizer_->NextToken();
2363       if (!token->IsIdentifier())
2364         return Result("IMAGE FORMAT must be an identifier");
2365 
2366       auto type = script_->ParseType(token->AsString());
2367       if (!type)
2368         return Result("invalid IMAGE FORMAT");
2369 
2370       auto fmt = MakeUnique<Format>(type);
2371       buffer->SetFormat(fmt.get());
2372       script_->RegisterFormat(std::move(fmt));
2373     } else if (token->AsString() == "MIP_LEVELS") {
2374       token = tokenizer_->NextToken();
2375 
2376       if (!token->IsInteger())
2377         return Result("invalid value for MIP_LEVELS");
2378 
2379       buffer->SetMipLevels(token->AsUint32());
2380     } else if (token->AsString() == "DIM_1D") {
2381       buffer->SetImageDimension(ImageDimension::k1D);
2382     } else if (token->AsString() == "DIM_2D") {
2383       buffer->SetImageDimension(ImageDimension::k2D);
2384     } else if (token->AsString() == "DIM_3D") {
2385       buffer->SetImageDimension(ImageDimension::k3D);
2386     } else if (token->AsString() == "WIDTH") {
2387       token = tokenizer_->NextToken();
2388       if (!token->IsInteger() || token->AsUint32() == 0)
2389         return Result("expected positive IMAGE WIDTH");
2390 
2391       buffer->SetWidth(token->AsUint32());
2392       width_set = true;
2393     } else if (token->AsString() == "HEIGHT") {
2394       token = tokenizer_->NextToken();
2395       if (!token->IsInteger() || token->AsUint32() == 0)
2396         return Result("expected positive IMAGE HEIGHT");
2397 
2398       buffer->SetHeight(token->AsUint32());
2399       height_set = true;
2400     } else if (token->AsString() == "DEPTH") {
2401       token = tokenizer_->NextToken();
2402       if (!token->IsInteger() || token->AsUint32() == 0)
2403         return Result("expected positive IMAGE DEPTH");
2404 
2405       buffer->SetDepth(token->AsUint32());
2406       depth_set = true;
2407     } else if (token->AsString() == "SAMPLES") {
2408       token = tokenizer_->NextToken();
2409       if (!token->IsInteger())
2410         return Result("expected integer value for SAMPLES");
2411 
2412       const uint32_t samples = token->AsUint32();
2413       if (!IsValidSampleCount(samples))
2414         return Result("invalid sample count: " + token->ToOriginalString());
2415 
2416       buffer->SetSamples(samples);
2417     } else {
2418       return Result("unknown IMAGE command provided: " +
2419                     token->ToOriginalString());
2420     }
2421     token = tokenizer_->PeekNextToken();
2422   }
2423 
2424   if (buffer->GetImageDimension() == ImageDimension::k3D && !depth_set)
2425     return Result("expected IMAGE DEPTH");
2426 
2427   if ((buffer->GetImageDimension() == ImageDimension::k3D ||
2428        buffer->GetImageDimension() == ImageDimension::k2D) &&
2429       !height_set) {
2430     return Result("expected IMAGE HEIGHT");
2431   }
2432   if (!width_set)
2433     return Result("expected IMAGE WIDTH");
2434 
2435   const uint32_t size_in_items =
2436       buffer->GetWidth() * buffer->GetHeight() * buffer->GetDepth();
2437   buffer->SetElementCount(size_in_items);
2438 
2439   // Parse initializers.
2440   token = tokenizer_->NextToken();
2441   if (token->IsIdentifier()) {
2442     if (token->AsString() == "DATA") {
2443       Result r = ParseBufferInitializerData(buffer.get());
2444       if (!r.IsSuccess())
2445         return r;
2446 
2447       if (size_in_items != buffer->ElementCount()) {
2448         return Result(
2449             "Elements provided in data does not match size specified: " +
2450             std::to_string(size_in_items) + " specified vs " +
2451             std::to_string(buffer->ElementCount()) + " provided");
2452       }
2453     } else if (token->AsString() == "FILL") {
2454       Result r = ParseBufferInitializerFill(buffer.get(), size_in_items);
2455       if (!r.IsSuccess())
2456         return r;
2457     } else if (token->AsString() == "SERIES_FROM") {
2458       Result r = ParseBufferInitializerSeries(buffer.get(), size_in_items);
2459       if (!r.IsSuccess())
2460         return r;
2461     } else {
2462       return Result("unexpected IMAGE token: " + token->AsString());
2463     }
2464   } else if (!token->IsEOL() && !token->IsEOS()) {
2465     return Result("unexpected IMAGE token: " + token->ToOriginalString());
2466   }
2467 
2468   Result r = script_->AddBuffer(std::move(buffer));
2469   if (!r.IsSuccess())
2470     return r;
2471 
2472   return {};
2473 }
2474 
ParseBufferInitializer(Buffer * buffer)2475 Result Parser::ParseBufferInitializer(Buffer* buffer) {
2476   auto token = tokenizer_->NextToken();
2477   if (!token->IsIdentifier())
2478     return Result("BUFFER invalid data type");
2479 
2480   auto type = script_->ParseType(token->AsString());
2481   std::unique_ptr<Format> fmt;
2482   if (type != nullptr) {
2483     fmt = MakeUnique<Format>(type);
2484     buffer->SetFormat(fmt.get());
2485   } else {
2486     auto new_type = ToType(token->AsString());
2487     if (!new_type)
2488       return Result("invalid data type '" + token->AsString() + "' provided");
2489 
2490     fmt = MakeUnique<Format>(new_type.get());
2491     buffer->SetFormat(fmt.get());
2492     type = new_type.get();
2493     script_->RegisterType(std::move(new_type));
2494   }
2495   script_->RegisterFormat(std::move(fmt));
2496 
2497   token = tokenizer_->NextToken();
2498   if (!token->IsIdentifier())
2499     return Result("BUFFER missing initializer");
2500 
2501   if (token->AsString() == "STD140") {
2502     buffer->GetFormat()->SetLayout(Format::Layout::kStd140);
2503     token = tokenizer_->NextToken();
2504   } else if (token->AsString() == "STD430") {
2505     buffer->GetFormat()->SetLayout(Format::Layout::kStd430);
2506     token = tokenizer_->NextToken();
2507   }
2508 
2509   if (!token->IsIdentifier())
2510     return Result("BUFFER missing initializer");
2511 
2512   if (token->AsString() == "SIZE")
2513     return ParseBufferInitializerSize(buffer);
2514   if (token->AsString() == "WIDTH") {
2515     token = tokenizer_->NextToken();
2516     if (!token->IsInteger())
2517       return Result("expected an integer for WIDTH");
2518     const uint32_t width = token->AsUint32();
2519     if (width == 0)
2520       return Result("expected WIDTH to be positive");
2521     buffer->SetWidth(width);
2522     buffer->SetImageDimension(ImageDimension::k2D);
2523 
2524     token = tokenizer_->NextToken();
2525     if (token->AsString() != "HEIGHT")
2526       return Result("BUFFER HEIGHT missing");
2527     token = tokenizer_->NextToken();
2528     if (!token->IsInteger())
2529       return Result("expected an integer for HEIGHT");
2530     const uint32_t height = token->AsUint32();
2531     if (height == 0)
2532       return Result("expected HEIGHT to be positive");
2533     buffer->SetHeight(height);
2534 
2535     token = tokenizer_->NextToken();
2536     uint32_t size_in_items = width * height;
2537     buffer->SetElementCount(size_in_items);
2538     if (token->AsString() == "FILL")
2539       return ParseBufferInitializerFill(buffer, size_in_items);
2540     if (token->AsString() == "SERIES_FROM")
2541       return ParseBufferInitializerSeries(buffer, size_in_items);
2542     return {};
2543   }
2544   if (token->AsString() == "DATA")
2545     return ParseBufferInitializerData(buffer);
2546 
2547   return Result("unknown initializer for BUFFER");
2548 }
2549 
ParseBufferInitializerSize(Buffer * buffer)2550 Result Parser::ParseBufferInitializerSize(Buffer* buffer) {
2551   auto token = tokenizer_->NextToken();
2552   if (token->IsEOS() || token->IsEOL())
2553     return Result("BUFFER size missing");
2554   if (!token->IsInteger())
2555     return Result("BUFFER size invalid");
2556 
2557   uint32_t size_in_items = token->AsUint32();
2558   buffer->SetElementCount(size_in_items);
2559 
2560   token = tokenizer_->NextToken();
2561   if (!token->IsIdentifier())
2562     return Result("BUFFER invalid initializer");
2563 
2564   if (token->AsString() == "FILL")
2565     return ParseBufferInitializerFill(buffer, size_in_items);
2566   if (token->AsString() == "SERIES_FROM")
2567     return ParseBufferInitializerSeries(buffer, size_in_items);
2568   if (token->AsString() == "FILE")
2569     return ParseBufferInitializerFile(buffer);
2570 
2571   return Result("invalid BUFFER initializer provided");
2572 }
2573 
ParseBufferInitializerFill(Buffer * buffer,uint32_t size_in_items)2574 Result Parser::ParseBufferInitializerFill(Buffer* buffer,
2575                                           uint32_t size_in_items) {
2576   auto token = tokenizer_->NextToken();
2577   if (token->IsEOS() || token->IsEOL())
2578     return Result("missing BUFFER fill value");
2579   if (!token->IsInteger() && !token->IsDouble())
2580     return Result("invalid BUFFER fill value");
2581 
2582   auto fmt = buffer->GetFormat();
2583   bool is_double_data = fmt->IsFloat32() || fmt->IsFloat64();
2584 
2585   // Inflate the size because our items are multi-dimensional.
2586   size_in_items = size_in_items * fmt->InputNeededPerElement();
2587 
2588   std::vector<Value> values;
2589   values.resize(size_in_items);
2590   for (size_t i = 0; i < size_in_items; ++i) {
2591     if (is_double_data)
2592       values[i].SetDoubleValue(token->AsDouble());
2593     else
2594       values[i].SetIntValue(token->AsUint64());
2595   }
2596   Result r = buffer->SetData(std::move(values));
2597   if (!r.IsSuccess())
2598     return r;
2599 
2600   return ValidateEndOfStatement("BUFFER fill command");
2601 }
2602 
ParseBufferInitializerSeries(Buffer * buffer,uint32_t size_in_items)2603 Result Parser::ParseBufferInitializerSeries(Buffer* buffer,
2604                                             uint32_t size_in_items) {
2605   auto token = tokenizer_->NextToken();
2606   if (token->IsEOS() || token->IsEOL())
2607     return Result("missing BUFFER series_from value");
2608   if (!token->IsInteger() && !token->IsDouble())
2609     return Result("invalid BUFFER series_from value");
2610 
2611   auto type = buffer->GetFormat()->GetType();
2612   if (type->IsMatrix() || type->IsVec())
2613     return Result("BUFFER series_from must not be multi-row/column types");
2614 
2615   Value counter;
2616 
2617   auto n = type->AsNumber();
2618   FormatMode mode = n->GetFormatMode();
2619   uint32_t num_bits = n->NumBits();
2620   if (type::Type::IsFloat32(mode, num_bits) ||
2621       type::Type::IsFloat64(mode, num_bits)) {
2622     counter.SetDoubleValue(token->AsDouble());
2623   } else {
2624     counter.SetIntValue(token->AsUint64());
2625   }
2626 
2627   token = tokenizer_->NextToken();
2628   if (!token->IsIdentifier())
2629     return Result("missing BUFFER series_from inc_by");
2630   if (token->AsString() != "INC_BY")
2631     return Result("BUFFER series_from invalid command");
2632 
2633   token = tokenizer_->NextToken();
2634   if (token->IsEOS() || token->IsEOL())
2635     return Result("missing BUFFER series_from inc_by value");
2636   if (!token->IsInteger() && !token->IsDouble())
2637     return Result("invalid BUFFER series_from inc_by value");
2638 
2639   std::vector<Value> values;
2640   values.resize(size_in_items);
2641   for (size_t i = 0; i < size_in_items; ++i) {
2642     if (type::Type::IsFloat32(mode, num_bits) ||
2643         type::Type::IsFloat64(mode, num_bits)) {
2644       double value = counter.AsDouble();
2645       values[i].SetDoubleValue(value);
2646       counter.SetDoubleValue(value + token->AsDouble());
2647     } else {
2648       uint64_t value = counter.AsUint64();
2649       values[i].SetIntValue(value);
2650       counter.SetIntValue(value + token->AsUint64());
2651     }
2652   }
2653   Result r = buffer->SetData(std::move(values));
2654   if (!r.IsSuccess())
2655     return r;
2656 
2657   return ValidateEndOfStatement("BUFFER series_from command");
2658 }
2659 
ParseBufferInitializerData(Buffer * buffer)2660 Result Parser::ParseBufferInitializerData(Buffer* buffer) {
2661   Result r = ParseBufferData(buffer, tokenizer_.get(), false);
2662 
2663   if (!r.IsSuccess())
2664     return r;
2665 
2666   return ValidateEndOfStatement("BUFFER data command");
2667 }
2668 
ParseBufferInitializerFile(Buffer * buffer)2669 Result Parser::ParseBufferInitializerFile(Buffer* buffer) {
2670   auto token = tokenizer_->NextToken();
2671 
2672   if (!token->IsIdentifier())
2673     return Result("invalid value for FILE");
2674 
2675   BufferDataFileType file_type = BufferDataFileType::kPng;
2676 
2677   if (token->AsString() == "TEXT") {
2678     file_type = BufferDataFileType::kText;
2679     token = tokenizer_->NextToken();
2680   } else if (token->AsString() == "BINARY") {
2681     file_type = BufferDataFileType::kBinary;
2682     token = tokenizer_->NextToken();
2683   } else if (token->AsString() == "PNG") {
2684     token = tokenizer_->NextToken();
2685   }
2686 
2687   if (!token->IsIdentifier())
2688     return Result("missing file name for FILE");
2689 
2690   if (!delegate_)
2691     return Result("missing delegate");
2692 
2693   BufferInfo info;
2694   Result r = delegate_->LoadBufferData(token->AsString(), file_type, &info);
2695 
2696   if (!r.IsSuccess())
2697     return r;
2698 
2699   std::vector<uint8_t>* data = buffer->ValuePtr();
2700 
2701   data->clear();
2702   data->reserve(info.values.size());
2703   for (auto v : info.values) {
2704     data->push_back(v.AsUint8());
2705   }
2706 
2707   if (file_type == BufferDataFileType::kText) {
2708     auto s = std::string(data->begin(), data->end());
2709     Tokenizer tok(s);
2710     r = ParseBufferData(buffer, &tok, true);
2711     if (!r.IsSuccess())
2712       return r;
2713   } else {
2714     buffer->SetElementCount(static_cast<uint32_t>(data->size()) /
2715                             buffer->GetFormat()->SizeInBytes());
2716     buffer->SetWidth(info.width);
2717     buffer->SetHeight(info.height);
2718   }
2719 
2720   return {};
2721 }
2722 
ParseRun()2723 Result Parser::ParseRun() {
2724   auto token = tokenizer_->NextToken();
2725 
2726   // Timed execution option for this specific run.
2727   bool is_timed_execution = false;
2728   if (token->AsString() == "TIMED_EXECUTION") {
2729     token = tokenizer_->NextToken();
2730     is_timed_execution = true;
2731   }
2732 
2733   if (!token->IsIdentifier())
2734     return Result("missing pipeline name for RUN command");
2735 
2736   size_t line = tokenizer_->GetCurrentLine();
2737 
2738   auto* pipeline = script_->GetPipeline(token->AsString());
2739   if (!pipeline)
2740     return Result("unknown pipeline for RUN command: " + token->AsString());
2741 
2742   if (pipeline->IsRayTracing()) {
2743     auto cmd = MakeUnique<RayTracingCommand>(pipeline);
2744     cmd->SetLine(line);
2745     if (is_timed_execution) {
2746       cmd->SetTimedExecution();
2747     }
2748 
2749     while (true) {
2750       if (tokenizer_->PeekNextToken()->IsInteger())
2751         break;
2752 
2753       token = tokenizer_->NextToken();
2754 
2755       if (token->IsEOL() || token->IsEOS())
2756         return Result("Incomplete RUN command");
2757 
2758       if (!token->IsIdentifier())
2759         return Result("Shader binding table type is expected");
2760 
2761       std::string tok = token->AsString();
2762       token = tokenizer_->NextToken();
2763 
2764       if (!token->IsIdentifier())
2765         return Result("Shader binding table name expected");
2766 
2767       std::string sbtname = token->AsString();
2768       if (pipeline->GetSBT(sbtname) == nullptr)
2769         return Result("Shader binding table with this name was not defined");
2770 
2771       if (tok == "RAYGEN") {
2772         if (!cmd->GetRayGenSBTName().empty())
2773           return Result("RAYGEN shader binding table can specified only once");
2774         cmd->SetRGenSBTName(sbtname);
2775       } else if (tok == "MISS") {
2776         if (!cmd->GetMissSBTName().empty())
2777           return Result("MISS shader binding table can specified only once");
2778         cmd->SetMissSBTName(sbtname);
2779       } else if (tok == "HIT") {
2780         if (!cmd->GetHitsSBTName().empty())
2781           return Result("HIT shader binding table can specified only once");
2782         cmd->SetHitsSBTName(sbtname);
2783       } else if (tok == "CALL") {
2784         if (!cmd->GetCallSBTName().empty())
2785           return Result("CALL shader binding table can specified only once");
2786         cmd->SetCallSBTName(sbtname);
2787       } else {
2788         return Result("Unknown shader binding table type");
2789       }
2790     }
2791 
2792     for (int i = 0; i < 3; i++) {
2793       token = tokenizer_->NextToken();
2794 
2795       if (!token->IsInteger())
2796         return Result("invalid parameter for RUN command: " +
2797                       token->ToOriginalString());
2798       if (i == 0)
2799         cmd->SetX(token->AsUint32());
2800       else if (i == 1)
2801         cmd->SetY(token->AsUint32());
2802       else
2803         cmd->SetZ(token->AsUint32());
2804     }
2805 
2806     command_list_.push_back(std::move(cmd));
2807     return ValidateEndOfStatement("RUN command");
2808   }
2809 
2810   token = tokenizer_->NextToken();
2811   if (token->IsEOL() || token->IsEOS())
2812     return Result("RUN command requires parameters");
2813 
2814   if (token->IsInteger()) {
2815     if (!pipeline->IsCompute())
2816       return Result("RUN command requires compute pipeline");
2817 
2818     auto cmd = MakeUnique<ComputeCommand>(pipeline);
2819     cmd->SetLine(line);
2820     cmd->SetX(token->AsUint32());
2821     if (is_timed_execution) {
2822       cmd->SetTimedExecution();
2823     }
2824 
2825     token = tokenizer_->NextToken();
2826     if (!token->IsInteger()) {
2827       return Result("invalid parameter for RUN command: " +
2828                     token->ToOriginalString());
2829     }
2830     cmd->SetY(token->AsUint32());
2831 
2832     token = tokenizer_->NextToken();
2833     if (!token->IsInteger()) {
2834       return Result("invalid parameter for RUN command: " +
2835                     token->ToOriginalString());
2836     }
2837     cmd->SetZ(token->AsUint32());
2838 
2839     command_list_.push_back(std::move(cmd));
2840     return ValidateEndOfStatement("RUN command");
2841   }
2842 
2843   if (!token->IsIdentifier())
2844     return Result("invalid token in RUN command: " + token->ToOriginalString());
2845 
2846   if (token->AsString() == "DRAW_RECT") {
2847     if (!pipeline->IsGraphics())
2848       return Result("RUN command requires graphics pipeline");
2849 
2850     if (pipeline->GetVertexBuffers().size() > 1) {
2851       return Result(
2852           "RUN DRAW_RECT is not supported in a pipeline with more than one "
2853           "vertex buffer attached");
2854     }
2855 
2856     token = tokenizer_->NextToken();
2857     if (token->IsEOS() || token->IsEOL())
2858       return Result("RUN DRAW_RECT command requires parameters");
2859 
2860     if (!token->IsIdentifier() || token->AsString() != "POS") {
2861       return Result("invalid token in RUN command: " +
2862                     token->ToOriginalString() + "; expected POS");
2863     }
2864 
2865     token = tokenizer_->NextToken();
2866     if (!token->IsInteger())
2867       return Result("missing X position for RUN command");
2868 
2869     auto cmd =
2870         MakeUnique<DrawRectCommand>(pipeline, *pipeline->GetPipelineData());
2871     cmd->SetLine(line);
2872     cmd->EnableOrtho();
2873     if (is_timed_execution) {
2874       cmd->SetTimedExecution();
2875     }
2876 
2877     Result r = token->ConvertToDouble();
2878     if (!r.IsSuccess())
2879       return r;
2880     cmd->SetX(token->AsFloat());
2881 
2882     token = tokenizer_->NextToken();
2883     if (!token->IsInteger())
2884       return Result("missing Y position for RUN command");
2885 
2886     r = token->ConvertToDouble();
2887     if (!r.IsSuccess())
2888       return r;
2889     cmd->SetY(token->AsFloat());
2890 
2891     token = tokenizer_->NextToken();
2892     if (!token->IsIdentifier() || token->AsString() != "SIZE") {
2893       return Result("invalid token in RUN command: " +
2894                     token->ToOriginalString() + "; expected SIZE");
2895     }
2896 
2897     token = tokenizer_->NextToken();
2898     if (!token->IsInteger())
2899       return Result("missing width value for RUN command");
2900 
2901     r = token->ConvertToDouble();
2902     if (!r.IsSuccess())
2903       return r;
2904     cmd->SetWidth(token->AsFloat());
2905 
2906     token = tokenizer_->NextToken();
2907     if (!token->IsInteger())
2908       return Result("missing height value for RUN command");
2909 
2910     r = token->ConvertToDouble();
2911     if (!r.IsSuccess())
2912       return r;
2913     cmd->SetHeight(token->AsFloat());
2914 
2915     command_list_.push_back(std::move(cmd));
2916     return ValidateEndOfStatement("RUN command");
2917   }
2918 
2919   if (token->AsString() == "DRAW_GRID") {
2920     if (!pipeline->IsGraphics())
2921       return Result("RUN command requires graphics pipeline");
2922 
2923     if (pipeline->GetVertexBuffers().size() > 0) {
2924       return Result(
2925           "RUN DRAW_GRID is not supported in a pipeline with "
2926           "vertex buffers attached");
2927     }
2928 
2929     token = tokenizer_->NextToken();
2930     if (token->IsEOS() || token->IsEOL())
2931       return Result("RUN DRAW_GRID command requires parameters");
2932 
2933     if (!token->IsIdentifier() || token->AsString() != "POS") {
2934       return Result("invalid token in RUN command: " +
2935                     token->ToOriginalString() + "; expected POS");
2936     }
2937 
2938     token = tokenizer_->NextToken();
2939     if (!token->IsInteger())
2940       return Result("missing X position for RUN command");
2941 
2942     auto cmd =
2943         MakeUnique<DrawGridCommand>(pipeline, *pipeline->GetPipelineData());
2944     cmd->SetLine(line);
2945     if (is_timed_execution) {
2946       cmd->SetTimedExecution();
2947     }
2948 
2949     Result r = token->ConvertToDouble();
2950     if (!r.IsSuccess())
2951       return r;
2952     cmd->SetX(token->AsFloat());
2953 
2954     token = tokenizer_->NextToken();
2955     if (!token->IsInteger())
2956       return Result("missing Y position for RUN command");
2957 
2958     r = token->ConvertToDouble();
2959     if (!r.IsSuccess())
2960       return r;
2961     cmd->SetY(token->AsFloat());
2962 
2963     token = tokenizer_->NextToken();
2964     if (!token->IsIdentifier() || token->AsString() != "SIZE") {
2965       return Result("invalid token in RUN command: " +
2966                     token->ToOriginalString() + "; expected SIZE");
2967     }
2968 
2969     token = tokenizer_->NextToken();
2970     if (!token->IsInteger())
2971       return Result("missing width value for RUN command");
2972 
2973     r = token->ConvertToDouble();
2974     if (!r.IsSuccess())
2975       return r;
2976     cmd->SetWidth(token->AsFloat());
2977 
2978     token = tokenizer_->NextToken();
2979     if (!token->IsInteger())
2980       return Result("missing height value for RUN command");
2981 
2982     r = token->ConvertToDouble();
2983     if (!r.IsSuccess())
2984       return r;
2985     cmd->SetHeight(token->AsFloat());
2986 
2987     token = tokenizer_->NextToken();
2988     if (!token->IsIdentifier() || token->AsString() != "CELLS") {
2989       return Result("invalid token in RUN command: " +
2990                     token->ToOriginalString() + "; expected CELLS");
2991     }
2992 
2993     token = tokenizer_->NextToken();
2994     if (!token->IsInteger())
2995       return Result("missing columns value for RUN command");
2996 
2997     cmd->SetColumns(token->AsUint32());
2998 
2999     token = tokenizer_->NextToken();
3000     if (!token->IsInteger())
3001       return Result("missing rows value for RUN command");
3002 
3003     cmd->SetRows(token->AsUint32());
3004 
3005     command_list_.push_back(std::move(cmd));
3006     return ValidateEndOfStatement("RUN command");
3007   }
3008 
3009   if (token->AsString() == "DRAW_ARRAY") {
3010     if (!pipeline->IsGraphics())
3011       return Result("RUN command requires graphics pipeline");
3012 
3013     if (pipeline->GetVertexBuffers().empty())
3014       return Result("RUN DRAW_ARRAY requires attached vertex buffer");
3015 
3016     token = tokenizer_->NextToken();
3017     if (!token->IsIdentifier() || token->AsString() != "AS")
3018       return Result("missing AS for RUN command");
3019 
3020     token = tokenizer_->NextToken();
3021     if (!token->IsIdentifier()) {
3022       return Result("invalid topology for RUN command: " +
3023                     token->ToOriginalString());
3024     }
3025 
3026     Topology topo = NameToTopology(token->AsString());
3027     if (topo == Topology::kUnknown)
3028       return Result("invalid topology for RUN command: " + token->AsString());
3029 
3030     bool indexed = false;
3031     uint32_t start_idx = 0;
3032     uint32_t count = 0;
3033     uint32_t start_instance = 0;
3034     uint32_t instance_count = 1;
3035 
3036     token = tokenizer_->PeekNextToken();
3037 
3038     while (!token->IsEOS() && !token->IsEOL()) {
3039       token = tokenizer_->NextToken();
3040 
3041       if (!token->IsIdentifier())
3042         return Result("expecting identifier for RUN command");
3043 
3044       if (token->AsString() == "INDEXED") {
3045         if (!pipeline->GetIndexBuffer()) {
3046           return Result(
3047               "RUN DRAW_ARRAYS INDEXED requires attached index buffer");
3048         }
3049 
3050         indexed = true;
3051       } else if (token->AsString() == "START_IDX") {
3052         token = tokenizer_->NextToken();
3053         if (!token->IsInteger()) {
3054           return Result("invalid START_IDX value for RUN command: " +
3055                         token->ToOriginalString());
3056         }
3057         if (token->AsInt32() < 0)
3058           return Result("START_IDX value must be >= 0 for RUN command");
3059         start_idx = token->AsUint32();
3060       } else if (token->AsString() == "COUNT") {
3061         token = tokenizer_->NextToken();
3062         if (!token->IsInteger()) {
3063           return Result("invalid COUNT value for RUN command: " +
3064                         token->ToOriginalString());
3065         }
3066         if (token->AsInt32() <= 0)
3067           return Result("COUNT value must be > 0 for RUN command");
3068 
3069         count = token->AsUint32();
3070       } else if (token->AsString() == "INSTANCE_COUNT") {
3071         token = tokenizer_->NextToken();
3072         if (!token->IsInteger()) {
3073           return Result("invalid INSTANCE_COUNT value for RUN command: " +
3074                         token->ToOriginalString());
3075         }
3076         if (token->AsInt32() <= 0)
3077           return Result("INSTANCE_COUNT value must be > 0 for RUN command");
3078 
3079         instance_count = token->AsUint32();
3080       } else if (token->AsString() == "START_INSTANCE") {
3081         token = tokenizer_->NextToken();
3082         if (!token->IsInteger()) {
3083           return Result("invalid START_INSTANCE value for RUN command: " +
3084                         token->ToOriginalString());
3085         }
3086         if (token->AsInt32() < 0)
3087           return Result("START_INSTANCE value must be >= 0 for RUN command");
3088         start_instance = token->AsUint32();
3089       } else {
3090         return Result("Unexpected identifier for RUN command: " +
3091                       token->ToOriginalString());
3092       }
3093 
3094       token = tokenizer_->PeekNextToken();
3095     }
3096 
3097     uint32_t vertex_count =
3098         indexed ? pipeline->GetIndexBuffer()->ElementCount()
3099                 : pipeline->GetVertexBuffers()[0].buffer->ElementCount();
3100 
3101     // If we get here then we never set count, as if count was set it must
3102     // be > 0.
3103     if (count == 0)
3104       count = vertex_count - start_idx;
3105 
3106     if (start_idx + count > vertex_count) {
3107       if (indexed)
3108         return Result("START_IDX plus COUNT exceeds index buffer data size");
3109       else
3110         return Result("START_IDX plus COUNT exceeds vertex buffer data size");
3111     }
3112 
3113     auto cmd =
3114         MakeUnique<DrawArraysCommand>(pipeline, *pipeline->GetPipelineData());
3115     cmd->SetLine(line);
3116     cmd->SetTopology(topo);
3117     cmd->SetFirstVertexIndex(start_idx);
3118     cmd->SetVertexCount(count);
3119     cmd->SetInstanceCount(instance_count);
3120     cmd->SetFirstInstance(start_instance);
3121     if (is_timed_execution) {
3122       cmd->SetTimedExecution();
3123     }
3124 
3125     if (indexed)
3126       cmd->EnableIndexed();
3127 
3128     command_list_.push_back(std::move(cmd));
3129     return ValidateEndOfStatement("RUN command");
3130   }
3131 
3132   return Result("invalid token in RUN command: " + token->AsString());
3133 }
3134 
ParseClear()3135 Result Parser::ParseClear() {
3136   auto token = tokenizer_->NextToken();
3137   if (!token->IsIdentifier())
3138     return Result("missing pipeline name for CLEAR command");
3139 
3140   size_t line = tokenizer_->GetCurrentLine();
3141 
3142   auto* pipeline = script_->GetPipeline(token->AsString());
3143   if (!pipeline)
3144     return Result("unknown pipeline for CLEAR command: " + token->AsString());
3145   if (!pipeline->IsGraphics())
3146     return Result("CLEAR command requires graphics pipeline");
3147 
3148   auto cmd = MakeUnique<ClearCommand>(pipeline);
3149   cmd->SetLine(line);
3150   command_list_.push_back(std::move(cmd));
3151 
3152   return ValidateEndOfStatement("CLEAR command");
3153 }
3154 
ParseValues(const std::string & name,Format * fmt,std::vector<Value> * values)3155 Result Parser::ParseValues(const std::string& name,
3156                            Format* fmt,
3157                            std::vector<Value>* values) {
3158   assert(values);
3159 
3160   auto token = tokenizer_->NextToken();
3161   const auto& segs = fmt->GetSegments();
3162   size_t seg_idx = 0;
3163   while (!token->IsEOL() && !token->IsEOS()) {
3164     Value v;
3165 
3166     while (segs[seg_idx].IsPadding()) {
3167       ++seg_idx;
3168       if (seg_idx >= segs.size())
3169         seg_idx = 0;
3170     }
3171 
3172     if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
3173       if (!token->IsInteger() && !token->IsDouble() && !token->IsHex()) {
3174         return Result(std::string("Invalid value provided to ") + name +
3175                       " command: " + token->ToOriginalString());
3176       }
3177 
3178       Result r = token->ConvertToDouble();
3179       if (!r.IsSuccess())
3180         return r;
3181 
3182       v.SetDoubleValue(token->AsDouble());
3183     } else {
3184       if (!token->IsInteger() && !token->IsHex()) {
3185         return Result(std::string("Invalid value provided to ") + name +
3186                       " command: " + token->ToOriginalString());
3187       }
3188 
3189       uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
3190       v.SetIntValue(val);
3191     }
3192     ++seg_idx;
3193     if (seg_idx >= segs.size())
3194       seg_idx = 0;
3195 
3196     values->push_back(v);
3197     token = tokenizer_->NextToken();
3198   }
3199   return {};
3200 }
3201 
ParseExpect()3202 Result Parser::ParseExpect() {
3203   auto token = tokenizer_->NextToken();
3204   if (!token->IsIdentifier())
3205     return Result("invalid buffer name in EXPECT command");
3206 
3207   if (token->AsString() == "IDX")
3208     return Result("missing buffer name between EXPECT and IDX");
3209   if (token->AsString() == "EQ_BUFFER")
3210     return Result("missing buffer name between EXPECT and EQ_BUFFER");
3211   if (token->AsString() == "RMSE_BUFFER")
3212     return Result("missing buffer name between EXPECT and RMSE_BUFFER");
3213   if (token->AsString() == "EQ_HISTOGRAM_EMD_BUFFER") {
3214     return Result(
3215         "missing buffer name between EXPECT and EQ_HISTOGRAM_EMD_BUFFER");
3216   }
3217 
3218   size_t line = tokenizer_->GetCurrentLine();
3219   auto* buffer = script_->GetBuffer(token->AsString());
3220   if (!buffer)
3221     return Result("unknown buffer name for EXPECT command: " +
3222                   token->AsString());
3223 
3224   token = tokenizer_->NextToken();
3225 
3226   if (!token->IsIdentifier())
3227     return Result("invalid comparator in EXPECT command");
3228 
3229   if (token->AsString() == "EQ_BUFFER" || token->AsString() == "RMSE_BUFFER" ||
3230       token->AsString() == "EQ_HISTOGRAM_EMD_BUFFER") {
3231     auto type = token->AsString();
3232 
3233     token = tokenizer_->NextToken();
3234     if (!token->IsIdentifier())
3235       return Result("invalid buffer name in EXPECT " + type + " command");
3236 
3237     auto* buffer_2 = script_->GetBuffer(token->AsString());
3238     if (!buffer_2) {
3239       return Result("unknown buffer name for EXPECT " + type +
3240                     " command: " + token->AsString());
3241     }
3242 
3243     if (!buffer->GetFormat()->Equal(buffer_2->GetFormat())) {
3244       return Result("EXPECT " + type +
3245                     " command cannot compare buffers of differing format");
3246     }
3247     if (buffer->ElementCount() != buffer_2->ElementCount()) {
3248       return Result("EXPECT " + type +
3249                     " command cannot compare buffers of different size: " +
3250                     std::to_string(buffer->ElementCount()) + " vs " +
3251                     std::to_string(buffer_2->ElementCount()));
3252     }
3253     if (buffer->GetWidth() != buffer_2->GetWidth()) {
3254       return Result("EXPECT " + type +
3255                     " command cannot compare buffers of different width");
3256     }
3257     if (buffer->GetHeight() != buffer_2->GetHeight()) {
3258       return Result("EXPECT " + type +
3259                     " command cannot compare buffers of different height");
3260     }
3261 
3262     auto cmd = MakeUnique<CompareBufferCommand>(buffer, buffer_2);
3263     if (type == "RMSE_BUFFER") {
3264       cmd->SetComparator(CompareBufferCommand::Comparator::kRmse);
3265 
3266       token = tokenizer_->NextToken();
3267       if (!token->IsIdentifier() && token->AsString() == "TOLERANCE")
3268         return Result("missing TOLERANCE for EXPECT RMSE_BUFFER");
3269 
3270       token = tokenizer_->NextToken();
3271       if (!token->IsInteger() && !token->IsDouble())
3272         return Result("invalid TOLERANCE for EXPECT RMSE_BUFFER");
3273 
3274       Result r = token->ConvertToDouble();
3275       if (!r.IsSuccess())
3276         return r;
3277 
3278       cmd->SetTolerance(token->AsFloat());
3279     } else if (type == "EQ_HISTOGRAM_EMD_BUFFER") {
3280       cmd->SetComparator(CompareBufferCommand::Comparator::kHistogramEmd);
3281 
3282       token = tokenizer_->NextToken();
3283       if (!token->IsIdentifier() && token->AsString() == "TOLERANCE")
3284         return Result("missing TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
3285 
3286       token = tokenizer_->NextToken();
3287       if (!token->IsInteger() && !token->IsDouble())
3288         return Result("invalid TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
3289 
3290       Result r = token->ConvertToDouble();
3291       if (!r.IsSuccess())
3292         return r;
3293 
3294       cmd->SetTolerance(token->AsFloat());
3295     }
3296 
3297     command_list_.push_back(std::move(cmd));
3298 
3299     // Early return
3300     return ValidateEndOfStatement("EXPECT " + type + " command");
3301   }
3302 
3303   if (token->AsString() != "IDX")
3304     return Result("missing IDX in EXPECT command");
3305 
3306   token = tokenizer_->NextToken();
3307   if (!token->IsInteger() || token->AsInt32() < 0)
3308     return Result("invalid X value in EXPECT command");
3309   token->ConvertToDouble();
3310   float x = token->AsFloat();
3311 
3312   bool has_y_val = false;
3313   float y = 0;
3314   token = tokenizer_->NextToken();
3315   if (token->IsInteger()) {
3316     has_y_val = true;
3317 
3318     if (token->AsInt32() < 0)
3319       return Result("invalid Y value in EXPECT command");
3320     token->ConvertToDouble();
3321     y = token->AsFloat();
3322 
3323     token = tokenizer_->NextToken();
3324   }
3325 
3326   if (token->IsIdentifier() && token->AsString() == "SIZE") {
3327     if (!has_y_val)
3328       return Result("invalid Y value in EXPECT command");
3329 
3330     auto probe = MakeUnique<ProbeCommand>(buffer);
3331     probe->SetLine(line);
3332     probe->SetX(x);
3333     probe->SetY(y);
3334     probe->SetProbeRect();
3335 
3336     token = tokenizer_->NextToken();
3337     if (!token->IsInteger() || token->AsInt32() <= 0)
3338       return Result("invalid width in EXPECT command");
3339     token->ConvertToDouble();
3340     probe->SetWidth(token->AsFloat());
3341 
3342     token = tokenizer_->NextToken();
3343     if (!token->IsInteger() || token->AsInt32() <= 0)
3344       return Result("invalid height in EXPECT command");
3345     token->ConvertToDouble();
3346     probe->SetHeight(token->AsFloat());
3347 
3348     token = tokenizer_->NextToken();
3349     if (!token->IsIdentifier()) {
3350       return Result("invalid token in EXPECT command:" +
3351                     token->ToOriginalString());
3352     }
3353 
3354     if (token->AsString() == "EQ_RGBA") {
3355       probe->SetIsRGBA();
3356     } else if (token->AsString() != "EQ_RGB") {
3357       return Result("unknown comparator type in EXPECT: " +
3358                     token->ToOriginalString());
3359     }
3360 
3361     token = tokenizer_->NextToken();
3362     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3363       return Result("invalid R value in EXPECT command");
3364     token->ConvertToDouble();
3365     probe->SetR(token->AsFloat() / 255.f);
3366 
3367     token = tokenizer_->NextToken();
3368     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3369       return Result("invalid G value in EXPECT command");
3370     token->ConvertToDouble();
3371     probe->SetG(token->AsFloat() / 255.f);
3372 
3373     token = tokenizer_->NextToken();
3374     if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3375       return Result("invalid B value in EXPECT command");
3376     token->ConvertToDouble();
3377     probe->SetB(token->AsFloat() / 255.f);
3378 
3379     if (probe->IsRGBA()) {
3380       token = tokenizer_->NextToken();
3381       if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255)
3382         return Result("invalid A value in EXPECT command");
3383       token->ConvertToDouble();
3384       probe->SetA(token->AsFloat() / 255.f);
3385     }
3386 
3387     token = tokenizer_->NextToken();
3388     if (token->IsIdentifier() && token->AsString() == "TOLERANCE") {
3389       std::vector<Probe::Tolerance> tolerances;
3390 
3391       Result r = ParseTolerances(&tolerances);
3392 
3393       if (!r.IsSuccess())
3394         return r;
3395 
3396       if (tolerances.empty())
3397         return Result("TOLERANCE specified but no tolerances provided");
3398 
3399       if (!probe->IsRGBA() && tolerances.size() > 3) {
3400         return Result(
3401             "TOLERANCE for an RGB comparison has a maximum of 3 values");
3402       }
3403 
3404       if (tolerances.size() > 4) {
3405         return Result(
3406             "TOLERANCE for an RGBA comparison has a maximum of 4 values");
3407       }
3408 
3409       probe->SetTolerances(std::move(tolerances));
3410       token = tokenizer_->NextToken();
3411     }
3412 
3413     if (!token->IsEOL() && !token->IsEOS()) {
3414       return Result("extra parameters after EXPECT command: " +
3415                     token->ToOriginalString());
3416     }
3417 
3418     command_list_.push_back(std::move(probe));
3419 
3420     return {};
3421   }
3422 
3423   auto probe = MakeUnique<ProbeSSBOCommand>(buffer);
3424   probe->SetLine(line);
3425 
3426   if (token->IsIdentifier() && token->AsString() == "TOLERANCE") {
3427     std::vector<Probe::Tolerance> tolerances;
3428 
3429     Result r = ParseTolerances(&tolerances);
3430 
3431     if (!r.IsSuccess())
3432       return r;
3433 
3434     if (tolerances.empty())
3435       return Result("TOLERANCE specified but no tolerances provided");
3436     if (tolerances.size() > 4)
3437       return Result("TOLERANCE has a maximum of 4 values");
3438 
3439     probe->SetTolerances(std::move(tolerances));
3440     token = tokenizer_->NextToken();
3441   }
3442 
3443   if (!token->IsIdentifier() || !IsComparator(token->AsString())) {
3444     return Result("unexpected token in EXPECT command: " +
3445                   token->ToOriginalString());
3446   }
3447 
3448   if (has_y_val)
3449     return Result("Y value not needed for non-color comparator");
3450 
3451   auto cmp = ToComparator(token->AsString());
3452   if (probe->HasTolerances()) {
3453     if (cmp != ProbeSSBOCommand::Comparator::kEqual)
3454       return Result("TOLERANCE only available with EQ probes");
3455 
3456     cmp = ProbeSSBOCommand::Comparator::kFuzzyEqual;
3457   }
3458 
3459   probe->SetComparator(cmp);
3460   probe->SetFormat(buffer->GetFormat());
3461   probe->SetOffset(static_cast<uint32_t>(x));
3462 
3463   std::vector<Value> values;
3464   Result r = ParseValues("EXPECT", buffer->GetFormat(), &values);
3465   if (!r.IsSuccess())
3466     return r;
3467 
3468   if (values.empty())
3469     return Result("missing comparison values for EXPECT command");
3470 
3471   probe->SetValues(std::move(values));
3472   command_list_.push_back(std::move(probe));
3473 
3474   return {};
3475 }
3476 
ParseCopy()3477 Result Parser::ParseCopy() {
3478   auto token = tokenizer_->NextToken();
3479   if (token->IsEOL() || token->IsEOS())
3480     return Result("missing buffer name after COPY");
3481   if (!token->IsIdentifier())
3482     return Result("invalid buffer name after COPY");
3483 
3484   size_t line = tokenizer_->GetCurrentLine();
3485 
3486   auto name = token->AsString();
3487   if (name == "TO")
3488     return Result("missing buffer name between COPY and TO");
3489 
3490   Buffer* buffer_from = script_->GetBuffer(name);
3491   if (!buffer_from)
3492     return Result("COPY origin buffer was not declared");
3493 
3494   token = tokenizer_->NextToken();
3495   if (token->IsEOL() || token->IsEOS())
3496     return Result("missing 'TO' after COPY and buffer name");
3497   if (!token->IsIdentifier())
3498     return Result("expected 'TO' after COPY and buffer name");
3499 
3500   name = token->AsString();
3501   if (name != "TO")
3502     return Result("expected 'TO' after COPY and buffer name");
3503 
3504   token = tokenizer_->NextToken();
3505   if (token->IsEOL() || token->IsEOS())
3506     return Result("missing buffer name after TO");
3507   if (!token->IsIdentifier())
3508     return Result("invalid buffer name after TO");
3509 
3510   name = token->AsString();
3511   Buffer* buffer_to = script_->GetBuffer(name);
3512   if (!buffer_to)
3513     return Result("COPY destination buffer was not declared");
3514 
3515   // Set destination buffer to mirror origin buffer
3516   buffer_to->SetWidth(buffer_from->GetWidth());
3517   buffer_to->SetHeight(buffer_from->GetHeight());
3518   buffer_to->SetElementCount(buffer_from->ElementCount());
3519 
3520   if (buffer_from == buffer_to)
3521     return Result("COPY origin and destination buffers are identical");
3522 
3523   auto cmd = MakeUnique<CopyCommand>(buffer_from, buffer_to);
3524   cmd->SetLine(line);
3525   command_list_.push_back(std::move(cmd));
3526 
3527   return ValidateEndOfStatement("COPY command");
3528 }
3529 
ParseClearColor()3530 Result Parser::ParseClearColor() {
3531   auto token = tokenizer_->NextToken();
3532   if (!token->IsIdentifier())
3533     return Result("missing pipeline name for CLEAR_COLOR command");
3534 
3535   size_t line = tokenizer_->GetCurrentLine();
3536 
3537   auto* pipeline = script_->GetPipeline(token->AsString());
3538   if (!pipeline) {
3539     return Result("unknown pipeline for CLEAR_COLOR command: " +
3540                   token->AsString());
3541   }
3542   if (!pipeline->IsGraphics()) {
3543     return Result("CLEAR_COLOR command requires graphics pipeline");
3544   }
3545 
3546   auto cmd = MakeUnique<ClearColorCommand>(pipeline);
3547   cmd->SetLine(line);
3548 
3549   token = tokenizer_->NextToken();
3550   if (token->IsEOL() || token->IsEOS())
3551     return Result("missing R value for CLEAR_COLOR command");
3552   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3553     return Result("invalid R value for CLEAR_COLOR command: " +
3554                   token->ToOriginalString());
3555   }
3556   token->ConvertToDouble();
3557   cmd->SetR(token->AsFloat() / 255.f);
3558 
3559   token = tokenizer_->NextToken();
3560   if (token->IsEOL() || token->IsEOS())
3561     return Result("missing G value for CLEAR_COLOR command");
3562   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3563     return Result("invalid G value for CLEAR_COLOR command: " +
3564                   token->ToOriginalString());
3565   }
3566   token->ConvertToDouble();
3567   cmd->SetG(token->AsFloat() / 255.f);
3568 
3569   token = tokenizer_->NextToken();
3570   if (token->IsEOL() || token->IsEOS())
3571     return Result("missing B value for CLEAR_COLOR command");
3572   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3573     return Result("invalid B value for CLEAR_COLOR command: " +
3574                   token->ToOriginalString());
3575   }
3576   token->ConvertToDouble();
3577   cmd->SetB(token->AsFloat() / 255.f);
3578 
3579   token = tokenizer_->NextToken();
3580   if (token->IsEOL() || token->IsEOS())
3581     return Result("missing A value for CLEAR_COLOR command");
3582   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3583     return Result("invalid A value for CLEAR_COLOR command: " +
3584                   token->ToOriginalString());
3585   }
3586   token->ConvertToDouble();
3587   cmd->SetA(token->AsFloat() / 255.f);
3588 
3589   command_list_.push_back(std::move(cmd));
3590   return ValidateEndOfStatement("CLEAR_COLOR command");
3591 }
3592 
ParseClearDepth()3593 Result Parser::ParseClearDepth() {
3594   auto token = tokenizer_->NextToken();
3595   if (!token->IsIdentifier())
3596     return Result("missing pipeline name for CLEAR_DEPTH command");
3597 
3598   size_t line = tokenizer_->GetCurrentLine();
3599 
3600   auto* pipeline = script_->GetPipeline(token->AsString());
3601   if (!pipeline) {
3602     return Result("unknown pipeline for CLEAR_DEPTH command: " +
3603                   token->AsString());
3604   }
3605   if (!pipeline->IsGraphics()) {
3606     return Result("CLEAR_DEPTH command requires graphics pipeline");
3607   }
3608 
3609   auto cmd = MakeUnique<ClearDepthCommand>(pipeline);
3610   cmd->SetLine(line);
3611 
3612   token = tokenizer_->NextToken();
3613   if (token->IsEOL() || token->IsEOS())
3614     return Result("missing value for CLEAR_DEPTH command");
3615   if (!token->IsDouble()) {
3616     return Result("invalid value for CLEAR_DEPTH command: " +
3617                   token->ToOriginalString());
3618   }
3619   cmd->SetValue(token->AsFloat());
3620 
3621   command_list_.push_back(std::move(cmd));
3622   return ValidateEndOfStatement("CLEAR_DEPTH command");
3623 }
3624 
ParseClearStencil()3625 Result Parser::ParseClearStencil() {
3626   auto token = tokenizer_->NextToken();
3627   if (!token->IsIdentifier())
3628     return Result("missing pipeline name for CLEAR_STENCIL command");
3629 
3630   size_t line = tokenizer_->GetCurrentLine();
3631 
3632   auto* pipeline = script_->GetPipeline(token->AsString());
3633   if (!pipeline) {
3634     return Result("unknown pipeline for CLEAR_STENCIL command: " +
3635                   token->AsString());
3636   }
3637   if (!pipeline->IsGraphics()) {
3638     return Result("CLEAR_STENCIL command requires graphics pipeline");
3639   }
3640 
3641   auto cmd = MakeUnique<ClearStencilCommand>(pipeline);
3642   cmd->SetLine(line);
3643 
3644   token = tokenizer_->NextToken();
3645   if (token->IsEOL() || token->IsEOS())
3646     return Result("missing value for CLEAR_STENCIL command");
3647   if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
3648     return Result("invalid value for CLEAR_STENCIL command: " +
3649                   token->ToOriginalString());
3650   }
3651   cmd->SetValue(token->AsUint32());
3652 
3653   command_list_.push_back(std::move(cmd));
3654   return ValidateEndOfStatement("CLEAR_STENCIL command");
3655 }
3656 
ParseDeviceFeature()3657 Result Parser::ParseDeviceFeature() {
3658   auto token = tokenizer_->NextToken();
3659   if (token->IsEOS() || token->IsEOL())
3660     return Result("missing feature name for DEVICE_FEATURE command");
3661   if (!token->IsIdentifier())
3662     return Result("invalid feature name for DEVICE_FEATURE command");
3663   if (!script_->IsKnownFeature(token->AsString()))
3664     return Result("unknown feature name for DEVICE_FEATURE command");
3665 
3666   script_->AddRequiredFeature(token->AsString());
3667 
3668   return ValidateEndOfStatement("DEVICE_FEATURE command");
3669 }
3670 
ParseDeviceProperty()3671 Result Parser::ParseDeviceProperty() {
3672   auto token = tokenizer_->NextToken();
3673   if (token->IsEOS() || token->IsEOL())
3674     return Result("missing property name for DEVICE_PROPERTY command");
3675   if (!token->IsIdentifier())
3676     return Result("invalid property name for DEVICE_PROPERTY command");
3677   if (!script_->IsKnownProperty(token->AsString()))
3678     return Result("unknown property name for DEVICE_PROPERTY command");
3679 
3680   script_->AddRequiredProperty(token->AsString());
3681 
3682   return ValidateEndOfStatement("DEVICE_PROPERTY command");
3683 }
3684 
ParseRepeat()3685 Result Parser::ParseRepeat() {
3686   auto token = tokenizer_->NextToken();
3687   if (token->IsEOL() || token->IsEOL())
3688     return Result("missing count parameter for REPEAT command");
3689   if (!token->IsInteger()) {
3690     return Result("invalid count parameter for REPEAT command: " +
3691                   token->ToOriginalString());
3692   }
3693   if (token->AsInt32() <= 0)
3694     return Result("count parameter must be > 0 for REPEAT command");
3695 
3696   uint32_t count = token->AsUint32();
3697 
3698   std::vector<std::unique_ptr<Command>> cur_commands;
3699   std::swap(cur_commands, command_list_);
3700 
3701   for (token = tokenizer_->NextToken(); !token->IsEOS();
3702        token = tokenizer_->NextToken()) {
3703     if (token->IsEOL())
3704       continue;
3705     if (!token->IsIdentifier())
3706       return Result("expected identifier");
3707 
3708     std::string tok = token->AsString();
3709     if (tok == "END")
3710       break;
3711     if (!IsRepeatable(tok))
3712       return Result("unknown token: " + tok);
3713 
3714     Result r = ParseRepeatableCommand(tok);
3715     if (!r.IsSuccess())
3716       return r;
3717   }
3718   if (!token->IsIdentifier() || token->AsString() != "END")
3719     return Result("missing END for REPEAT command");
3720 
3721   auto cmd = MakeUnique<RepeatCommand>(count);
3722   cmd->SetCommands(std::move(command_list_));
3723 
3724   std::swap(cur_commands, command_list_);
3725   command_list_.push_back(std::move(cmd));
3726 
3727   return ValidateEndOfStatement("REPEAT command");
3728 }
3729 
ParseDerivePipelineBlock()3730 Result Parser::ParseDerivePipelineBlock() {
3731   auto token = tokenizer_->NextToken();
3732   if (!token->IsIdentifier() || token->AsString() == "FROM")
3733     return Result("missing pipeline name for DERIVE_PIPELINE command");
3734 
3735   std::string name = token->AsString();
3736   if (script_->GetPipeline(name) != nullptr)
3737     return Result("duplicate pipeline name for DERIVE_PIPELINE command");
3738 
3739   token = tokenizer_->NextToken();
3740   if (!token->IsIdentifier() || token->AsString() != "FROM")
3741     return Result("missing FROM in DERIVE_PIPELINE command");
3742 
3743   token = tokenizer_->NextToken();
3744   if (!token->IsIdentifier())
3745     return Result("missing parent pipeline name in DERIVE_PIPELINE command");
3746 
3747   Pipeline* parent = script_->GetPipeline(token->AsString());
3748   if (!parent)
3749     return Result("unknown parent pipeline in DERIVE_PIPELINE command");
3750 
3751   Result r = ValidateEndOfStatement("DERIVE_PIPELINE command");
3752   if (!r.IsSuccess())
3753     return r;
3754 
3755   auto pipeline = parent->Clone();
3756   pipeline->SetName(name);
3757 
3758   return ParsePipelineBody("DERIVE_PIPELINE", std::move(pipeline));
3759 }
3760 
ParseDeviceExtension()3761 Result Parser::ParseDeviceExtension() {
3762   auto token = tokenizer_->NextToken();
3763   if (token->IsEOL() || token->IsEOS())
3764     return Result("DEVICE_EXTENSION missing name");
3765   if (!token->IsIdentifier()) {
3766     return Result("DEVICE_EXTENSION invalid name: " +
3767                   token->ToOriginalString());
3768   }
3769 
3770   script_->AddRequiredDeviceExtension(token->AsString());
3771 
3772   return ValidateEndOfStatement("DEVICE_EXTENSION command");
3773 }
3774 
ParseInstanceExtension()3775 Result Parser::ParseInstanceExtension() {
3776   auto token = tokenizer_->NextToken();
3777   if (token->IsEOL() || token->IsEOS())
3778     return Result("INSTANCE_EXTENSION missing name");
3779   if (!token->IsIdentifier()) {
3780     return Result("INSTANCE_EXTENSION invalid name: " +
3781                   token->ToOriginalString());
3782   }
3783 
3784   script_->AddRequiredInstanceExtension(token->AsString());
3785 
3786   return ValidateEndOfStatement("INSTANCE_EXTENSION command");
3787 }
3788 
ParseSet()3789 Result Parser::ParseSet() {
3790   auto token = tokenizer_->NextToken();
3791   if (!token->IsIdentifier() || token->AsString() != "ENGINE_DATA")
3792     return Result("SET missing ENGINE_DATA");
3793 
3794   token = tokenizer_->NextToken();
3795   if (token->IsEOS() || token->IsEOL())
3796     return Result("SET missing variable to be set");
3797 
3798   if (!token->IsIdentifier())
3799     return Result("SET invalid variable to set: " + token->ToOriginalString());
3800 
3801   if (token->AsString() != "fence_timeout_ms")
3802     return Result("SET unknown variable provided: " + token->AsString());
3803 
3804   token = tokenizer_->NextToken();
3805   if (token->IsEOS() || token->IsEOL())
3806     return Result("SET missing value for fence_timeout_ms");
3807   if (!token->IsInteger())
3808     return Result("SET invalid value for fence_timeout_ms, must be uint32");
3809 
3810   script_->GetEngineData().fence_timeout_ms = token->AsUint32();
3811 
3812   return ValidateEndOfStatement("SET command");
3813 }
3814 
ParseSampler()3815 Result Parser::ParseSampler() {
3816   auto token = tokenizer_->NextToken();
3817   if (!token->IsIdentifier())
3818     return Result("invalid token when looking for sampler name");
3819 
3820   auto sampler = MakeUnique<Sampler>();
3821   sampler->SetName(token->AsString());
3822 
3823   token = tokenizer_->NextToken();
3824   while (!token->IsEOS() && !token->IsEOL()) {
3825     if (!token->IsIdentifier())
3826       return Result("invalid token when looking for sampler parameters");
3827 
3828     auto param = token->AsString();
3829     if (param == "MAG_FILTER") {
3830       token = tokenizer_->NextToken();
3831 
3832       if (!token->IsIdentifier())
3833         return Result("invalid token when looking for MAG_FILTER value");
3834 
3835       auto filter = token->AsString();
3836 
3837       if (filter == "linear")
3838         sampler->SetMagFilter(FilterType::kLinear);
3839       else if (filter == "nearest")
3840         sampler->SetMagFilter(FilterType::kNearest);
3841       else
3842         return Result("invalid MAG_FILTER value " + filter);
3843     } else if (param == "MIN_FILTER") {
3844       token = tokenizer_->NextToken();
3845 
3846       if (!token->IsIdentifier())
3847         return Result("invalid token when looking for MIN_FILTER value");
3848 
3849       auto filter = token->AsString();
3850 
3851       if (filter == "linear")
3852         sampler->SetMinFilter(FilterType::kLinear);
3853       else if (filter == "nearest")
3854         sampler->SetMinFilter(FilterType::kNearest);
3855       else
3856         return Result("invalid MIN_FILTER value " + filter);
3857     } else if (param == "ADDRESS_MODE_U") {
3858       token = tokenizer_->NextToken();
3859 
3860       if (!token->IsIdentifier())
3861         return Result("invalid token when looking for ADDRESS_MODE_U value");
3862 
3863       auto mode_str = token->AsString();
3864       auto mode = StrToAddressMode(mode_str);
3865 
3866       if (mode == AddressMode::kUnknown)
3867         return Result("invalid ADDRESS_MODE_U value " + mode_str);
3868 
3869       sampler->SetAddressModeU(mode);
3870     } else if (param == "ADDRESS_MODE_V") {
3871       token = tokenizer_->NextToken();
3872 
3873       if (!token->IsIdentifier())
3874         return Result("invalid token when looking for ADDRESS_MODE_V value");
3875 
3876       auto mode_str = token->AsString();
3877       auto mode = StrToAddressMode(mode_str);
3878 
3879       if (mode == AddressMode::kUnknown)
3880         return Result("invalid ADDRESS_MODE_V value " + mode_str);
3881 
3882       sampler->SetAddressModeV(mode);
3883     } else if (param == "ADDRESS_MODE_W") {
3884       token = tokenizer_->NextToken();
3885 
3886       if (!token->IsIdentifier())
3887         return Result("invalid token when looking for ADDRESS_MODE_W value");
3888 
3889       auto mode_str = token->AsString();
3890       auto mode = StrToAddressMode(mode_str);
3891 
3892       if (mode == AddressMode::kUnknown)
3893         return Result("invalid ADDRESS_MODE_W value " + mode_str);
3894 
3895       sampler->SetAddressModeW(mode);
3896     } else if (param == "BORDER_COLOR") {
3897       token = tokenizer_->NextToken();
3898 
3899       if (!token->IsIdentifier())
3900         return Result("invalid token when looking for BORDER_COLOR value");
3901 
3902       auto color_str = token->AsString();
3903 
3904       if (color_str == "float_transparent_black")
3905         sampler->SetBorderColor(BorderColor::kFloatTransparentBlack);
3906       else if (color_str == "int_transparent_black")
3907         sampler->SetBorderColor(BorderColor::kIntTransparentBlack);
3908       else if (color_str == "float_opaque_black")
3909         sampler->SetBorderColor(BorderColor::kFloatOpaqueBlack);
3910       else if (color_str == "int_opaque_black")
3911         sampler->SetBorderColor(BorderColor::kIntOpaqueBlack);
3912       else if (color_str == "float_opaque_white")
3913         sampler->SetBorderColor(BorderColor::kFloatOpaqueWhite);
3914       else if (color_str == "int_opaque_white")
3915         sampler->SetBorderColor(BorderColor::kIntOpaqueWhite);
3916       else
3917         return Result("invalid BORDER_COLOR value " + color_str);
3918     } else if (param == "MIN_LOD") {
3919       token = tokenizer_->NextToken();
3920 
3921       if (!token->IsDouble())
3922         return Result("invalid token when looking for MIN_LOD value");
3923 
3924       sampler->SetMinLOD(token->AsFloat());
3925     } else if (param == "MAX_LOD") {
3926       token = tokenizer_->NextToken();
3927 
3928       if (!token->IsDouble())
3929         return Result("invalid token when looking for MAX_LOD value");
3930 
3931       sampler->SetMaxLOD(token->AsFloat());
3932     } else if (param == "NORMALIZED_COORDS") {
3933       sampler->SetNormalizedCoords(true);
3934     } else if (param == "UNNORMALIZED_COORDS") {
3935       sampler->SetNormalizedCoords(false);
3936       sampler->SetMinLOD(0.0f);
3937       sampler->SetMaxLOD(0.0f);
3938     } else if (param == "COMPARE") {
3939       token = tokenizer_->NextToken();
3940 
3941       if (!token->IsIdentifier())
3942         return Result("invalid value for COMPARE");
3943 
3944       if (token->AsString() == "on")
3945         sampler->SetCompareEnable(true);
3946       else if (token->AsString() == "off")
3947         sampler->SetCompareEnable(false);
3948       else
3949         return Result("invalid value for COMPARE: " + token->AsString());
3950     } else if (param == "COMPARE_OP") {
3951       token = tokenizer_->NextToken();
3952 
3953       if (!token->IsIdentifier())
3954         return Result("invalid value for COMPARE_OP");
3955 
3956       CompareOp compare_op = StrToCompareOp(token->AsString());
3957       if (compare_op != CompareOp::kUnknown) {
3958         sampler->SetCompareOp(compare_op);
3959       } else {
3960         return Result("invalid value for COMPARE_OP: " + token->AsString());
3961       }
3962     } else {
3963       return Result("unexpected sampler parameter " + param);
3964     }
3965 
3966     token = tokenizer_->NextToken();
3967   }
3968 
3969   if (sampler->GetMaxLOD() < sampler->GetMinLOD()) {
3970     return Result("max LOD needs to be greater than or equal to min LOD");
3971   }
3972 
3973   return script_->AddSampler(std::move(sampler));
3974 }
3975 
IsRayTracingShader(ShaderType type)3976 bool Parser::IsRayTracingShader(ShaderType type) {
3977   return type == kShaderTypeRayGeneration || type == kShaderTypeAnyHit ||
3978          type == kShaderTypeClosestHit || type == kShaderTypeMiss ||
3979          type == kShaderTypeIntersection || type == kShaderTypeCall;
3980 }
3981 
ParseAS()3982 Result Parser::ParseAS() {
3983   auto token = tokenizer_->NextToken();
3984   if (!token->IsIdentifier())
3985     return Result("Acceleration structure requires TOP_LEVEL or BOTTOM_LEVEL");
3986 
3987   Result r;
3988   auto type = token->AsString();
3989   if (type == "BOTTOM_LEVEL")
3990     r = ParseBLAS();
3991   else if (type == "TOP_LEVEL")
3992     r = ParseTLAS();
3993   else
3994     return Result("Unexpected acceleration structure type");
3995 
3996   return r;
3997 }
3998 
ParseBLAS()3999 Result Parser::ParseBLAS() {
4000   auto token = tokenizer_->NextToken();
4001   if (!token->IsIdentifier())
4002     return Result("Bottom level acceleration structure requires a name");
4003 
4004   auto name = token->AsString();
4005   if (script_->GetBLAS(name) != nullptr)
4006     return Result(
4007         "Bottom level acceleration structure with this name already defined");
4008 
4009   std::unique_ptr<BLAS> blas = MakeUnique<BLAS>();
4010   blas->SetName(name);
4011 
4012   token = tokenizer_->NextToken();
4013   if (!token->IsEOL())
4014     return Result("New line expected");
4015 
4016   Result r;
4017   while (true) {
4018     token = tokenizer_->NextToken();
4019     if (token->IsEOL()) {
4020       continue;
4021     }
4022     if (token->IsEOS()) {
4023       return Result("END command missing");
4024     }
4025     if (!token->IsIdentifier()) {
4026       return Result("Identifier expected");
4027     }
4028 
4029     auto geom = token->AsString();
4030     if (geom == "END") {
4031       break;
4032     } else if (geom == "GEOMETRY") {
4033       token = tokenizer_->NextToken();
4034       if (!token->IsIdentifier()) {
4035         return Result("Identifier expected");
4036       }
4037 
4038       auto type = token->AsString();
4039       if (type == "TRIANGLES") {
4040         r = ParseBLASTriangle(blas.get());
4041       } else if (type == "AABBS") {
4042         r = ParseBLASAABB(blas.get());
4043       } else {
4044         return Result("Unexpected geometry type");
4045       }
4046     } else {
4047       return Result("Unexpected identifier");
4048     }
4049 
4050     if (!r.IsSuccess()) {
4051       return r;
4052     }
4053   }
4054 
4055   if (blas->GetGeometrySize() > 0) {
4056     auto type = blas->GetGeometries()[0]->GetType();
4057     auto& geometries = blas->GetGeometries();
4058     for (auto& g : geometries)
4059       if (g->GetType() != type)
4060         return Result("Only one type of geometry is allowed within a BLAS");
4061   }
4062 
4063   return script_->AddBLAS(std::move(blas));
4064 }
4065 
ParseBLASTriangle(BLAS * blas)4066 Result Parser::ParseBLASTriangle(BLAS* blas) {
4067   std::unique_ptr<Geometry> geometry = MakeUnique<Geometry>();
4068   std::vector<float> g;
4069   uint32_t flags = 0;
4070   geometry->SetType(GeometryType::kTriangle);
4071 
4072   while (true) {
4073     auto token = tokenizer_->NextToken();
4074 
4075     if (token->IsEOS())
4076       return Result("END expected");
4077     if (token->IsEOL())
4078       continue;
4079 
4080     if (token->IsIdentifier()) {
4081       std::string tok = token->AsString();
4082       if (tok == "END") {
4083         break;
4084       } else if (tok == "FLAGS") {
4085         Result r = ParseGeometryFlags(&flags);
4086         if (!r.IsSuccess())
4087           return r;
4088       } else {
4089         return Result("END or float value is expected");
4090       }
4091     } else if (token->IsInteger() || token->IsDouble()) {
4092       g.push_back(token->AsFloat());
4093     } else {
4094       return Result("Unexpected data type");
4095     }
4096   }
4097 
4098   if (g.empty())
4099     return Result("No triangles have been specified.");
4100 
4101   if (g.size() % 3 != 0)
4102     return Result("Each vertex consists of three float coordinates.");
4103 
4104   if ((g.size() / 3) % 3 != 0)
4105     return Result("Each triangle should include three vertices.");
4106 
4107   geometry->SetData(g);
4108   geometry->SetFlags(flags);
4109 
4110   blas->AddGeometry(&geometry);
4111 
4112   return {};
4113 }
4114 
ParseBLASAABB(BLAS * blas)4115 Result Parser::ParseBLASAABB(BLAS* blas) {
4116   std::unique_ptr<Geometry> geometry = MakeUnique<Geometry>();
4117   std::vector<float> g;
4118   uint32_t flags = 0;
4119   geometry->SetType(GeometryType::kAABB);
4120 
4121   while (true) {
4122     auto token = tokenizer_->NextToken();
4123 
4124     if (token->IsEOS())
4125       return Result("END expected");
4126     if (token->IsEOL())
4127       continue;
4128 
4129     if (token->IsIdentifier()) {
4130       std::string tok = token->AsString();
4131       if (tok == "END") {
4132         break;
4133       } else if (tok == "FLAGS") {
4134         Result r = ParseGeometryFlags(&flags);
4135         if (!r.IsSuccess())
4136           return r;
4137       } else {
4138         return Result("END or float value is expected");
4139       }
4140     } else if (token->IsDouble()) {
4141       g.push_back(token->AsFloat());
4142     } else if (token->IsInteger()) {
4143       g.push_back(static_cast<float>(token->AsInt64()));
4144     } else {
4145       return Result("Unexpected data type");
4146     }
4147   }
4148 
4149   if (g.empty())
4150     return Result("No AABBs have been specified.");
4151 
4152   if ((g.size() % 6) != 0)
4153     return Result(
4154         "Each vertex consists of three float coordinates. Each AABB should "
4155         "include two vertices.");
4156 
4157   geometry->SetData(g);
4158   geometry->SetFlags(flags);
4159 
4160   blas->AddGeometry(&geometry);
4161 
4162   return {};
4163 }
4164 
ParseGeometryFlags(uint32_t * flags)4165 Result Parser::ParseGeometryFlags(uint32_t* flags) {
4166   std::unique_ptr<Token> token;
4167   bool first_eol = true;
4168   bool singleline = true;
4169   Result r;
4170 
4171   while (true) {
4172     token = tokenizer_->NextToken();
4173     if (token->IsEOL()) {
4174       if (first_eol) {
4175         first_eol = false;
4176         singleline = (*flags != 0);
4177       }
4178       if (singleline)
4179         break;
4180       else
4181         continue;
4182     }
4183     if (token->IsEOS())
4184       return Result("END command missing");
4185 
4186     if (token->IsIdentifier()) {
4187       if (token->AsString() == "END")
4188         break;
4189       else if (token->AsString() == "OPAQUE")
4190         *flags |= VK_GEOMETRY_OPAQUE_BIT_KHR;
4191       else if (token->AsString() == "NO_DUPLICATE_ANY_HIT")
4192         *flags |= VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR;
4193       else
4194         return Result("Unknown flag: " + token->AsString());
4195     } else {
4196       r = Result("Identifier expected");
4197     }
4198 
4199     if (!r.IsSuccess())
4200       return r;
4201   }
4202 
4203   return {};
4204 }
4205 
ParseTLAS()4206 Result Parser::ParseTLAS() {
4207   auto token = tokenizer_->NextToken();
4208   if (!token->IsIdentifier())
4209     return Result("invalid TLAS name provided");
4210 
4211   auto name = token->AsString();
4212 
4213   token = tokenizer_->NextToken();
4214   if (!token->IsEOL())
4215     return Result("New line expected");
4216 
4217   std::unique_ptr<TLAS> tlas = MakeUnique<TLAS>();
4218 
4219   tlas->SetName(name);
4220 
4221   while (true) {
4222     token = tokenizer_->NextToken();
4223     if (token->IsEOL())
4224       continue;
4225     if (token->IsEOS())
4226       return Result("END command missing");
4227     if (!token->IsIdentifier())
4228       return Result("expected identifier");
4229 
4230     Result r;
4231     std::string tok = token->AsString();
4232     if (tok == "END")
4233       break;
4234     if (tok == "BOTTOM_LEVEL_INSTANCE")
4235       r = ParseBLASInstance(tlas.get());
4236     else
4237       r = Result("unknown token: " + tok);
4238 
4239     if (!r.IsSuccess())
4240       return r;
4241   }
4242 
4243   Result r = script_->AddTLAS(std::move(tlas));
4244   if (!r.IsSuccess())
4245     return r;
4246 
4247   return {};
4248 }
4249 
4250 // BOTTOM_LEVEL_INSTANCE <blas_name> [MASK 0-255] [OFFSET 0-16777215] [INDEX
4251 // 0-16777215] [FLAGS {flags}] [TRANSFORM {float x 12} END]
ParseBLASInstance(TLAS * tlas)4252 Result Parser::ParseBLASInstance(TLAS* tlas) {
4253   std::unique_ptr<Token> token;
4254   std::unique_ptr<BLASInstance> instance = MakeUnique<BLASInstance>();
4255 
4256   token = tokenizer_->NextToken();
4257 
4258   if (!token->IsIdentifier())
4259     return Result("Bottom level acceleration structure name expected");
4260 
4261   std::string name = token->AsString();
4262   auto ptr = script_->GetBLAS(name);
4263 
4264   if (!ptr)
4265     return Result(
4266         "Bottom level acceleration structure with given name not found");
4267 
4268   instance->SetUsedBLAS(name, ptr);
4269 
4270   while (true) {
4271     token = tokenizer_->NextToken();
4272     if (token->IsEOS())
4273       return Result("Unexpected end");
4274     if (token->IsEOL())
4275       continue;
4276 
4277     if (!token->IsIdentifier())
4278       return Result("expected identifier");
4279 
4280     Result r;
4281     std::string tok = token->AsString();
4282     if (tok == "END") {
4283       break;
4284     } else if (tok == "TRANSFORM") {
4285       r = ParseBLASInstanceTransform(instance.get());
4286     } else if (tok == "FLAGS") {
4287       r = ParseBLASInstanceFlags(instance.get());
4288     } else if (tok == "MASK") {
4289       token = tokenizer_->NextToken();
4290       uint64_t v;
4291 
4292       if (token->IsInteger())
4293         v = token->AsUint64();
4294       else if (token->IsHex())
4295         v = token->AsHex();
4296       else
4297         return Result("Integer or hex value expected");
4298 
4299       instance->SetMask(uint32_t(v));
4300     } else if (tok == "OFFSET") {
4301       token = tokenizer_->NextToken();
4302       uint64_t v;
4303 
4304       if (token->IsInteger())
4305         v = token->AsUint64();
4306       else if (token->IsHex())
4307         v = token->AsHex();
4308       else
4309         return Result("Integer or hex value expected");
4310 
4311       instance->SetOffset(uint32_t(v));
4312     } else if (tok == "INDEX") {
4313       token = tokenizer_->NextToken();
4314       uint64_t v;
4315 
4316       if (token->IsInteger())
4317         v = token->AsUint64();
4318       else if (token->IsHex())
4319         v = token->AsHex();
4320       else
4321         return Result("Integer or hex value expected");
4322 
4323       instance->SetInstanceIndex(uint32_t(v));
4324     } else {
4325       r = Result("Unknown token in BOTTOM_LEVEL_INSTANCE block: " + tok);
4326     }
4327 
4328     if (!r.IsSuccess())
4329       return r;
4330   }
4331 
4332   tlas->AddInstance(std::move(instance));
4333 
4334   return {};
4335 }
4336 
ParseBLASInstanceTransform(BLASInstance * instance)4337 Result Parser::ParseBLASInstanceTransform(BLASInstance* instance) {
4338   std::unique_ptr<Token> token;
4339   std::vector<float> transform;
4340 
4341   transform.reserve(12);
4342 
4343   while (true) {
4344     token = tokenizer_->NextToken();
4345     if (token->IsEOL())
4346       continue;
4347     if (token->IsEOS())
4348       return Result("END command missing");
4349 
4350     if (token->IsIdentifier() && token->AsString() == "END")
4351       break;
4352     else if (token->IsDouble() || token->IsInteger())
4353       transform.push_back(token->AsFloat());
4354     else
4355       return Result("Unknown token: " + token->AsString());
4356   }
4357 
4358   if (transform.size() != 12)
4359     return Result("Transform matrix expected to have 12 numbers");
4360 
4361   instance->SetTransform(transform);
4362 
4363   return {};
4364 }
4365 
ParseBLASInstanceFlags(BLASInstance * instance)4366 Result Parser::ParseBLASInstanceFlags(BLASInstance* instance) {
4367   std::unique_ptr<Token> token;
4368   uint32_t flags = 0;
4369   bool first_eol = true;
4370   bool singleline = true;
4371   Result r;
4372 
4373   while (true) {
4374     token = tokenizer_->NextToken();
4375     if (token->IsEOL()) {
4376       if (first_eol) {
4377         first_eol = false;
4378         singleline = (flags != 0);
4379       }
4380       if (singleline)
4381         break;
4382       else
4383         continue;
4384     }
4385     if (token->IsEOS())
4386       return Result("END command missing");
4387 
4388     if (token->IsInteger()) {
4389       flags |= token->AsUint32();
4390     } else if (token->IsHex()) {
4391       flags |= uint32_t(token->AsHex());
4392     } else if (token->IsIdentifier()) {
4393       if (token->AsString() == "END")
4394         break;
4395       else if (token->AsString() == "TRIANGLE_FACING_CULL_DISABLE")
4396         flags |= VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
4397       else if (token->AsString() == "TRIANGLE_FLIP_FACING")
4398         flags |= VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR;
4399       else if (token->AsString() == "FORCE_OPAQUE")
4400         flags |= VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR;
4401       else if (token->AsString() == "FORCE_NO_OPAQUE")
4402         flags |= VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR;
4403       else if (token->AsString() == "FORCE_OPACITY_MICROMAP_2_STATE")
4404         flags |= VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_EXT;
4405       else if (token->AsString() == "DISABLE_OPACITY_MICROMAPS")
4406         flags |= VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_EXT;
4407       else
4408         return Result("Unknown flag: " + token->AsString());
4409     } else {
4410       r = Result("Identifier expected");
4411     }
4412 
4413     if (!r.IsSuccess())
4414       return r;
4415   }
4416 
4417   if (r.IsSuccess())
4418     instance->SetFlags(flags);
4419 
4420   return {};
4421 }
4422 
ParseSBT(Pipeline * pipeline)4423 Result Parser::ParseSBT(Pipeline* pipeline) {
4424   auto token = tokenizer_->NextToken();
4425   if (!token->IsIdentifier())
4426     return Result("SHADER_BINDINGS_TABLE requires a name");
4427 
4428   auto name = token->AsString();
4429   if (pipeline->GetSBT(name) != nullptr)
4430     return Result("SHADER_BINDINGS_TABLE with this name already defined");
4431 
4432   std::unique_ptr<SBT> sbt = MakeUnique<SBT>();
4433   sbt->SetName(name);
4434 
4435   token = tokenizer_->NextToken();
4436   if (!token->IsEOL())
4437     return Result("New line expected");
4438 
4439   while (true) {
4440     token = tokenizer_->NextToken();
4441     if (token->IsEOL()) {
4442       continue;
4443     }
4444     if (token->IsEOS()) {
4445       return Result("END command missing");
4446     }
4447     if (!token->IsIdentifier()) {
4448       return Result("Identifier expected");
4449     }
4450 
4451     auto tok = token->AsString();
4452     if (tok == "END") {
4453       break;
4454     }
4455 
4456     uint32_t index = 0;
4457     ShaderGroup* shader_group = script_->FindShaderGroup(pipeline, tok, &index);
4458 
4459     if (shader_group == nullptr)
4460       return Result(
4461           "Shader group not found neither in pipeline, nor in libraries");
4462 
4463     std::unique_ptr<SBTRecord> sbtrecord = MakeUnique<SBTRecord>();
4464 
4465     sbtrecord->SetUsedShaderGroupName(tok);
4466     sbtrecord->SetIndex(index);
4467     sbtrecord->SetCount(1);
4468 
4469     sbt->AddSBTRecord(std::move(sbtrecord));
4470   }
4471 
4472   return pipeline->AddSBT(std::move(sbt));
4473 }
4474 
ParseMaxRayPayloadSize(Pipeline * pipeline)4475 Result Parser::ParseMaxRayPayloadSize(Pipeline* pipeline) {
4476   if (!pipeline->IsRayTracing())
4477     return Result(
4478         "Ray payload size parameter is allowed only for ray tracing pipeline");
4479 
4480   auto token = tokenizer_->NextToken();
4481   if (!token->IsInteger())
4482     return Result("Ray payload size expects an integer");
4483 
4484   pipeline->SetMaxPipelineRayPayloadSize(token->AsUint32());
4485 
4486   return {};
4487 }
4488 
ParseMaxRayHitAttributeSize(Pipeline * pipeline)4489 Result Parser::ParseMaxRayHitAttributeSize(Pipeline* pipeline) {
4490   if (!pipeline->IsRayTracing())
4491     return Result(
4492         "Ray hit attribute size is allowed only for ray tracing pipeline");
4493 
4494   auto token = tokenizer_->NextToken();
4495   if (!token->IsInteger())
4496     return Result("Ray hit attribute size expects an integer");
4497 
4498   pipeline->SetMaxPipelineRayHitAttributeSize(token->AsUint32());
4499 
4500   return {};
4501 }
4502 
ParseMaxRayRecursionDepth(Pipeline * pipeline)4503 Result Parser::ParseMaxRayRecursionDepth(Pipeline* pipeline) {
4504   if (!pipeline->IsRayTracing())
4505     return Result(
4506         "Ray recursion depth is allowed only for ray tracing pipeline");
4507 
4508   auto token = tokenizer_->NextToken();
4509   if (!token->IsInteger())
4510     return Result("Ray recursion depth expects an integer");
4511 
4512   pipeline->SetMaxPipelineRayRecursionDepth(token->AsUint32());
4513 
4514   return {};
4515 }
4516 
ParseFlags(Pipeline * pipeline)4517 Result Parser::ParseFlags(Pipeline* pipeline) {
4518   if (!pipeline->IsRayTracing())
4519     return Result("Flags are allowed only for ray tracing pipeline");
4520 
4521   std::unique_ptr<Token> token;
4522   uint32_t flags = pipeline->GetCreateFlags();
4523   bool first_eol = true;
4524   bool singleline = true;
4525   Result r;
4526 
4527   while (true) {
4528     token = tokenizer_->NextToken();
4529     if (token->IsEOL()) {
4530       if (first_eol) {
4531         first_eol = false;
4532         singleline = (flags != 0);
4533       }
4534       if (singleline)
4535         break;
4536       else
4537         continue;
4538     }
4539     if (token->IsEOS())
4540       return Result("END command missing");
4541 
4542     if (token->IsInteger()) {
4543       flags |= token->AsUint32();
4544     } else if (token->IsHex()) {
4545       flags |= uint32_t(token->AsHex());
4546     } else if (token->IsIdentifier()) {
4547       if (token->AsString() == "END")
4548         break;
4549       else if (token->AsString() == "LIBRARY")
4550         flags |= VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
4551       else
4552         return Result("Unknown flag: " + token->AsString());
4553     } else {
4554       r = Result("Identifier expected");
4555     }
4556 
4557     if (!r.IsSuccess())
4558       return r;
4559   }
4560 
4561   if (r.IsSuccess())
4562     pipeline->SetCreateFlags(flags);
4563 
4564   return {};
4565 }
4566 
ParseUseLibrary(Pipeline * pipeline)4567 Result Parser::ParseUseLibrary(Pipeline* pipeline) {
4568   if (!pipeline->IsRayTracing())
4569     return Result("Use library is allowed only for ray tracing pipeline");
4570 
4571   while (true) {
4572     auto token = tokenizer_->NextToken();
4573 
4574     if (token->IsEOS())
4575       return Result("EOL expected");
4576     if (token->IsEOL())
4577       break;
4578 
4579     if (token->IsIdentifier()) {
4580       std::string tok = token->AsString();
4581 
4582       Pipeline* use_pipeline = script_->GetPipeline(tok);
4583       if (!use_pipeline)
4584         return Result("Pipeline not found: " + tok);
4585 
4586       pipeline->AddPipelineLibrary(use_pipeline);
4587     } else {
4588       return Result("Unexpected data type");
4589     }
4590   }
4591 
4592   return {};
4593 }
4594 
ParseTolerances(std::vector<Probe::Tolerance> * tolerances)4595 Result Parser::ParseTolerances(std::vector<Probe::Tolerance>* tolerances) {
4596   auto token = tokenizer_->PeekNextToken();
4597   while (!token->IsEOL() && !token->IsEOS()) {
4598     if (!token->IsInteger() && !token->IsDouble())
4599       break;
4600 
4601     token = tokenizer_->NextToken();
4602     Result r = token->ConvertToDouble();
4603     if (!r.IsSuccess())
4604       return r;
4605 
4606     double value = token->AsDouble();
4607     token = tokenizer_->PeekNextToken();
4608     if (token->IsIdentifier() && token->AsString() == "%") {
4609       tolerances->push_back(Probe::Tolerance{true, value});
4610       tokenizer_->NextToken();
4611       token = tokenizer_->PeekNextToken();
4612     } else {
4613       tolerances->push_back(Probe::Tolerance{false, value});
4614     }
4615   }
4616 
4617   return {};
4618 }
4619 
ParseVirtualFile()4620 Result Parser::ParseVirtualFile() {
4621   auto token = tokenizer_->NextToken();
4622   if (!token->IsIdentifier() && !token->IsString())
4623     return Result("invalid virtual file path");
4624 
4625   auto path = token->AsString();
4626 
4627   auto r = ValidateEndOfStatement("VIRTUAL_FILE command");
4628   if (!r.IsSuccess())
4629     return r;
4630 
4631   auto data = tokenizer_->ExtractToNext("END");
4632 
4633   token = tokenizer_->NextToken();
4634   if (!token->IsIdentifier() || token->AsString() != "END")
4635     return Result("VIRTUAL_FILE missing END command");
4636 
4637   return script_->AddVirtualFile(path, data);
4638 }
4639 
4640 }  // namespace amberscript
4641 }  // namespace amber
4642