// 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, Pipeline) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics my_pipeline ATTACH my_shader ATTACH my_fragment END )"; Parser parser; Result r = parser.Parse(in); ASSERT_TRUE(r.IsSuccess()) << r.Error(); auto script = parser.GetScript(); EXPECT_EQ(2U, script->GetShaders().size()); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(1U, pipelines.size()); const auto* pipeline = pipelines[0].get(); EXPECT_EQ("my_pipeline", pipeline->GetName()); EXPECT_EQ(PipelineType::kGraphics, pipeline->GetType()); const auto& shaders = pipeline->GetShaders(); ASSERT_EQ(2U, shaders.size()); ASSERT_TRUE(shaders[0].GetShader() != nullptr); EXPECT_EQ("my_shader", shaders[0].GetShader()->GetName()); EXPECT_EQ(kShaderTypeVertex, shaders[0].GetShader()->GetType()); EXPECT_EQ(static_cast(0), shaders[0].GetShaderOptimizations().size()); ASSERT_TRUE(shaders[1].GetShader() != nullptr); EXPECT_EQ("my_fragment", shaders[1].GetShader()->GetName()); EXPECT_EQ(kShaderTypeFragment, shaders[1].GetShader()->GetType()); EXPECT_EQ(static_cast(0), shaders[1].GetShaderOptimizations().size()); } TEST_F(AmberScriptParserTest, PipelineMissingEnd) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH PIPELINE graphics my_pipeline ATTACH my_shader )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("5: PIPELINE missing END command", r.Error()); } TEST_F(AmberScriptParserTest, PipelineWithExtraParams) { std::string in = R"( PIPELINE graphics my_pipeline INVALID ATTACH my_shader END )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("2: extra parameters after PIPELINE command: INVALID", r.Error()); } TEST_F(AmberScriptParserTest, PipelineInvalidType) { std::string in = "PIPELINE my_name\nEND"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("1: unknown pipeline type: my_name", r.Error()); } TEST_F(AmberScriptParserTest, PipelineMissingName) { std::string in = "PIPELINE compute\nEND"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("2: invalid token when looking for pipeline name", r.Error()); } TEST_F(AmberScriptParserTest, PipelineWithInvalidTokenType) { std::string in = "PIPELINE 123 my_pipeline\nEND"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("1: invalid token when looking for pipeline type", r.Error()); } TEST_F(AmberScriptParserTest, PipelineWithInvalidTokenName) { std::string in = "PIPELINE compute 123\nEND"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("1: invalid token when looking for pipeline name", r.Error()); } TEST_F(AmberScriptParserTest, PipelineEmpty) { std::string in = "PIPELINE compute my_pipeline\nEND"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("compute pipeline requires a compute shader", r.Error()); } TEST_F(AmberScriptParserTest, PipelineWithUnknownCommand) { std::string in = R"( PIPELINE compute my_pipeline SHADER vertex my_shader PASSTHROUGH END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("3: unknown token in pipeline block: SHADER", r.Error()); } TEST_F(AmberScriptParserTest, DuplicatePipelineName) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # Fragment shader END PIPELINE graphics my_pipeline ATTACH my_shader ATTACH my_fragment END PIPELINE graphics my_pipeline ATTACH my_shader ATTACH my_fragment END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("14: duplicate pipeline name provided", r.Error()); } TEST_F(AmberScriptParserTest, PipelineDefaultColorBuffer) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics my_pipeline ATTACH my_shader ATTACH my_fragment END PIPELINE graphics my_pipeline2 ATTACH my_shader 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(2U, pipelines.size()); ASSERT_EQ(1U, pipelines[0]->GetColorAttachments().size()); const auto& buf1 = pipelines[0]->GetColorAttachments()[0]; ASSERT_TRUE(buf1.buffer != nullptr); Buffer* buffer1 = buf1.buffer; EXPECT_EQ(FormatType::kB8G8R8A8_UNORM, buffer1->GetFormat()->GetFormatType()); EXPECT_EQ(0u, buf1.location); EXPECT_EQ(250u * 250u, buffer1->ElementCount()); EXPECT_EQ(250u * 250u * 4u, buffer1->ValueCount()); EXPECT_EQ(250u * 250u * 4u * sizeof(uint8_t), buffer1->GetSizeInBytes()); ASSERT_EQ(1U, pipelines[1]->GetColorAttachments().size()); const auto& buf2 = pipelines[1]->GetColorAttachments()[0]; ASSERT_TRUE(buf2.buffer != nullptr); ASSERT_EQ(buffer1, buf2.buffer); EXPECT_EQ(0u, buf2.location); EXPECT_EQ(FormatType::kB8G8R8A8_UNORM, buf2.buffer->GetFormat()->GetFormatType()); EXPECT_EQ(250u * 250u, buf2.buffer->ElementCount()); EXPECT_EQ(250u * 250u * 4u, buf2.buffer->ValueCount()); EXPECT_EQ(250u * 250u * 4u * sizeof(uint8_t), buf2.buffer->GetSizeInBytes()); } TEST_F(AmberScriptParserTest, PipelineDefaultColorBufferMismatchSize) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics my_pipeline ATTACH my_shader ATTACH my_fragment END PIPELINE graphics my_pipeline2 ATTACH my_shader ATTACH my_fragment FRAMEBUFFER_SIZE 256 256 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("shared framebuffer must have same size over all PIPELINES", r.Error()); } TEST_F(AmberScriptParserTest, PipelinePolygonMode) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics my_pipeline_default ATTACH my_shader ATTACH my_fragment FRAMEBUFFER_SIZE 256 256 END PIPELINE graphics my_pipeline_fill ATTACH my_shader ATTACH my_fragment POLYGON_MODE fill FRAMEBUFFER_SIZE 256 256 END PIPELINE graphics my_pipeline_line ATTACH my_shader ATTACH my_fragment POLYGON_MODE line FRAMEBUFFER_SIZE 256 256 END PIPELINE graphics my_pipeline_point ATTACH my_shader ATTACH my_fragment POLYGON_MODE point FRAMEBUFFER_SIZE 256 256 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_TRUE(r.IsSuccess()); auto script = parser.GetScript(); const auto& pipelines = script->GetPipelines(); ASSERT_EQ(4U, pipelines.size()); auto mode0 = pipelines[0]->GetPipelineData()->GetPolygonMode(); ASSERT_EQ(mode0, PolygonMode::kFill); auto mode1 = pipelines[1]->GetPipelineData()->GetPolygonMode(); ASSERT_EQ(mode1, PolygonMode::kFill); auto mode2 = pipelines[2]->GetPipelineData()->GetPolygonMode(); ASSERT_EQ(mode2, PolygonMode::kLine); auto mode3 = pipelines[3]->GetPipelineData()->GetPolygonMode(); ASSERT_EQ(mode3, PolygonMode::kPoint); } TEST_F(AmberScriptParserTest, PipelineMissingPolygonMode) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics my_pipeline ATTACH my_shader ATTACH my_fragment POLYGON_MODE FRAMEBUFFER_SIZE 256 256 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("11: missing mode in POLYGON_MODE command", r.Error()); } TEST_F(AmberScriptParserTest, PipelineInvalidPolygonMode) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics my_pipeline ATTACH my_shader ATTACH my_fragment POLYGON_MODE foo FRAMEBUFFER_SIZE 256 256 END)"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("10: invalid polygon mode: foo", r.Error()); } TEST_F(AmberScriptParserTest, DerivePipeline) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END SHADER fragment other_fragment GLSL # GLSL Shader END BUFFER buf1 DATA_TYPE int32 SIZE 20 FILL 5 BUFFER buf2 DATA_TYPE int32 SIZE 20 FILL 7 PIPELINE graphics parent_pipeline ATTACH my_shader ATTACH my_fragment BIND BUFFER buf1 AS storage DESCRIPTOR_SET 1 BINDING 3 END DERIVE_PIPELINE child_pipeline FROM parent_pipeline ATTACH other_fragment BIND BUFFER buf2 AS storage DESCRIPTOR_SET 1 BINDING 3 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(2U, pipelines.size()); const auto* pipeline1 = pipelines[0].get(); auto buffers1 = pipeline1->GetBuffers(); ASSERT_EQ(1U, buffers1.size()); EXPECT_EQ("buf1", buffers1[0].buffer->GetName()); EXPECT_EQ(1u, buffers1[0].descriptor_set); EXPECT_EQ(3u, buffers1[0].binding); auto shaders1 = pipeline1->GetShaders(); ASSERT_EQ(2U, shaders1.size()); EXPECT_EQ("my_shader", shaders1[0].GetShader()->GetName()); EXPECT_EQ("my_fragment", shaders1[1].GetShader()->GetName()); const auto* pipeline2 = pipelines[1].get(); EXPECT_EQ("child_pipeline", pipeline2->GetName()); auto buffers2 = pipeline2->GetBuffers(); ASSERT_EQ(1U, buffers2.size()); EXPECT_EQ("buf2", buffers2[0].buffer->GetName()); EXPECT_EQ(1u, buffers2[0].descriptor_set); EXPECT_EQ(3u, buffers2[0].binding); auto shaders2 = pipeline2->GetShaders(); ASSERT_EQ(2U, shaders2.size()); EXPECT_EQ("my_shader", shaders2[0].GetShader()->GetName()); EXPECT_EQ("other_fragment", shaders2[1].GetShader()->GetName()); } TEST_F(AmberScriptParserTest, DerivePipelineMissingEnd) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics parent_pipeline ATTACH my_shader ATTACH my_fragment END DERIVE_PIPELINE derived_pipeline FROM parent_pipeline )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("13: DERIVE_PIPELINE missing END command", r.Error()); } TEST_F(AmberScriptParserTest, DerivePipelineMissingPipelineName) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics parent_pipeline ATTACH my_shader ATTACH my_fragment END DERIVE_PIPELINE FROM parent_pipeline END )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("12: missing pipeline name for DERIVE_PIPELINE command", r.Error()); } TEST_F(AmberScriptParserTest, DerivePipelineMissingFrom) { std::string in = R"( DERIVE_PIPELINE derived_pipeline parent_pipeline END )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("2: missing FROM in DERIVE_PIPELINE command", r.Error()); } TEST_F(AmberScriptParserTest, DerivePipelineMissingParentPipelineName) { std::string in = R"( DERIVE_PIPELINE derived_pipeline FROM END )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("3: missing parent pipeline name in DERIVE_PIPELINE command", r.Error()); } TEST_F(AmberScriptParserTest, DerivePipelineUnknownParentPipeline) { std::string in = R"( DERIVE_PIPELINE derived_pipeline FROM parent_pipeline END )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("2: unknown parent pipeline in DERIVE_PIPELINE command", r.Error()); } TEST_F(AmberScriptParserTest, DerivePipelineDuplicatePipelineName) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH SHADER fragment my_fragment GLSL # GLSL Shader END PIPELINE graphics parent_pipeline ATTACH my_shader ATTACH my_fragment END DERIVE_PIPELINE parent_pipeline FROM parent_pipeline END )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("12: duplicate pipeline name for DERIVE_PIPELINE command", r.Error()); } TEST_F(AmberScriptParserTest, DerivePipelineNoParams) { std::string in = R"( DERIVE_PIPELINE END )"; Parser parser; Result r = parser.Parse(in); ASSERT_FALSE(r.IsSuccess()); EXPECT_EQ("3: missing pipeline name for DERIVE_PIPELINE command", r.Error()); } TEST_F(AmberScriptParserTest, DerivePipelineSpecialized) { std::string in = R"( SHADER compute my_shader GLSL #shaders END PIPELINE compute p1 ATTACH my_shader SPECIALIZE 3 AS uint32 4 END DERIVE_PIPELINE p2 FROM p1 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(2U, pipelines.size()); const auto* p1 = pipelines[0].get(); const auto& s1 = p1->GetShaders(); ASSERT_EQ(1U, s1.size()); EXPECT_EQ(1u, s1[0].GetSpecialization().size()); EXPECT_EQ(4u, s1[0].GetSpecialization().at(3)); const auto* p2 = pipelines[1].get(); const auto& s2 = p2->GetShaders(); ASSERT_EQ(1U, s2.size()); EXPECT_EQ(1u, s2[0].GetSpecialization().size()); EXPECT_EQ(4u, s2[0].GetSpecialization().at(3)); } } // namespace amberscript } // namespace amber