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,PipelineWithUnknownShader)23 TEST_F(AmberScriptParserTest, PipelineWithUnknownShader) {
24 std::string in = R"(
25 PIPELINE graphics my_pipeline
26 ATTACH my_shader
27 END)";
28
29 Parser parser;
30 Result r = parser.Parse(in);
31 ASSERT_FALSE(r.IsSuccess());
32 EXPECT_EQ("3: unknown shader in ATTACH command", r.Error());
33 }
34
TEST_F(AmberScriptParserTest,DuplicateShadersInAPipeline)35 TEST_F(AmberScriptParserTest, DuplicateShadersInAPipeline) {
36 std::string in = R"(
37 SHADER vertex my_shader PASSTHROUGH
38 PIPELINE graphics my_pipeline
39 ATTACH my_shader
40 ATTACH my_shader
41 END)";
42
43 Parser parser;
44 Result r = parser.Parse(in);
45 ASSERT_FALSE(r.IsSuccess());
46 EXPECT_EQ("6: can not add duplicate shader to pipeline", r.Error());
47 }
48
TEST_F(AmberScriptParserTest,AttachInvalidToken)49 TEST_F(AmberScriptParserTest, AttachInvalidToken) {
50 std::string in = R"(PIPELINE graphics my_pipeline
51 ATTACH 1234
52 END)";
53
54 Parser parser;
55 Result r = parser.Parse(in);
56 ASSERT_FALSE(r.IsSuccess());
57 EXPECT_EQ("2: invalid token in ATTACH command", r.Error());
58 }
59
TEST_F(AmberScriptParserTest,AttachExtraParameter)60 TEST_F(AmberScriptParserTest, AttachExtraParameter) {
61 std::string in = R"(
62 SHADER vertex my_shader PASSTHROUGH
63 PIPELINE graphics my_pipeline
64 ATTACH my_shader INVALID
65 END)";
66
67 Parser parser;
68 Result r = parser.Parse(in);
69 ASSERT_FALSE(r.IsSuccess());
70 EXPECT_EQ("4: unknown ATTACH parameter: INVALID", r.Error());
71 }
72
TEST_F(AmberScriptParserTest,AttachMissingValue)73 TEST_F(AmberScriptParserTest, AttachMissingValue) {
74 std::string in = R"(
75 SHADER vertex my_shader PASSTHROUGH
76 PIPELINE graphics my_pipeline
77 ATTACH
78 END)";
79
80 Parser parser;
81 Result r = parser.Parse(in);
82 ASSERT_FALSE(r.IsSuccess());
83 EXPECT_EQ("5: invalid token in ATTACH command", r.Error());
84 }
85
TEST_F(AmberScriptParserTest,ComputeShaderInGraphicsPipeline)86 TEST_F(AmberScriptParserTest, ComputeShaderInGraphicsPipeline) {
87 std::string in = R"(SHADER compute my_shader GLSL
88 void main() {
89 gl_FragColor = vec3(2, 3, 4);
90 }
91 END
92
93 PIPELINE graphics my_pipeline
94 ATTACH my_shader
95 END)";
96
97 Parser parser;
98 Result r = parser.Parse(in);
99 ASSERT_FALSE(r.IsSuccess()) << r.Error();
100 EXPECT_EQ("9: can not add a compute shader to a graphics pipeline",
101 r.Error());
102 }
103
104 struct ShaderTypeData {
105 const char* name;
106 ShaderType type;
107 };
108
109 using AmberScriptParserPipelineAttachTest =
110 testing::TestWithParam<ShaderTypeData>;
TEST_P(AmberScriptParserPipelineAttachTest,GraphicsShaderInComputePipeline)111 TEST_P(AmberScriptParserPipelineAttachTest, GraphicsShaderInComputePipeline) {
112 auto test_data = GetParam();
113
114 std::string in = "SHADER " + std::string(test_data.name) + R"( my_shader GLSL
115 void main() {
116 gl_FragColor = vec3(2, 3, 4);
117 }
118 END
119
120 PIPELINE compute my_pipeline
121 ATTACH my_shader
122 END)";
123
124 Parser parser;
125 Result r = parser.Parse(in);
126 ASSERT_FALSE(r.IsSuccess()) << r.Error();
127 EXPECT_EQ("9: only compute shaders allowed in a compute pipeline", r.Error());
128 }
129 INSTANTIATE_TEST_SUITE_P(
130 AmberScriptParserPipelineAttachTests,
131 AmberScriptParserPipelineAttachTest,
132 testing::Values(
133 ShaderTypeData{"vertex", kShaderTypeVertex},
134 ShaderTypeData{"fragment", kShaderTypeFragment},
135 ShaderTypeData{"geometry", kShaderTypeGeometry},
136 ShaderTypeData{"tessellation_evaluation",
137 kShaderTypeTessellationEvaluation},
138 ShaderTypeData{
139 "tessellation_control",
140 kShaderTypeTessellationControl})); // NOLINT(whitespace/parens)
141
TEST_F(AmberScriptParserTest,PipelineEntryPoint)142 TEST_F(AmberScriptParserTest, PipelineEntryPoint) {
143 std::string in = R"(
144 SHADER vertex my_shader PASSTHROUGH
145 SHADER fragment my_fragment GLSL
146 # GLSL Shader
147 END
148
149 PIPELINE graphics my_pipeline
150 ATTACH my_shader ENTRY_POINT green
151 ATTACH my_fragment
152 END
153 )";
154
155 Parser parser;
156 Result r = parser.Parse(in);
157 ASSERT_TRUE(r.IsSuccess()) << r.Error();
158
159 auto script = parser.GetScript();
160 const auto& pipelines = script->GetPipelines();
161 ASSERT_EQ(1U, pipelines.size());
162
163 const auto* pipeline = pipelines[0].get();
164 const auto& shaders = pipeline->GetShaders();
165 ASSERT_EQ(2U, shaders.size());
166
167 ASSERT_TRUE(shaders[0].GetShader() != nullptr);
168 EXPECT_EQ(kShaderTypeVertex, shaders[0].GetShader()->GetType());
169 EXPECT_EQ("green", shaders[0].GetEntryPoint());
170
171 ASSERT_TRUE(shaders[1].GetShader() != nullptr);
172 EXPECT_EQ(kShaderTypeFragment, shaders[1].GetShader()->GetType());
173 EXPECT_EQ("main", shaders[1].GetEntryPoint());
174 }
175
TEST_F(AmberScriptParserTest,PipelineEntryPointWithInvalidValue)176 TEST_F(AmberScriptParserTest, PipelineEntryPointWithInvalidValue) {
177 std::string in = R"(
178 SHADER compute my_compute GLSL
179 # Compute Shader
180 END
181 PIPELINE compute my_pipeline
182 ATTACH my_compute ENTRY_POINT 1234
183 END)";
184
185 Parser parser;
186 Result r = parser.Parse(in);
187 ASSERT_FALSE(r.IsSuccess());
188 EXPECT_EQ("6: missing shader name in ATTACH ENTRY_POINT command", r.Error());
189 }
190
TEST_F(AmberScriptParserTest,PipelineEntryPointMissingValue)191 TEST_F(AmberScriptParserTest, PipelineEntryPointMissingValue) {
192 std::string in = R"(
193 SHADER compute my_compute GLSL
194 # Compute Shader
195 END
196 PIPELINE compute my_pipeline
197 ATTACH my_compute ENTRY_POINT
198 END)";
199
200 Parser parser;
201 Result r = parser.Parse(in);
202 ASSERT_FALSE(r.IsSuccess());
203 EXPECT_EQ("7: missing shader name in ATTACH ENTRY_POINT command", r.Error());
204 }
205
TEST_F(AmberScriptParserTest,PipelineEntryPointExtraParameter)206 TEST_F(AmberScriptParserTest, PipelineEntryPointExtraParameter) {
207 std::string in = R"(
208 SHADER compute my_compute GLSL
209 # Compute Shader
210 END
211 PIPELINE compute my_pipeline
212 ATTACH my_compute ENTRY_POINT green INVALID
213 END)";
214
215 Parser parser;
216 Result r = parser.Parse(in);
217 ASSERT_FALSE(r.IsSuccess());
218 EXPECT_EQ("6: unknown ATTACH parameter: INVALID", r.Error());
219 }
220
TEST_F(AmberScriptParserTest,PiplineMultiShaderAttach)221 TEST_F(AmberScriptParserTest, PiplineMultiShaderAttach) {
222 std::string in = R"(
223 SHADER multi my_shader GLSL
224 # shaders
225 END
226 PIPELINE compute my_pipeline
227 ATTACH my_shader TYPE compute ENTRY_POINT my_entry_point
228 END)";
229
230 Parser parser;
231 Result r = parser.Parse(in);
232 ASSERT_TRUE(r.IsSuccess()) << r.Error();
233
234 auto script = parser.GetScript();
235 const auto& pipelines = script->GetPipelines();
236 ASSERT_EQ(1U, pipelines.size());
237
238 const auto* pipeline = pipelines[0].get();
239 const auto& shaders = pipeline->GetShaders();
240 ASSERT_EQ(1U, shaders.size());
241
242 ASSERT_TRUE(shaders[0].GetShader() != nullptr);
243 EXPECT_EQ(kShaderTypeMulti, shaders[0].GetShader()->GetType());
244 EXPECT_EQ(kShaderTypeCompute, shaders[0].GetShaderType());
245 EXPECT_EQ("my_entry_point", shaders[0].GetEntryPoint());
246 }
247
TEST_F(AmberScriptParserTest,PipelineMultiShaderMismatchPipelineAndShaderType)248 TEST_F(AmberScriptParserTest,
249 PipelineMultiShaderMismatchPipelineAndShaderType) {
250 std::string in = R"(
251 SHADER multi my_shader GLSL
252 # shaders
253 END
254 PIPELINE graphics my_pipeline
255 ATTACH my_shader TYPE compute ENTRY_POINT my_entry_point
256 END)";
257
258 Parser parser;
259 Result r = parser.Parse(in);
260 ASSERT_FALSE(r.IsSuccess());
261 EXPECT_EQ("6: can not add a compute shader to a graphics pipeline",
262 r.Error());
263 }
264
TEST_F(AmberScriptParserTest,PipelineMultiShaderMissingEntryPoint)265 TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingEntryPoint) {
266 std::string in = R"(
267 SHADER multi my_shader GLSL
268 # shaders
269 END
270 PIPELINE graphics my_pipeline
271 ATTACH my_shader TYPE fragment
272 END)";
273
274 Parser parser;
275 Result r = parser.Parse(in);
276 ASSERT_FALSE(r.IsSuccess());
277 EXPECT_EQ("7: ATTACH TYPE requires an ENTRY_POINT", r.Error());
278 }
279
TEST_F(AmberScriptParserTest,PipelineMultiShaderMissingType)280 TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingType) {
281 std::string in = R"(
282 SHADER multi my_shader GLSL
283 # shaders
284 END
285 PIPELINE graphics my_pipeline
286 ATTACH my_shader
287 END)";
288
289 Parser parser;
290 Result r = parser.Parse(in);
291 ASSERT_FALSE(r.IsSuccess());
292 EXPECT_EQ("7: multi shader ATTACH requires TYPE", r.Error());
293 }
294
TEST_F(AmberScriptParserTest,PipelineMultiShaderMissingTypeWithEntryPoint)295 TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingTypeWithEntryPoint) {
296 std::string in = R"(
297 SHADER multi my_shader GLSL
298 # shaders
299 END
300 PIPELINE graphics my_pipeline
301 ATTACH my_shader ENTRY_POINT my_ep
302 END)";
303
304 Parser parser;
305 Result r = parser.Parse(in);
306 ASSERT_FALSE(r.IsSuccess());
307 EXPECT_EQ("6: ATTACH missing TYPE for multi shader", r.Error());
308 }
309
TEST_F(AmberScriptParserTest,PipelineSpecializationUint32)310 TEST_F(AmberScriptParserTest, PipelineSpecializationUint32) {
311 std::string in = R"(
312 SHADER compute my_shader GLSL
313 #shaders
314 END
315 PIPELINE compute my_pipeline
316 ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 AS uint32 4
317 END)";
318
319 Parser parser;
320 Result r = parser.Parse(in);
321 EXPECT_EQ(r.Error(), "");
322 ASSERT_TRUE(r.IsSuccess());
323
324 auto script = parser.GetScript();
325 const auto& pipelines = script->GetPipelines();
326 ASSERT_EQ(1U, pipelines.size());
327
328 const auto* pipeline = pipelines[0].get();
329 const auto& shaders = pipeline->GetShaders();
330 ASSERT_EQ(1U, shaders.size());
331
332 EXPECT_EQ(1u, shaders[0].GetSpecialization().size());
333 EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1));
334 }
335
TEST_F(AmberScriptParserTest,PipelineSpecializationInt32)336 TEST_F(AmberScriptParserTest, PipelineSpecializationInt32) {
337 std::string in = R"(
338 SHADER compute my_shader GLSL
339 #shaders
340 END
341 PIPELINE compute my_pipeline
342 ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 2 AS int32 -1
343 END)";
344
345 Parser parser;
346 Result r = parser.Parse(in);
347 ASSERT_TRUE(r.IsSuccess());
348
349 auto script = parser.GetScript();
350 const auto& pipelines = script->GetPipelines();
351 ASSERT_EQ(1U, pipelines.size());
352
353 const auto* pipeline = pipelines[0].get();
354 const auto& shaders = pipeline->GetShaders();
355 ASSERT_EQ(1U, shaders.size());
356
357 EXPECT_EQ(1u, shaders[0].GetSpecialization().size());
358 EXPECT_EQ(0xffffffffu, shaders[0].GetSpecialization().at(2));
359 }
360
TEST_F(AmberScriptParserTest,PipelineSpecializationFloat)361 TEST_F(AmberScriptParserTest, PipelineSpecializationFloat) {
362 std::string in = R"(
363 SHADER compute my_shader GLSL
364 #shaders
365 END
366 PIPELINE compute my_pipeline
367 ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 3 AS float 1.1
368 END)";
369
370 Parser parser;
371 Result r = parser.Parse(in);
372 ASSERT_TRUE(r.IsSuccess());
373
374 auto script = parser.GetScript();
375 const auto& pipelines = script->GetPipelines();
376 ASSERT_EQ(1U, pipelines.size());
377
378 const auto* pipeline = pipelines[0].get();
379 const auto& shaders = pipeline->GetShaders();
380 ASSERT_EQ(1U, shaders.size());
381
382 EXPECT_EQ(1u, shaders[0].GetSpecialization().size());
383 EXPECT_EQ(0x3f8ccccdu, shaders[0].GetSpecialization().at(3));
384 }
385
TEST_F(AmberScriptParserTest,PipelineSpecializationIDIsString)386 TEST_F(AmberScriptParserTest, PipelineSpecializationIDIsString) {
387 std::string in = R"(
388 SHADER compute my_shader GLSL
389 #shaders
390 END
391 PIPELINE compute my_pipeline
392 ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE s3 AS float 1.1
393 END)";
394
395 Parser parser;
396 Result r = parser.Parse(in);
397 ASSERT_FALSE(r.IsSuccess());
398 EXPECT_EQ("6: specialization ID must be an integer", r.Error());
399 }
400
TEST_F(AmberScriptParserTest,PipelineSpecializationNoAS)401 TEST_F(AmberScriptParserTest, PipelineSpecializationNoAS) {
402 std::string in = R"(
403 SHADER compute my_shader GLSL
404 #shaders
405 END
406 PIPELINE compute my_pipeline
407 ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 ASa float 1.1
408 END)";
409
410 Parser parser;
411 Result r = parser.Parse(in);
412 ASSERT_FALSE(r.IsSuccess());
413 EXPECT_EQ("6: expected AS as next token", r.Error());
414 }
415
TEST_F(AmberScriptParserTest,PipelineSpecializationNotDataType)416 TEST_F(AmberScriptParserTest, PipelineSpecializationNotDataType) {
417 std::string in = R"(
418 SHADER compute my_shader GLSL
419 #shaders
420 END
421 PIPELINE compute my_pipeline
422 ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 AS uint 1.1
423 END)";
424
425 Parser parser;
426 Result r = parser.Parse(in);
427 ASSERT_FALSE(r.IsSuccess());
428 EXPECT_EQ("6: invalid data type 'uint' provided", r.Error());
429 }
430
TEST_F(AmberScriptParserTest,PipelineSpecializationBadDataType)431 TEST_F(AmberScriptParserTest, PipelineSpecializationBadDataType) {
432 std::string in = R"(
433 SHADER compute my_shader GLSL
434 #shaders
435 END
436 PIPELINE compute my_pipeline
437 ATTACH my_shader ENTRY_POINT my_ep SPECIALIZE 1 AS uint8 1.1
438 END)";
439
440 Parser parser;
441 Result r = parser.Parse(in);
442 ASSERT_FALSE(r.IsSuccess());
443 EXPECT_EQ(
444 "6: only 32-bit types are currently accepted for specialization values",
445 r.Error());
446 }
447
TEST_F(AmberScriptParserTest,PipelineSpecializationMultipleSpecializations)448 TEST_F(AmberScriptParserTest, PipelineSpecializationMultipleSpecializations) {
449 std::string in = R"(
450 SHADER compute my_shader GLSL
451 #shaders
452 END
453 PIPELINE compute my_pipeline
454 ATTACH my_shader TYPE compute ENTRY_POINT my_ep \
455 SPECIALIZE 1 AS uint32 4 \
456 SPECIALIZE 2 AS uint32 5 \
457 SPECIALIZE 5 AS uint32 1
458 END)";
459
460 Parser parser;
461 Result r = parser.Parse(in);
462 ASSERT_TRUE(r.IsSuccess());
463
464 auto script = parser.GetScript();
465 const auto& pipelines = script->GetPipelines();
466 ASSERT_EQ(1U, pipelines.size());
467
468 const auto* pipeline = pipelines[0].get();
469 const auto& shaders = pipeline->GetShaders();
470 ASSERT_EQ(1U, shaders.size());
471
472 EXPECT_EQ(3u, shaders[0].GetSpecialization().size());
473 EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1));
474 EXPECT_EQ(5u, shaders[0].GetSpecialization().at(2));
475 EXPECT_EQ(1u, shaders[0].GetSpecialization().at(5));
476 }
477
TEST_F(AmberScriptParserTest,PipelineSpecializationNoType)478 TEST_F(AmberScriptParserTest, PipelineSpecializationNoType) {
479 std::string in = R"(
480 SHADER compute my_shader GLSL
481 #shaders
482 END
483 PIPELINE compute my_pipeline
484 ATTACH my_shader SPECIALIZE 1 AS uint32 4
485 END)";
486
487 Parser parser;
488 Result r = parser.Parse(in);
489 ASSERT_TRUE(r.IsSuccess());
490
491 auto script = parser.GetScript();
492 const auto& pipelines = script->GetPipelines();
493 ASSERT_EQ(1U, pipelines.size());
494
495 const auto* pipeline = pipelines[0].get();
496 const auto& shaders = pipeline->GetShaders();
497 ASSERT_EQ(1U, shaders.size());
498
499 EXPECT_EQ(1u, shaders[0].GetSpecialization().size());
500 EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1));
501 }
502
503 } // namespace amberscript
504 } // namespace amber
505