| // Copyright 2019 The Amber Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "gtest/gtest.h" |
| #include "src/amberscript/parser.h" |
| |
| namespace amber { |
| namespace amberscript { |
| |
| using AmberScriptParserTest = testing::Test; |
| |
| TEST_F(AmberScriptParserTest, PipelineWithUnknownShader) { |
| std::string in = R"( |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("3: unknown shader in ATTACH command", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, DuplicateShadersInAPipeline) { |
| std::string in = R"( |
| SHADER vertex my_shader PASSTHROUGH |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader |
| ATTACH my_shader |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("6: can not add duplicate shader to pipeline", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, AttachInvalidToken) { |
| std::string in = R"(PIPELINE graphics my_pipeline |
| ATTACH 1234 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("2: invalid token in ATTACH command", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, AttachExtraParameter) { |
| std::string in = R"( |
| SHADER vertex my_shader PASSTHROUGH |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader INVALID |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("4: unknown ATTACH parameter: INVALID", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, AttachMissingValue) { |
| std::string in = R"( |
| SHADER vertex my_shader PASSTHROUGH |
| PIPELINE graphics my_pipeline |
| ATTACH |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("5: invalid token in ATTACH command", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, ComputeShaderInGraphicsPipeline) { |
| std::string in = R"(SHADER compute my_shader GLSL |
| void main() { |
| gl_FragColor = vec3(2, 3, 4); |
| } |
| END |
| |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()) << r.Error(); |
| EXPECT_EQ("9: can not add a compute shader to a graphics pipeline", |
| r.Error()); |
| } |
| |
| struct ShaderTypeData { |
| const char* name; |
| ShaderType type; |
| }; |
| |
| using AmberScriptParserPipelineAttachTest = |
| testing::TestWithParam<ShaderTypeData>; |
| TEST_P(AmberScriptParserPipelineAttachTest, GraphicsShaderInComputePipeline) { |
| auto test_data = GetParam(); |
| |
| std::string in = "SHADER " + std::string(test_data.name) + R"( my_shader GLSL |
| void main() { |
| gl_FragColor = vec3(2, 3, 4); |
| } |
| END |
| |
| PIPELINE compute my_pipeline |
| ATTACH my_shader |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()) << r.Error(); |
| EXPECT_EQ("9: only compute shaders allowed in a compute pipeline", r.Error()); |
| } |
| INSTANTIATE_TEST_SUITE_P( |
| AmberScriptParserPipelineAttachTests, |
| AmberScriptParserPipelineAttachTest, |
| testing::Values( |
| ShaderTypeData{"vertex", kShaderTypeVertex}, |
| ShaderTypeData{"fragment", kShaderTypeFragment}, |
| ShaderTypeData{"geometry", kShaderTypeGeometry}, |
| ShaderTypeData{"tessellation_evaluation", |
| kShaderTypeTessellationEvaluation}, |
| ShaderTypeData{ |
| "tessellation_control", |
| kShaderTypeTessellationControl})); // NOLINT(whitespace/parens) |
| |
| TEST_F(AmberScriptParserTest, PipelineEntryPoint) { |
| std::string in = R"( |
| SHADER vertex my_shader PASSTHROUGH |
| SHADER fragment my_fragment GLSL |
| # GLSL Shader |
| END |
| |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader ENTRY_POINT green |
| ATTACH my_fragment |
| END |
| )"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_TRUE(r.IsSuccess()) << r.Error(); |
| |
| auto script = parser.GetScript(); |
| const auto& pipelines = script->GetPipelines(); |
| ASSERT_EQ(1U, pipelines.size()); |
| |
| const auto* pipeline = pipelines[0].get(); |
| const auto& shaders = pipeline->GetShaders(); |
| ASSERT_EQ(2U, shaders.size()); |
| |
| ASSERT_TRUE(shaders[0].GetShader() != nullptr); |
| EXPECT_EQ(kShaderTypeVertex, shaders[0].GetShader()->GetType()); |
| EXPECT_EQ("green", shaders[0].GetEntryPoint()); |
| |
| ASSERT_TRUE(shaders[1].GetShader() != nullptr); |
| EXPECT_EQ(kShaderTypeFragment, shaders[1].GetShader()->GetType()); |
| EXPECT_EQ("main", shaders[1].GetEntryPoint()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineEntryPointWithInvalidValue) { |
| std::string in = R"( |
| SHADER compute my_compute GLSL |
| # Compute Shader |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_compute ENTRY_POINT 1234 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("6: missing shader name in ATTACH ENTRY_POINT command", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineEntryPointMissingValue) { |
| std::string in = R"( |
| SHADER compute my_compute GLSL |
| # Compute Shader |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_compute ENTRY_POINT |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("7: missing shader name in ATTACH ENTRY_POINT command", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineEntryPointExtraParameter) { |
| std::string in = R"( |
| SHADER compute my_compute GLSL |
| # Compute Shader |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_compute ENTRY_POINT green INVALID |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("6: unknown ATTACH parameter: INVALID", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PiplineMultiShaderAttach) { |
| std::string in = R"( |
| SHADER multi my_shader GLSL |
| # shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_entry_point |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_TRUE(r.IsSuccess()) << r.Error(); |
| |
| auto script = parser.GetScript(); |
| const auto& pipelines = script->GetPipelines(); |
| ASSERT_EQ(1U, pipelines.size()); |
| |
| const auto* pipeline = pipelines[0].get(); |
| const auto& shaders = pipeline->GetShaders(); |
| ASSERT_EQ(1U, shaders.size()); |
| |
| ASSERT_TRUE(shaders[0].GetShader() != nullptr); |
| EXPECT_EQ(kShaderTypeMulti, shaders[0].GetShader()->GetType()); |
| EXPECT_EQ(kShaderTypeCompute, shaders[0].GetShaderType()); |
| EXPECT_EQ("my_entry_point", shaders[0].GetEntryPoint()); |
| } |
| |
| TEST_F(AmberScriptParserTest, |
| PipelineMultiShaderMismatchPipelineAndShaderType) { |
| std::string in = R"( |
| SHADER multi my_shader GLSL |
| # shaders |
| END |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_entry_point |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("6: can not add a compute shader to a graphics pipeline", |
| r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingEntryPoint) { |
| std::string in = R"( |
| SHADER multi my_shader GLSL |
| # shaders |
| END |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader TYPE fragment |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("7: ATTACH TYPE requires an ENTRY_POINT", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingType) { |
| std::string in = R"( |
| SHADER multi my_shader GLSL |
| # shaders |
| END |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("7: multi shader ATTACH requires TYPE", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineMultiShaderMissingTypeWithEntryPoint) { |
| std::string in = R"( |
| SHADER multi my_shader GLSL |
| # shaders |
| END |
| PIPELINE graphics my_pipeline |
| ATTACH my_shader ENTRY_POINT my_ep |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("6: ATTACH missing TYPE for multi shader", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationUint32) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 AS uint32 4 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| EXPECT_EQ(r.Error(), ""); |
| ASSERT_TRUE(r.IsSuccess()); |
| |
| auto script = parser.GetScript(); |
| const auto& pipelines = script->GetPipelines(); |
| ASSERT_EQ(1U, pipelines.size()); |
| |
| const auto* pipeline = pipelines[0].get(); |
| const auto& shaders = pipeline->GetShaders(); |
| ASSERT_EQ(1U, shaders.size()); |
| |
| EXPECT_EQ(1u, shaders[0].GetSpecialization().size()); |
| EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1)); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationInt32) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 2 AS int32 -1 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_TRUE(r.IsSuccess()); |
| |
| auto script = parser.GetScript(); |
| const auto& pipelines = script->GetPipelines(); |
| ASSERT_EQ(1U, pipelines.size()); |
| |
| const auto* pipeline = pipelines[0].get(); |
| const auto& shaders = pipeline->GetShaders(); |
| ASSERT_EQ(1U, shaders.size()); |
| |
| EXPECT_EQ(1u, shaders[0].GetSpecialization().size()); |
| EXPECT_EQ(0xffffffffu, shaders[0].GetSpecialization().at(2)); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationFloat) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 3 AS float 1.1 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_TRUE(r.IsSuccess()); |
| |
| auto script = parser.GetScript(); |
| const auto& pipelines = script->GetPipelines(); |
| ASSERT_EQ(1U, pipelines.size()); |
| |
| const auto* pipeline = pipelines[0].get(); |
| const auto& shaders = pipeline->GetShaders(); |
| ASSERT_EQ(1U, shaders.size()); |
| |
| EXPECT_EQ(1u, shaders[0].GetSpecialization().size()); |
| EXPECT_EQ(0x3f8ccccdu, shaders[0].GetSpecialization().at(3)); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationIDIsString) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE s3 AS float 1.1 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("6: specialization ID must be an integer", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationNoAS) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 ASa float 1.1 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("6: expected AS as next token", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationNotDataType) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_ep SPECIALIZE 1 AS uint 1.1 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ("6: invalid data type 'uint' provided", r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationBadDataType) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader ENTRY_POINT my_ep SPECIALIZE 1 AS uint8 1.1 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_FALSE(r.IsSuccess()); |
| EXPECT_EQ( |
| "6: only 32-bit types are currently accepted for specialization values", |
| r.Error()); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationMultipleSpecializations) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader TYPE compute ENTRY_POINT my_ep \ |
| SPECIALIZE 1 AS uint32 4 \ |
| SPECIALIZE 2 AS uint32 5 \ |
| SPECIALIZE 5 AS uint32 1 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_TRUE(r.IsSuccess()); |
| |
| auto script = parser.GetScript(); |
| const auto& pipelines = script->GetPipelines(); |
| ASSERT_EQ(1U, pipelines.size()); |
| |
| const auto* pipeline = pipelines[0].get(); |
| const auto& shaders = pipeline->GetShaders(); |
| ASSERT_EQ(1U, shaders.size()); |
| |
| EXPECT_EQ(3u, shaders[0].GetSpecialization().size()); |
| EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1)); |
| EXPECT_EQ(5u, shaders[0].GetSpecialization().at(2)); |
| EXPECT_EQ(1u, shaders[0].GetSpecialization().at(5)); |
| } |
| |
| TEST_F(AmberScriptParserTest, PipelineSpecializationNoType) { |
| std::string in = R"( |
| SHADER compute my_shader GLSL |
| #shaders |
| END |
| PIPELINE compute my_pipeline |
| ATTACH my_shader SPECIALIZE 1 AS uint32 4 |
| END)"; |
| |
| Parser parser; |
| Result r = parser.Parse(in); |
| ASSERT_TRUE(r.IsSuccess()); |
| |
| auto script = parser.GetScript(); |
| const auto& pipelines = script->GetPipelines(); |
| ASSERT_EQ(1U, pipelines.size()); |
| |
| const auto* pipeline = pipelines[0].get(); |
| const auto& shaders = pipeline->GetShaders(); |
| ASSERT_EQ(1U, shaders.size()); |
| |
| EXPECT_EQ(1u, shaders[0].GetSpecialization().size()); |
| EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1)); |
| } |
| |
| } // namespace amberscript |
| } // namespace amber |