blob: ebd2ddd688795dcf3f3c5a6eb109f3c78aaee49e [file] [log] [blame]
// 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<uint32_t>(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<uint32_t>(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", 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(0, buf1.location);
EXPECT_EQ(250 * 250, buffer1->ElementCount());
EXPECT_EQ(250 * 250 * 4, buffer1->ValueCount());
EXPECT_EQ(250 * 250 * 4 * 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(0, buf2.location);
EXPECT_EQ(FormatType::kB8G8R8A8_UNORM,
buf2.buffer->GetFormat()->GetFormatType());
EXPECT_EQ(250 * 250, buf2.buffer->ElementCount());
EXPECT_EQ(250 * 250 * 4, buf2.buffer->ValueCount());
EXPECT_EQ(250 * 250 * 4 * 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, 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(1, buffers1[0].descriptor_set);
EXPECT_EQ(3, 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(1, buffers2[0].descriptor_set);
EXPECT_EQ(3, 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(1, s1[0].GetSpecialization().size());
EXPECT_EQ(4, s1[0].GetSpecialization().at(3));
const auto* p2 = pipelines[1].get();
const auto& s2 = p2->GetShaders();
ASSERT_EQ(1U, s2.size());
EXPECT_EQ(1, s2[0].GetSpecialization().size());
EXPECT_EQ(4, s2[0].GetSpecialization().at(3));
}
} // namespace amberscript
} // namespace amber