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