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