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