1 // Copyright 2019 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 parseried.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "gtest/gtest.h"
16 #include "src/amberscript/parser.h"
17
18 namespace amber {
19 namespace amberscript {
20
21 using AmberScriptParserTest = testing::Test;
22
TEST_F(AmberScriptParserTest,Pipeline)23 TEST_F(AmberScriptParserTest, Pipeline) {
24 std::string in = R"(
25 SHADER vertex my_shader PASSTHROUGH
26 SHADER fragment my_fragment GLSL
27 # GLSL Shader
28 END
29
30 PIPELINE graphics my_pipeline
31 ATTACH my_shader
32 ATTACH my_fragment
33 END
34 )";
35
36 Parser parser;
37 Result r = parser.Parse(in);
38 ASSERT_TRUE(r.IsSuccess()) << r.Error();
39
40 auto script = parser.GetScript();
41 EXPECT_EQ(2U, script->GetShaders().size());
42
43 const auto& pipelines = script->GetPipelines();
44 ASSERT_EQ(1U, pipelines.size());
45
46 const auto* pipeline = pipelines[0].get();
47 EXPECT_EQ("my_pipeline", pipeline->GetName());
48 EXPECT_EQ(PipelineType::kGraphics, pipeline->GetType());
49
50 const auto& shaders = pipeline->GetShaders();
51 ASSERT_EQ(2U, shaders.size());
52
53 ASSERT_TRUE(shaders[0].GetShader() != nullptr);
54 EXPECT_EQ("my_shader", shaders[0].GetShader()->GetName());
55 EXPECT_EQ(kShaderTypeVertex, shaders[0].GetShader()->GetType());
56 EXPECT_EQ(static_cast<uint32_t>(0),
57 shaders[0].GetShaderOptimizations().size());
58
59 ASSERT_TRUE(shaders[1].GetShader() != nullptr);
60 EXPECT_EQ("my_fragment", shaders[1].GetShader()->GetName());
61 EXPECT_EQ(kShaderTypeFragment, shaders[1].GetShader()->GetType());
62 EXPECT_EQ(static_cast<uint32_t>(0),
63 shaders[1].GetShaderOptimizations().size());
64 }
65
TEST_F(AmberScriptParserTest,PipelineMissingEnd)66 TEST_F(AmberScriptParserTest, PipelineMissingEnd) {
67 std::string in = R"(
68 SHADER vertex my_shader PASSTHROUGH
69 PIPELINE graphics my_pipeline
70 ATTACH my_shader
71 )";
72
73 Parser parser;
74 Result r = parser.Parse(in);
75 ASSERT_FALSE(r.IsSuccess());
76 EXPECT_EQ("5: PIPELINE missing END command", r.Error());
77 }
78
TEST_F(AmberScriptParserTest,PipelineWithExtraParams)79 TEST_F(AmberScriptParserTest, PipelineWithExtraParams) {
80 std::string in = R"(
81 PIPELINE graphics my_pipeline INVALID
82 ATTACH my_shader
83 END
84 )";
85
86 Parser parser;
87 Result r = parser.Parse(in);
88 ASSERT_FALSE(r.IsSuccess());
89 EXPECT_EQ("2: extra parameters after PIPELINE command: INVALID", r.Error());
90 }
91
TEST_F(AmberScriptParserTest,PipelineInvalidType)92 TEST_F(AmberScriptParserTest, PipelineInvalidType) {
93 std::string in = "PIPELINE my_name\nEND";
94
95 Parser parser;
96 Result r = parser.Parse(in);
97 ASSERT_FALSE(r.IsSuccess());
98 EXPECT_EQ("1: unknown pipeline type: my_name", r.Error());
99 }
100
TEST_F(AmberScriptParserTest,PipelineMissingName)101 TEST_F(AmberScriptParserTest, PipelineMissingName) {
102 std::string in = "PIPELINE compute\nEND";
103
104 Parser parser;
105 Result r = parser.Parse(in);
106 ASSERT_FALSE(r.IsSuccess());
107 EXPECT_EQ("2: invalid token when looking for pipeline name", r.Error());
108 }
109
TEST_F(AmberScriptParserTest,PipelineWithInvalidTokenType)110 TEST_F(AmberScriptParserTest, PipelineWithInvalidTokenType) {
111 std::string in = "PIPELINE 123 my_pipeline\nEND";
112
113 Parser parser;
114 Result r = parser.Parse(in);
115 ASSERT_FALSE(r.IsSuccess());
116 EXPECT_EQ("1: invalid token when looking for pipeline type", r.Error());
117 }
118
TEST_F(AmberScriptParserTest,PipelineWithInvalidTokenName)119 TEST_F(AmberScriptParserTest, PipelineWithInvalidTokenName) {
120 std::string in = "PIPELINE compute 123\nEND";
121
122 Parser parser;
123 Result r = parser.Parse(in);
124 ASSERT_FALSE(r.IsSuccess());
125 EXPECT_EQ("1: invalid token when looking for pipeline name", r.Error());
126 }
127
TEST_F(AmberScriptParserTest,PipelineEmpty)128 TEST_F(AmberScriptParserTest, PipelineEmpty) {
129 std::string in = "PIPELINE compute my_pipeline\nEND";
130
131 Parser parser;
132 Result r = parser.Parse(in);
133 ASSERT_FALSE(r.IsSuccess());
134 EXPECT_EQ("compute pipeline requires a compute shader", r.Error());
135 }
136
TEST_F(AmberScriptParserTest,PipelineWithUnknownCommand)137 TEST_F(AmberScriptParserTest, PipelineWithUnknownCommand) {
138 std::string in = R"(
139 PIPELINE compute my_pipeline
140 SHADER vertex my_shader PASSTHROUGH
141 END)";
142
143 Parser parser;
144 Result r = parser.Parse(in);
145 ASSERT_FALSE(r.IsSuccess());
146 EXPECT_EQ("3: unknown token in pipeline block: SHADER", r.Error());
147 }
148
TEST_F(AmberScriptParserTest,DuplicatePipelineName)149 TEST_F(AmberScriptParserTest, DuplicatePipelineName) {
150 std::string in = R"(
151 SHADER vertex my_shader PASSTHROUGH
152 SHADER fragment my_fragment GLSL
153 # Fragment shader
154 END
155
156 PIPELINE graphics my_pipeline
157 ATTACH my_shader
158 ATTACH my_fragment
159 END
160 PIPELINE graphics my_pipeline
161 ATTACH my_shader
162 ATTACH my_fragment
163 END)";
164
165 Parser parser;
166 Result r = parser.Parse(in);
167 ASSERT_FALSE(r.IsSuccess());
168 EXPECT_EQ("14: duplicate pipeline name provided", r.Error());
169 }
170
TEST_F(AmberScriptParserTest,PipelineDefaultColorBuffer)171 TEST_F(AmberScriptParserTest, PipelineDefaultColorBuffer) {
172 std::string in = R"(
173 SHADER vertex my_shader PASSTHROUGH
174 SHADER fragment my_fragment GLSL
175 # GLSL Shader
176 END
177
178 PIPELINE graphics my_pipeline
179 ATTACH my_shader
180 ATTACH my_fragment
181 END
182 PIPELINE graphics my_pipeline2
183 ATTACH my_shader
184 ATTACH my_fragment
185 END)";
186
187 Parser parser;
188 Result r = parser.Parse(in);
189 ASSERT_TRUE(r.IsSuccess()) << r.Error();
190
191 auto script = parser.GetScript();
192 const auto& pipelines = script->GetPipelines();
193 ASSERT_EQ(2U, pipelines.size());
194
195 ASSERT_EQ(1U, pipelines[0]->GetColorAttachments().size());
196 const auto& buf1 = pipelines[0]->GetColorAttachments()[0];
197 ASSERT_TRUE(buf1.buffer != nullptr);
198
199 Buffer* buffer1 = buf1.buffer;
200 EXPECT_EQ(FormatType::kB8G8R8A8_UNORM, buffer1->GetFormat()->GetFormatType());
201 EXPECT_EQ(0u, buf1.location);
202 EXPECT_EQ(250u * 250u, buffer1->ElementCount());
203 EXPECT_EQ(250u * 250u * 4u, buffer1->ValueCount());
204 EXPECT_EQ(250u * 250u * 4u * sizeof(uint8_t), buffer1->GetSizeInBytes());
205
206 ASSERT_EQ(1U, pipelines[1]->GetColorAttachments().size());
207 const auto& buf2 = pipelines[1]->GetColorAttachments()[0];
208 ASSERT_TRUE(buf2.buffer != nullptr);
209 ASSERT_EQ(buffer1, buf2.buffer);
210 EXPECT_EQ(0u, buf2.location);
211 EXPECT_EQ(FormatType::kB8G8R8A8_UNORM,
212 buf2.buffer->GetFormat()->GetFormatType());
213 EXPECT_EQ(250u * 250u, buf2.buffer->ElementCount());
214 EXPECT_EQ(250u * 250u * 4u, buf2.buffer->ValueCount());
215 EXPECT_EQ(250u * 250u * 4u * sizeof(uint8_t), buf2.buffer->GetSizeInBytes());
216 }
217
TEST_F(AmberScriptParserTest,PipelineDefaultColorBufferMismatchSize)218 TEST_F(AmberScriptParserTest, PipelineDefaultColorBufferMismatchSize) {
219 std::string in = R"(
220 SHADER vertex my_shader PASSTHROUGH
221 SHADER fragment my_fragment GLSL
222 # GLSL Shader
223 END
224
225 PIPELINE graphics my_pipeline
226 ATTACH my_shader
227 ATTACH my_fragment
228 END
229 PIPELINE graphics my_pipeline2
230 ATTACH my_shader
231 ATTACH my_fragment
232 FRAMEBUFFER_SIZE 256 256
233 END)";
234
235 Parser parser;
236 Result r = parser.Parse(in);
237 ASSERT_FALSE(r.IsSuccess());
238
239 EXPECT_EQ("shared framebuffer must have same size over all PIPELINES",
240 r.Error());
241 }
242
TEST_F(AmberScriptParserTest,PipelinePolygonMode)243 TEST_F(AmberScriptParserTest, PipelinePolygonMode) {
244 std::string in = R"(
245 SHADER vertex my_shader PASSTHROUGH
246 SHADER fragment my_fragment GLSL
247 # GLSL Shader
248 END
249
250 PIPELINE graphics my_pipeline_default
251 ATTACH my_shader
252 ATTACH my_fragment
253 FRAMEBUFFER_SIZE 256 256
254 END
255 PIPELINE graphics my_pipeline_fill
256 ATTACH my_shader
257 ATTACH my_fragment
258 POLYGON_MODE fill
259 FRAMEBUFFER_SIZE 256 256
260 END
261 PIPELINE graphics my_pipeline_line
262 ATTACH my_shader
263 ATTACH my_fragment
264 POLYGON_MODE line
265 FRAMEBUFFER_SIZE 256 256
266 END
267 PIPELINE graphics my_pipeline_point
268 ATTACH my_shader
269 ATTACH my_fragment
270 POLYGON_MODE point
271 FRAMEBUFFER_SIZE 256 256
272 END)";
273
274 Parser parser;
275 Result r = parser.Parse(in);
276 ASSERT_TRUE(r.IsSuccess());
277
278 auto script = parser.GetScript();
279 const auto& pipelines = script->GetPipelines();
280 ASSERT_EQ(4U, pipelines.size());
281
282 auto mode0 = pipelines[0]->GetPipelineData()->GetPolygonMode();
283 ASSERT_EQ(mode0, PolygonMode::kFill);
284 auto mode1 = pipelines[1]->GetPipelineData()->GetPolygonMode();
285 ASSERT_EQ(mode1, PolygonMode::kFill);
286 auto mode2 = pipelines[2]->GetPipelineData()->GetPolygonMode();
287 ASSERT_EQ(mode2, PolygonMode::kLine);
288 auto mode3 = pipelines[3]->GetPipelineData()->GetPolygonMode();
289 ASSERT_EQ(mode3, PolygonMode::kPoint);
290 }
291
TEST_F(AmberScriptParserTest,PipelineMissingPolygonMode)292 TEST_F(AmberScriptParserTest, PipelineMissingPolygonMode) {
293 std::string in = R"(
294 SHADER vertex my_shader PASSTHROUGH
295 SHADER fragment my_fragment GLSL
296 # GLSL Shader
297 END
298
299 PIPELINE graphics my_pipeline
300 ATTACH my_shader
301 ATTACH my_fragment
302 POLYGON_MODE
303 FRAMEBUFFER_SIZE 256 256
304 END)";
305
306 Parser parser;
307 Result r = parser.Parse(in);
308 ASSERT_FALSE(r.IsSuccess());
309
310 EXPECT_EQ("11: missing mode in POLYGON_MODE command", r.Error());
311 }
312
TEST_F(AmberScriptParserTest,PipelineInvalidPolygonMode)313 TEST_F(AmberScriptParserTest, PipelineInvalidPolygonMode) {
314 std::string in = R"(
315 SHADER vertex my_shader PASSTHROUGH
316 SHADER fragment my_fragment GLSL
317 # GLSL Shader
318 END
319
320 PIPELINE graphics my_pipeline
321 ATTACH my_shader
322 ATTACH my_fragment
323 POLYGON_MODE foo
324 FRAMEBUFFER_SIZE 256 256
325 END)";
326
327 Parser parser;
328 Result r = parser.Parse(in);
329 ASSERT_FALSE(r.IsSuccess());
330
331 EXPECT_EQ("10: invalid polygon mode: foo", r.Error());
332 }
333
TEST_F(AmberScriptParserTest,DerivePipeline)334 TEST_F(AmberScriptParserTest, DerivePipeline) {
335 std::string in = R"(
336 SHADER vertex my_shader PASSTHROUGH
337 SHADER fragment my_fragment GLSL
338 # GLSL Shader
339 END
340 SHADER fragment other_fragment GLSL
341 # GLSL Shader
342 END
343 BUFFER buf1 DATA_TYPE int32 SIZE 20 FILL 5
344 BUFFER buf2 DATA_TYPE int32 SIZE 20 FILL 7
345
346 PIPELINE graphics parent_pipeline
347 ATTACH my_shader
348 ATTACH my_fragment
349 BIND BUFFER buf1 AS storage DESCRIPTOR_SET 1 BINDING 3
350 END
351
352 DERIVE_PIPELINE child_pipeline FROM parent_pipeline
353 ATTACH other_fragment
354 BIND BUFFER buf2 AS storage DESCRIPTOR_SET 1 BINDING 3
355 END
356 )";
357
358 Parser parser;
359 Result r = parser.Parse(in);
360 ASSERT_TRUE(r.IsSuccess()) << r.Error();
361
362 auto script = parser.GetScript();
363 const auto& pipelines = script->GetPipelines();
364 ASSERT_EQ(2U, pipelines.size());
365
366 const auto* pipeline1 = pipelines[0].get();
367 auto buffers1 = pipeline1->GetBuffers();
368 ASSERT_EQ(1U, buffers1.size());
369 EXPECT_EQ("buf1", buffers1[0].buffer->GetName());
370 EXPECT_EQ(1u, buffers1[0].descriptor_set);
371 EXPECT_EQ(3u, buffers1[0].binding);
372
373 auto shaders1 = pipeline1->GetShaders();
374 ASSERT_EQ(2U, shaders1.size());
375 EXPECT_EQ("my_shader", shaders1[0].GetShader()->GetName());
376 EXPECT_EQ("my_fragment", shaders1[1].GetShader()->GetName());
377
378 const auto* pipeline2 = pipelines[1].get();
379 EXPECT_EQ("child_pipeline", pipeline2->GetName());
380
381 auto buffers2 = pipeline2->GetBuffers();
382 ASSERT_EQ(1U, buffers2.size());
383 EXPECT_EQ("buf2", buffers2[0].buffer->GetName());
384 EXPECT_EQ(1u, buffers2[0].descriptor_set);
385 EXPECT_EQ(3u, buffers2[0].binding);
386
387 auto shaders2 = pipeline2->GetShaders();
388 ASSERT_EQ(2U, shaders2.size());
389 EXPECT_EQ("my_shader", shaders2[0].GetShader()->GetName());
390 EXPECT_EQ("other_fragment", shaders2[1].GetShader()->GetName());
391 }
392
TEST_F(AmberScriptParserTest,DerivePipelineMissingEnd)393 TEST_F(AmberScriptParserTest, DerivePipelineMissingEnd) {
394 std::string in = R"(
395 SHADER vertex my_shader PASSTHROUGH
396 SHADER fragment my_fragment GLSL
397 # GLSL Shader
398 END
399
400 PIPELINE graphics parent_pipeline
401 ATTACH my_shader
402 ATTACH my_fragment
403 END
404
405 DERIVE_PIPELINE derived_pipeline FROM parent_pipeline
406 )";
407
408 Parser parser;
409 Result r = parser.Parse(in);
410 ASSERT_FALSE(r.IsSuccess());
411 EXPECT_EQ("13: DERIVE_PIPELINE missing END command", r.Error());
412 }
413
TEST_F(AmberScriptParserTest,DerivePipelineMissingPipelineName)414 TEST_F(AmberScriptParserTest, DerivePipelineMissingPipelineName) {
415 std::string in = R"(
416 SHADER vertex my_shader PASSTHROUGH
417 SHADER fragment my_fragment GLSL
418 # GLSL Shader
419 END
420
421 PIPELINE graphics parent_pipeline
422 ATTACH my_shader
423 ATTACH my_fragment
424 END
425
426 DERIVE_PIPELINE FROM parent_pipeline
427 END
428 )";
429
430 Parser parser;
431 Result r = parser.Parse(in);
432 ASSERT_FALSE(r.IsSuccess());
433 EXPECT_EQ("12: missing pipeline name for DERIVE_PIPELINE command", r.Error());
434 }
435
TEST_F(AmberScriptParserTest,DerivePipelineMissingFrom)436 TEST_F(AmberScriptParserTest, DerivePipelineMissingFrom) {
437 std::string in = R"(
438 DERIVE_PIPELINE derived_pipeline parent_pipeline
439 END
440 )";
441
442 Parser parser;
443 Result r = parser.Parse(in);
444 ASSERT_FALSE(r.IsSuccess());
445 EXPECT_EQ("2: missing FROM in DERIVE_PIPELINE command", r.Error());
446 }
447
TEST_F(AmberScriptParserTest,DerivePipelineMissingParentPipelineName)448 TEST_F(AmberScriptParserTest, DerivePipelineMissingParentPipelineName) {
449 std::string in = R"(
450 DERIVE_PIPELINE derived_pipeline FROM
451 END
452 )";
453
454 Parser parser;
455 Result r = parser.Parse(in);
456 ASSERT_FALSE(r.IsSuccess());
457 EXPECT_EQ("3: missing parent pipeline name in DERIVE_PIPELINE command",
458 r.Error());
459 }
460
TEST_F(AmberScriptParserTest,DerivePipelineUnknownParentPipeline)461 TEST_F(AmberScriptParserTest, DerivePipelineUnknownParentPipeline) {
462 std::string in = R"(
463 DERIVE_PIPELINE derived_pipeline FROM parent_pipeline
464 END
465 )";
466
467 Parser parser;
468 Result r = parser.Parse(in);
469 ASSERT_FALSE(r.IsSuccess());
470 EXPECT_EQ("2: unknown parent pipeline in DERIVE_PIPELINE command", r.Error());
471 }
472
TEST_F(AmberScriptParserTest,DerivePipelineDuplicatePipelineName)473 TEST_F(AmberScriptParserTest, DerivePipelineDuplicatePipelineName) {
474 std::string in = R"(
475 SHADER vertex my_shader PASSTHROUGH
476 SHADER fragment my_fragment GLSL
477 # GLSL Shader
478 END
479
480 PIPELINE graphics parent_pipeline
481 ATTACH my_shader
482 ATTACH my_fragment
483 END
484
485 DERIVE_PIPELINE parent_pipeline FROM parent_pipeline
486 END
487 )";
488
489 Parser parser;
490 Result r = parser.Parse(in);
491 ASSERT_FALSE(r.IsSuccess());
492 EXPECT_EQ("12: duplicate pipeline name for DERIVE_PIPELINE command",
493 r.Error());
494 }
495
TEST_F(AmberScriptParserTest,DerivePipelineNoParams)496 TEST_F(AmberScriptParserTest, DerivePipelineNoParams) {
497 std::string in = R"(
498 DERIVE_PIPELINE
499 END
500 )";
501
502 Parser parser;
503 Result r = parser.Parse(in);
504 ASSERT_FALSE(r.IsSuccess());
505 EXPECT_EQ("3: missing pipeline name for DERIVE_PIPELINE command", r.Error());
506 }
507
TEST_F(AmberScriptParserTest,DerivePipelineSpecialized)508 TEST_F(AmberScriptParserTest, DerivePipelineSpecialized) {
509 std::string in = R"(
510 SHADER compute my_shader GLSL
511 #shaders
512 END
513 PIPELINE compute p1
514 ATTACH my_shader SPECIALIZE 3 AS uint32 4
515 END
516 DERIVE_PIPELINE p2 FROM p1
517 END
518 )";
519
520 Parser parser;
521 Result r = parser.Parse(in);
522 EXPECT_EQ("", r.Error());
523 ASSERT_TRUE(r.IsSuccess());
524
525 auto script = parser.GetScript();
526 const auto& pipelines = script->GetPipelines();
527 ASSERT_EQ(2U, pipelines.size());
528
529 const auto* p1 = pipelines[0].get();
530 const auto& s1 = p1->GetShaders();
531 ASSERT_EQ(1U, s1.size());
532
533 EXPECT_EQ(1u, s1[0].GetSpecialization().size());
534 EXPECT_EQ(4u, s1[0].GetSpecialization().at(3));
535
536 const auto* p2 = pipelines[1].get();
537 const auto& s2 = p2->GetShaders();
538 ASSERT_EQ(1U, s2.size());
539
540 EXPECT_EQ(1u, s2[0].GetSpecialization().size());
541 EXPECT_EQ(4u, s2[0].GetSpecialization().at(3));
542 }
543
544 } // namespace amberscript
545 } // namespace amber
546