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