aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordan sinclair <dj2@everburning.com>2019-04-09 18:09:47 -0400
committerDavid Neto <dneto@google.com>2019-04-09 18:09:47 -0400
commit43248b29723575d27eb96187ef9ca594739140ed (patch)
tree1f00ee30857a26b62a8a170d263698d52f9e17c0
parent539730372bb8145fa029759be17dff320c970895 (diff)
downloadamber-43248b29723575d27eb96187ef9ca594739140ed.tar.gz
[AmberScript] Add DERIVE_PIPELINE (#453)
This CL adds the DERIVE_PIPELINE command to allow pipelines to be based off of previously declared pipelines. Fixes #435.
-rw-r--r--docs/amber_script.md16
-rw-r--r--src/amberscript/parser.cc99
-rw-r--r--src/amberscript/parser.h3
-rw-r--r--src/amberscript/parser_bind_test.cc104
-rw-r--r--src/amberscript/parser_buffer_test.cc28
-rw-r--r--src/amberscript/parser_pipeline_test.cc169
-rw-r--r--src/pipeline.cc47
-rw-r--r--src/pipeline.h23
-rw-r--r--src/pipeline_test.cc132
-rw-r--r--src/vkscript/command_parser.cc4
-rw-r--r--src/vkscript/parser.cc6
-rw-r--r--src/vulkan/engine_vulkan.cc2
-rw-r--r--src/vulkan/graphics_pipeline.cc4
-rw-r--r--tests/cases/draw_rectangles.amber80
-rw-r--r--tools/amber-syntax.vim2
-rw-r--r--tools/amber.sublime-syntax4
16 files changed, 490 insertions, 233 deletions
diff --git a/docs/amber_script.md b/docs/amber_script.md
index c7d1399..c755d2a 100644
--- a/docs/amber_script.md
+++ b/docs/amber_script.md
@@ -159,8 +159,14 @@ argument to an EXPECT command.
PIPELINE <pipeline_type> <pipeline_name>
...
END
+
+# Create a pipeline and inherit from a previously declared pipeline.
+DERIVE_PIPELINE <<pipeline_name> FROM <parent_pipeline>
+...
+END
```
+
### Pipeline Content
The following commands are all specified within the `PIPELINE` command.
@@ -225,10 +231,6 @@ attachment content, depth/stencil content, uniform buffers, etc.
# and binding. The buffer will use a start index of 0.
BIND BUFFER <buffer_name> AS <buffer_type> DESCRIPTOR_SET <id> \
BINDING <id>
- # Bind the buffer of the given |buffer_type| at the given descriptor set
- # and binding and index at the given value.
- BIND BUFFER <buffer_name> AS <buffer_type> DESCRIPTOR_SET <id> \
- BINDING <id> IDX <val>
# Bind the sampler at the given descriptor set and binding.
BIND SAMPLER <sampler_name> DESCRIPTOR_SET <id> BINDING <id>
@@ -482,7 +484,7 @@ PIPELINE graphics kRedPipeline
ATTACH kFragmentShader ENTRY_POINT red
FRAMEBUFFER_SIZE 256 256
- BIND BUFFER kImgBuffer AS image IDX 0
+ BIND BUFFER kImgBuffer AS color LOCATION 0
END  # pipeline
PIPELINE graphics kGreenPipeline
@@ -490,7 +492,7 @@ PIPELINE graphics kGreenPipeline
ATTACH kFragmentShader ENTRY_POINT green
FRAMEBUFFER_SIZE 256 256
- BIND BUFFER kImgBuffer AS image IDX 0
+ BIND BUFFER kImgBuffer AS color LOCATION 0
END  # pipeline
RUN kRedPipeline DRAW_RECT POS 0 0 SIZE 256 256
@@ -597,7 +599,7 @@ CLEAR_COLOR kGraphicsPipeline 255 0 0 255
CLEAR kGraphicsPipeline
RUN kGraphicsPipeline DRAW_ARRAY AS triangle_list START_IDX 0 COUNT 24
- ```
+```
### Image Formats
* A1R5G5B5_UNORM_PACK16
diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc
index c0fa6c9..bd33320 100644
--- a/src/amberscript/parser.cc
+++ b/src/amberscript/parser.cc
@@ -76,6 +76,8 @@ Result Parser::Parse(const std::string& data) {
r = ParseRepeatableCommand(tok);
} else if (tok == "BUFFER") {
r = ParseBuffer();
+ } else if (tok == "DERIVE_PIPELINE") {
+ r = ParseDerivePipelineBlock();
} else if (tok == "DEVICE_FEATURE") {
r = ParseDeviceFeature();
} else if (tok == "PIPELINE") {
@@ -92,7 +94,7 @@ Result Parser::Parse(const std::string& data) {
}
script_->SetCommands(std::move(command_list_));
- // Generate any needed color and depth attachments. This is done before
+ // Generate any needed color attachments. This is done before
// validating in case one of the pipelines specifies the framebuffer size
// it needs to be verified against all other pipelines.
for (const auto& pipeline : script_->GetPipelines()) {
@@ -111,23 +113,6 @@ Result Parser::Parse(const std::string& data) {
if (!r.IsSuccess())
return r;
}
-
- // Add a depth buffer if needed
- if (pipeline->GetDepthBuffer().buffer == nullptr) {
- auto* buf = script_->GetBuffer(Pipeline::kGeneratedDepthBuffer);
- if (!buf) {
- auto new_buf = pipeline->GenerateDefaultDepthAttachmentBuffer();
- buf = new_buf.get();
-
- Result r = script_->AddBuffer(std::move(new_buf));
- if (!r.IsSuccess())
- return r;
- }
-
- Result r = pipeline->SetDepthBuffer(buf);
- if (!r.IsSuccess())
- return r;
- }
}
// Validate all the pipelines at the end. This allows us to verify the
@@ -391,6 +376,12 @@ Result Parser::ParsePipelineBlock() {
if (!r.IsSuccess())
return r;
+ return ParsePipelineBody("PIPELINE", std::move(pipeline));
+}
+
+Result Parser::ParsePipelineBody(const std::string& cmd_name,
+ std::unique_ptr<Pipeline> pipeline) {
+ std::unique_ptr<Token> token;
for (token = tokenizer_->NextToken(); !token->IsEOS();
token = tokenizer_->NextToken()) {
if (token->IsEOL())
@@ -398,6 +389,7 @@ Result Parser::ParsePipelineBlock() {
if (!token->IsString())
return Result("expected string");
+ Result r;
std::string tok = token->AsString();
if (tok == "END") {
break;
@@ -421,9 +413,9 @@ Result Parser::ParsePipelineBlock() {
}
if (!token->IsString() || token->AsString() != "END")
- return Result("PIPELINE missing END command");
+ return Result(cmd_name + " missing END command");
- r = script_->AddPipeline(std::move(pipeline));
+ Result r = script_->AddPipeline(std::move(pipeline));
if (!r.IsSuccess())
return r;
@@ -637,21 +629,7 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) {
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("invalid value for BINDING in BIND command");
- uint32_t binding = token->AsUint32();
-
- token = tokenizer_->NextToken();
- if (token->IsEOL() || token->IsEOS()) {
- pipeline->AddBuffer(buffer, descriptor_set, binding, 0);
- return {};
- }
- if (!token->IsString() || token->AsString() != "IDX")
- return Result("extra parameters after BIND command");
-
- token = tokenizer_->NextToken();
- if (!token->IsInteger())
- return Result("invalid value for IDX in BIND command");
-
- pipeline->AddBuffer(buffer, descriptor_set, binding, token->AsUint32());
+ pipeline->AddBuffer(buffer, descriptor_set, token->AsUint32());
}
return ValidateEndOfStatement("BIND command");
@@ -886,6 +864,8 @@ Result Parser::ParseBufferInitializerData(DataBuffer* buffer) {
Value v;
if (is_double_type) {
+ token->ConvertToDouble();
+
double val = token->IsHex() ? static_cast<double>(token->AsHex())
: token->AsDouble();
v.SetDoubleValue(val);
@@ -910,6 +890,8 @@ Result Parser::ParseRun() {
if (!token->IsString())
return Result("missing pipeline name for RUN command");
+ size_t line = tokenizer_->GetCurrentLine();
+
auto* pipeline = script_->GetPipeline(token->AsString());
if (!pipeline)
return Result("unknown pipeline for RUN command: " + token->AsString());
@@ -923,6 +905,7 @@ Result Parser::ParseRun() {
return Result("RUN command requires compute pipeline");
auto cmd = MakeUnique<ComputeCommand>(pipeline);
+ cmd->SetLine(line);
cmd->SetX(token->AsUint32());
token = tokenizer_->NextToken();
@@ -963,6 +946,7 @@ Result Parser::ParseRun() {
return Result("missing X position for RUN command");
auto cmd = MakeUnique<DrawRectCommand>(pipeline, PipelineData{});
+ cmd->SetLine(line);
cmd->EnableOrtho();
Result r = token->ConvertToDouble();
@@ -1012,6 +996,7 @@ Result Parser::ParseRun() {
return Result("RUN command requires graphics pipeline");
auto cmd = MakeUnique<DrawArraysCommand>(pipeline, PipelineData{});
+ cmd->SetLine(line);
command_list_.push_back(std::move(cmd));
return ValidateEndOfStatement("RUN command");
@@ -1022,10 +1007,11 @@ Result Parser::ParseRun() {
Result Parser::ParseClear() {
auto token = tokenizer_->NextToken();
-
if (!token->IsString())
return Result("missing pipeline name for CLEAR command");
+ size_t line = tokenizer_->GetCurrentLine();
+
auto* pipeline = script_->GetPipeline(token->AsString());
if (!pipeline)
return Result("unknown pipeline for CLEAR command: " + token->AsString());
@@ -1033,6 +1019,7 @@ Result Parser::ParseClear() {
return Result("CLEAR command requires graphics pipeline");
auto cmd = MakeUnique<ClearCommand>(pipeline);
+ cmd->SetLine(line);
command_list_.push_back(std::move(cmd));
return ValidateEndOfStatement("CLEAR command");
@@ -1083,6 +1070,7 @@ Result Parser::ParseExpect() {
if (token->AsString() == "EQ_BUFFER")
return Result("missing buffer name between EXPECT and EQ_BUFFER");
+ size_t line = tokenizer_->GetCurrentLine();
auto* buffer = script_->GetBuffer(token->AsString());
if (!buffer)
return Result("unknown buffer name for EXPECT command: " +
@@ -1154,6 +1142,7 @@ Result Parser::ParseExpect() {
return Result("invalid Y value in EXPECT command");
auto probe = MakeUnique<ProbeCommand>(buffer);
+ probe->SetLine(line);
probe->SetX(x);
probe->SetY(y);
probe->SetProbeRect();
@@ -1217,6 +1206,7 @@ Result Parser::ParseExpect() {
return Result("comparator must be provided a data buffer");
auto probe = MakeUnique<ProbeSSBOCommand>(buffer);
+ probe->SetLine(line);
probe->SetComparator(ToComparator(token->AsString()));
probe->SetDatumType(buffer->AsDataBuffer()->GetDatumType());
probe->SetOffset(static_cast<uint32_t>(x));
@@ -1247,6 +1237,8 @@ Result Parser::ParseCopy() {
if (!token->IsString())
return Result("invalid buffer name after COPY");
+ size_t line = tokenizer_->GetCurrentLine();
+
auto name = token->AsString();
if (name == "TO")
return Result("missing buffer name between COPY and TO");
@@ -1290,6 +1282,7 @@ Result Parser::ParseCopy() {
return Result("COPY origin and destination buffers are identical");
auto cmd = MakeUnique<CopyCommand>(buffer_from, buffer_to);
+ cmd->SetLine(line);
command_list_.push_back(std::move(cmd));
return ValidateEndOfStatement("COPY command");
@@ -1300,6 +1293,8 @@ Result Parser::ParseClearColor() {
if (!token->IsString())
return Result("missing pipeline name for CLEAR_COLOR command");
+ size_t line = tokenizer_->GetCurrentLine();
+
auto* pipeline = script_->GetPipeline(token->AsString());
if (!pipeline) {
return Result("unknown pipeline for CLEAR_COLOR command: " +
@@ -1310,6 +1305,7 @@ Result Parser::ParseClearColor() {
}
auto cmd = MakeUnique<ClearColorCommand>(pipeline);
+ cmd->SetLine(line);
token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
@@ -1414,5 +1410,36 @@ Result Parser::ParseRepeat() {
return ValidateEndOfStatement("REPEAT command");
}
+Result Parser::ParseDerivePipelineBlock() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsString() || token->AsString() == "FROM")
+ return Result("missing pipeline name for DERIVE_PIPELINE command");
+
+ std::string name = token->AsString();
+ if (script_->GetPipeline(name) != nullptr)
+ return Result("duplicate pipeline name for DERIVE_PIPELINE command");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsString() || token->AsString() != "FROM")
+ return Result("missing FROM in DERIVE_PIPELINE command");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsString())
+ return Result("missing parent pipeline name in DERIVE_PIPELINE command");
+
+ Pipeline* parent = script_->GetPipeline(token->AsString());
+ if (!parent)
+ return Result("unknown parent pipeline in DERIVE_PIPELINE command");
+
+ Result r = ValidateEndOfStatement("DERIVE_PIPELINE command");
+ if (!r.IsSuccess())
+ return r;
+
+ auto pipeline = parent->Clone();
+ pipeline->SetName(name);
+
+ return ParsePipelineBody("DERIVE_PIPELINE", std::move(pipeline));
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/amberscript/parser.h b/src/amberscript/parser.h
index faf25f2..289011c 100644
--- a/src/amberscript/parser.h
+++ b/src/amberscript/parser.h
@@ -70,6 +70,9 @@ class Parser : public amber::Parser {
Result ParseRepeat();
bool IsRepeatable(const std::string& name) const;
Result ParseRepeatableCommand(const std::string& name);
+ Result ParseDerivePipelineBlock();
+ Result ParsePipelineBody(const std::string& cmd_name,
+ std::unique_ptr<Pipeline> pipeline);
// Parses a set of values out of the token stream. |name| is the name of the
// current command we're parsing for error purposes. The |type| is the type
diff --git a/src/amberscript/parser_bind_test.cc b/src/amberscript/parser_bind_test.cc
index b5be6bc..90485a3 100644
--- a/src/amberscript/parser_bind_test.cc
+++ b/src/amberscript/parser_bind_test.cc
@@ -923,62 +923,6 @@ END
bufs[0].buffer->AsFormatBuffer()->GetFormat().GetFormatType());
}
-TEST_F(AmberScriptParserTest, BindBufferWithIdx) {
- std::string in = R"(
-SHADER vertex my_shader PASSTHROUGH
-SHADER fragment my_fragment GLSL
-# GLSL Shader
-END
-BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
-
-PIPELINE graphics my_pipeline
- ATTACH my_shader
- ATTACH my_fragment
-
- BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 IDX 5
-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& bufs = pipeline->GetBuffers();
- ASSERT_EQ(1U, bufs.size());
- EXPECT_EQ(BufferType::kUniform, bufs[0].buffer->GetBufferType());
- EXPECT_EQ(1U, bufs[0].descriptor_set);
- EXPECT_EQ(2U, bufs[0].binding);
- EXPECT_EQ(5U, bufs[0].location);
- EXPECT_TRUE(bufs[0].buffer->IsFormatBuffer());
- EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
- bufs[0].buffer->AsFormatBuffer()->GetFormat().GetFormatType());
-}
-
-TEST_F(AmberScriptParserTest, BindBufferMissingIdxValue) {
- std::string in = R"(
-SHADER vertex my_shader PASSTHROUGH
-SHADER fragment my_fragment GLSL
-# GLSL Shader
-END
-BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
-
-PIPELINE graphics my_pipeline
- ATTACH my_shader
- ATTACH my_fragment
-
- BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 IDX
-END)";
-
- Parser parser;
- Result r = parser.Parse(in);
- ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("13: invalid value for IDX in BIND command", r.Error());
-}
-
TEST_F(AmberScriptParserTest, BindBufferMissingBindingValue) {
std::string in = R"(
SHADER vertex my_shader PASSTHROUGH
@@ -1012,13 +956,13 @@ PIPELINE graphics my_pipeline
ATTACH my_shader
ATTACH my_fragment
- BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 IDX 5
+ BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1
END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: missing BINDING for BIND command", r.Error());
+ EXPECT_EQ("13: missing BINDING for BIND command", r.Error());
}
TEST_F(AmberScriptParserTest, BindBufferMissingDescriptorSetValue) {
@@ -1084,48 +1028,6 @@ END)";
EXPECT_EQ("12: extra parameters after BIND command", r.Error());
}
-TEST_F(AmberScriptParserTest, BindingBufferIdxExtraParams) {
- std::string in = R"(
-SHADER vertex my_shader PASSTHROUGH
-SHADER fragment my_fragment GLSL
-# GLSL Shader
-END
-BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
-
-PIPELINE graphics my_pipeline
- ATTACH my_shader
- ATTACH my_fragment
-
- BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 IDX 5 EXTRA
-END)";
-
- Parser parser;
- Result r = parser.Parse(in);
- ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: extra parameters after BIND command", r.Error());
-}
-
-TEST_F(AmberScriptParserTest, BindingBufferInvalidIdxValue) {
- std::string in = R"(
-SHADER vertex my_shader PASSTHROUGH
-SHADER fragment my_fragment GLSL
-# GLSL Shader
-END
-BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
-
-PIPELINE graphics my_pipeline
- ATTACH my_shader
- ATTACH my_fragment
-
- BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 IDX INVALID
-END)";
-
- Parser parser;
- Result r = parser.Parse(in);
- ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: invalid value for IDX in BIND command", r.Error());
-}
-
TEST_F(AmberScriptParserTest, BindingBufferInvalidBindingValue) {
std::string in = R"(
SHADER vertex my_shader PASSTHROUGH
@@ -1180,7 +1082,7 @@ PIPELINE graphics my_pipeline
ATTACH my_shader
ATTACH my_fragment
- BIND BUFFER my_buf AS INVALID DESCRIPTOR_SET 1 BINDING 2 IDX INVALID
+ BIND BUFFER my_buf AS INVALID DESCRIPTOR_SET 1 BINDING 2
END)";
Parser parser;
diff --git a/src/amberscript/parser_buffer_test.cc b/src/amberscript/parser_buffer_test.cc
index 0c0bbec..4f8769f 100644
--- a/src/amberscript/parser_buffer_test.cc
+++ b/src/amberscript/parser_buffer_test.cc
@@ -80,6 +80,34 @@ TEST_F(AmberScriptParserTest, BufferDataOneLine) {
}
}
+TEST_F(AmberScriptParserTest, BufferDataFloat) {
+ std::string in = "BUFFER my_buffer DATA_TYPE float DATA 1 2 3 4 END";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("my_buffer", buffers[0]->GetName());
+
+ ASSERT_TRUE(buffers[0]->IsDataBuffer());
+ auto* buffer = buffers[0]->AsDataBuffer();
+ EXPECT_TRUE(buffer->GetDatumType().IsFloat());
+ EXPECT_EQ(4U, buffer->GetSize());
+ EXPECT_EQ(4U * sizeof(float), buffer->GetSizeInBytes());
+
+ std::vector<float> results = {1, 2, 3, 4};
+ const auto* data = buffer->GetValues<float>();
+ ASSERT_EQ(results.size(), buffer->GetSize());
+ for (size_t i = 0; i < results.size(); ++i) {
+ EXPECT_FLOAT_EQ(results[i], data[i]);
+ }
+}
+
TEST_F(AmberScriptParserTest, BufferFill) {
std::string in = "BUFFER my_buffer DATA_TYPE uint8 SIZE 5 FILL 5";
diff --git a/src/amberscript/parser_pipeline_test.cc b/src/amberscript/parser_pipeline_test.cc
index 1610c58..41d781a 100644
--- a/src/amberscript/parser_pipeline_test.cc
+++ b/src/amberscript/parser_pipeline_test.cc
@@ -240,17 +240,29 @@ END)";
r.Error());
}
-TEST_F(AmberScriptParserTest, PipelineDefaultDepthBuffer) {
+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 my_pipeline
+PIPELINE graphics parent_pipeline
ATTACH my_shader
ATTACH my_fragment
-END)";
+ 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);
@@ -258,17 +270,148 @@ END)";
auto script = parser.GetScript();
const auto& pipelines = script->GetPipelines();
- ASSERT_EQ(1U, pipelines.size());
+ ASSERT_EQ(2U, pipelines.size());
- const auto* pipeline = pipelines[0].get();
- const auto& buf = pipeline->GetDepthBuffer();
-
- ASSERT_TRUE(buf.buffer != nullptr);
- EXPECT_EQ(FormatType::kD32_SFLOAT_S8_UINT,
- buf.buffer->AsFormatBuffer()->GetFormat().GetFormatType());
- EXPECT_EQ(250 * 250, buf.buffer->GetSize());
- EXPECT_EQ(250 * 250 * (sizeof(float) + sizeof(uint8_t)),
- buf.buffer->GetSizeInBytes());
+ 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());
}
} // namespace amberscript
diff --git a/src/pipeline.cc b/src/pipeline.cc
index a759aeb..7707dd2 100644
--- a/src/pipeline.cc
+++ b/src/pipeline.cc
@@ -31,7 +31,7 @@ const char* kDefaultDepthBufferFormat = "D32_SFLOAT_S8_UINT";
const char* Pipeline::kGeneratedColorBuffer = "framebuffer";
const char* Pipeline::kGeneratedDepthBuffer = "depth_buffer";
-Pipeline::ShaderInfo::ShaderInfo(const Shader* shader, ShaderType type)
+Pipeline::ShaderInfo::ShaderInfo(Shader* shader, ShaderType type)
: shader_(shader), shader_type_(type), entry_point_("main") {}
Pipeline::ShaderInfo::ShaderInfo(const ShaderInfo&) = default;
@@ -42,7 +42,20 @@ Pipeline::Pipeline(PipelineType type) : pipeline_type_(type) {}
Pipeline::~Pipeline() = default;
-Result Pipeline::AddShader(const Shader* shader, ShaderType shader_type) {
+std::unique_ptr<Pipeline> Pipeline::Clone() const {
+ auto clone = MakeUnique<Pipeline>(pipeline_type_);
+ clone->shaders_ = shaders_;
+ clone->color_attachments_ = color_attachments_;
+ clone->vertex_buffers_ = vertex_buffers_;
+ clone->buffers_ = buffers_;
+ clone->depth_buffer_ = depth_buffer_;
+ clone->index_buffer_ = index_buffer_;
+ clone->fb_width_ = fb_width_;
+ clone->fb_height_ = fb_height_;
+ return clone;
+}
+
+Result Pipeline::AddShader(Shader* shader, ShaderType shader_type) {
if (!shader)
return Result("shader can not be null when attached to pipeline");
@@ -55,12 +68,14 @@ Result Pipeline::AddShader(const Shader* shader, ShaderType shader_type) {
return Result("can not add a compute shader to a graphics pipeline");
}
- for (const auto& info : shaders_) {
+ for (auto& info : shaders_) {
const auto* is = info.GetShader();
if (is == shader)
return Result("can not add duplicate shader to pipeline");
- if (is->GetType() == shader_type)
- return Result("can not add duplicate shader type to pipeline");
+ if (is->GetType() == shader_type) {
+ info.SetShader(shader);
+ return {};
+ }
}
shaders_.emplace_back(shader, shader_type);
@@ -137,9 +152,7 @@ Result Pipeline::Validate() const {
}
}
- if (depth_buffer_.buffer == nullptr)
- return Result("PIPELINE missing depth buffer");
- if (depth_buffer_.buffer->GetSize() != fb_size)
+ if (depth_buffer_.buffer && depth_buffer_.buffer->GetSize() != fb_size)
return Result("shared depth buffer must have same size over all PIPELINES");
if (pipeline_type_ == PipelineType::kGraphics)
@@ -292,4 +305,22 @@ Buffer* Pipeline::GetBufferForBinding(uint32_t descriptor_set,
return nullptr;
}
+void Pipeline::AddBuffer(Buffer* buf,
+ uint32_t descriptor_set,
+ uint32_t binding) {
+ // If this buffer binding already exists, overwrite with the new buffer.
+ for (auto& info : buffers_) {
+ if (info.descriptor_set == descriptor_set && info.binding == binding) {
+ info.buffer = buf;
+ return;
+ }
+ }
+
+ buffers_.push_back(BufferInfo{buf});
+
+ auto& info = buffers_.back();
+ info.descriptor_set = descriptor_set;
+ info.binding = binding;
+}
+
} // namespace amber
diff --git a/src/pipeline.h b/src/pipeline.h
index 56bbd2a..cb6ded3 100644
--- a/src/pipeline.h
+++ b/src/pipeline.h
@@ -32,10 +32,12 @@ class Pipeline {
public:
class ShaderInfo {
public:
- ShaderInfo(const Shader*, ShaderType type);
+ ShaderInfo(Shader*, ShaderType type);
ShaderInfo(const ShaderInfo&);
~ShaderInfo();
+ ShaderInfo& operator=(const ShaderInfo&) = default;
+
void SetShaderOptimizations(const std::vector<std::string>& opts) {
shader_optimizations_ = opts;
}
@@ -43,6 +45,7 @@ class Pipeline {
return shader_optimizations_;
}
+ void SetShader(Shader* shader) { shader_ = shader; }
const Shader* GetShader() const { return shader_; }
void SetEntryPoint(const std::string& ep) { entry_point_ = ep; }
@@ -55,7 +58,7 @@ class Pipeline {
void SetData(std::vector<uint32_t>&& data) { data_ = std::move(data); }
private:
- const Shader* shader_ = nullptr;
+ Shader* shader_ = nullptr;
ShaderType shader_type_;
std::vector<std::string> shader_optimizations_;
std::string entry_point_;
@@ -78,6 +81,8 @@ class Pipeline {
explicit Pipeline(PipelineType type);
~Pipeline();
+ std::unique_ptr<Pipeline> Clone() const;
+
bool IsGraphics() const { return pipeline_type_ == PipelineType::kGraphics; }
bool IsCompute() const { return pipeline_type_ == PipelineType::kCompute; }
PipelineType GetType() const { return pipeline_type_; }
@@ -97,7 +102,7 @@ class Pipeline {
}
uint32_t GetFramebufferHeight() const { return fb_height_; }
- Result AddShader(const Shader*, ShaderType);
+ Result AddShader(Shader*, ShaderType);
std::vector<ShaderInfo>& GetShaders() { return shaders_; }
const std::vector<ShaderInfo>& GetShaders() const { return shaders_; }
@@ -123,17 +128,7 @@ class Pipeline {
Result SetIndexBuffer(Buffer* buf);
Buffer* GetIndexBuffer() const { return index_buffer_; }
- void AddBuffer(Buffer* buf,
- uint32_t descriptor_set,
- uint32_t binding,
- uint32_t location) {
- buffers_.push_back(BufferInfo{buf});
-
- auto& info = buffers_.back();
- info.descriptor_set = descriptor_set;
- info.binding = binding;
- info.location = location;
- }
+ void AddBuffer(Buffer* buf, uint32_t descriptor_set, uint32_t binding);
const std::vector<BufferInfo>& GetBuffers() const { return buffers_; }
Buffer* GetBufferForBinding(uint32_t descriptor_set, uint32_t binding) const;
diff --git a/src/pipeline_test.cc b/src/pipeline_test.cc
index 0e6bb7b..bd61375 100644
--- a/src/pipeline_test.cc
+++ b/src/pipeline_test.cc
@@ -15,6 +15,7 @@
#include "src/pipeline.h"
#include "gtest/gtest.h"
+#include "src/make_unique.h"
namespace amber {
namespace {
@@ -25,7 +26,7 @@ struct ShaderTypeData {
} // namespace
-class AmberScriptPipelineTest : public testing::Test {
+class PipelineTest : public testing::Test {
public:
void TearDown() override {
color_buffer_ = nullptr;
@@ -51,7 +52,7 @@ class AmberScriptPipelineTest : public testing::Test {
std::unique_ptr<Buffer> depth_buffer_;
};
-TEST_F(AmberScriptPipelineTest, AddShader) {
+TEST_F(PipelineTest, AddShader) {
Shader v(kShaderTypeVertex);
Shader f(kShaderTypeFragment);
@@ -69,14 +70,14 @@ TEST_F(AmberScriptPipelineTest, AddShader) {
EXPECT_EQ(&f, shaders[1].GetShader());
}
-TEST_F(AmberScriptPipelineTest, MissingShader) {
+TEST_F(PipelineTest, MissingShader) {
Pipeline p(PipelineType::kGraphics);
Result r = p.AddShader(nullptr, kShaderTypeVertex);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("shader can not be null when attached to pipeline", r.Error());
}
-TEST_F(AmberScriptPipelineTest, DuplicateShaders) {
+TEST_F(PipelineTest, DuplicateShaders) {
Shader v(kShaderTypeVertex);
Shader f(kShaderTypeFragment);
@@ -92,19 +93,6 @@ TEST_F(AmberScriptPipelineTest, DuplicateShaders) {
EXPECT_EQ("can not add duplicate shader to pipeline", r.Error());
}
-TEST_F(AmberScriptPipelineTest, DuplicateShaderType) {
- Shader v(kShaderTypeVertex);
- Shader f(kShaderTypeVertex);
-
- Pipeline p(PipelineType::kGraphics);
- Result r = p.AddShader(&v, kShaderTypeVertex);
- ASSERT_TRUE(r.IsSuccess()) << r.Error();
-
- r = p.AddShader(&f, kShaderTypeVertex);
- ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("can not add duplicate shader type to pipeline", r.Error());
-}
-
using AmberScriptPipelineComputePipelineTest =
testing::TestWithParam<ShaderTypeData>;
TEST_P(AmberScriptPipelineComputePipelineTest,
@@ -129,7 +117,7 @@ INSTANTIATE_TEST_CASE_P(
ShaderTypeData{
kShaderTypeTessellationControl}), ); // NOLINT(whitespace/parens)
-TEST_F(AmberScriptPipelineTest, SettingComputeShaderToGraphicsPipeline) {
+TEST_F(PipelineTest, SettingComputeShaderToGraphicsPipeline) {
Shader c(kShaderTypeCompute);
Pipeline p(PipelineType::kGraphics);
@@ -138,7 +126,7 @@ TEST_F(AmberScriptPipelineTest, SettingComputeShaderToGraphicsPipeline) {
EXPECT_EQ("can not add a compute shader to a graphics pipeline", r.Error());
}
-TEST_F(AmberScriptPipelineTest, SetShaderOptimizations) {
+TEST_F(PipelineTest, SetShaderOptimizations) {
Shader v(kShaderTypeVertex);
Shader f(kShaderTypeFragment);
@@ -164,7 +152,7 @@ TEST_F(AmberScriptPipelineTest, SetShaderOptimizations) {
EXPECT_EQ(first, shaders[1].GetShaderOptimizations());
}
-TEST_F(AmberScriptPipelineTest, DuplicateShaderOptimizations) {
+TEST_F(PipelineTest, DuplicateShaderOptimizations) {
Shader v(kShaderTypeVertex);
Pipeline p(PipelineType::kGraphics);
@@ -177,14 +165,14 @@ TEST_F(AmberScriptPipelineTest, DuplicateShaderOptimizations) {
EXPECT_EQ("duplicate optimization flag (One) set on shader", r.Error());
}
-TEST_F(AmberScriptPipelineTest, SetOptimizationForMissingShader) {
+TEST_F(PipelineTest, SetOptimizationForMissingShader) {
Pipeline p(PipelineType::kGraphics);
Result r = p.SetShaderOptimizations(nullptr, {"One", "Two"});
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("invalid shader specified for optimizations", r.Error());
}
-TEST_F(AmberScriptPipelineTest, SetOptimizationForInvalidShader) {
+TEST_F(PipelineTest, SetOptimizationForInvalidShader) {
Shader v(kShaderTypeVertex);
v.SetName("my_shader");
@@ -194,7 +182,7 @@ TEST_F(AmberScriptPipelineTest, SetOptimizationForInvalidShader) {
EXPECT_EQ("unknown shader specified for optimizations: my_shader", r.Error());
}
-TEST_F(AmberScriptPipelineTest, GraphicsPipelineRequiresColorAttachment) {
+TEST_F(PipelineTest, GraphicsPipelineRequiresColorAttachment) {
Pipeline p(PipelineType::kGraphics);
SetupDepthAttachment(&p);
@@ -203,17 +191,7 @@ TEST_F(AmberScriptPipelineTest, GraphicsPipelineRequiresColorAttachment) {
EXPECT_EQ("PIPELINE missing color attachment", r.Error());
}
-TEST_F(AmberScriptPipelineTest, GraphicsPipelineRequiresDepthAttachment) {
- Pipeline p(PipelineType::kGraphics);
- SetupColorAttachment(&p, 0);
-
- Result r = p.Validate();
- ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("PIPELINE missing depth buffer", r.Error());
-}
-
-TEST_F(AmberScriptPipelineTest,
- GraphicsPipelineRequiresVertexAndFragmentShader) {
+TEST_F(PipelineTest, GraphicsPipelineRequiresVertexAndFragmentShader) {
Shader v(kShaderTypeVertex);
Shader f(kShaderTypeFragment);
Shader g(kShaderTypeGeometry);
@@ -235,7 +213,7 @@ TEST_F(AmberScriptPipelineTest,
EXPECT_TRUE(r.IsSuccess()) << r.Error();
}
-TEST_F(AmberScriptPipelineTest, GraphicsPipelineMissingFragmentShader) {
+TEST_F(PipelineTest, GraphicsPipelineMissingFragmentShader) {
Shader v(kShaderTypeVertex);
Shader g(kShaderTypeGeometry);
@@ -254,7 +232,7 @@ TEST_F(AmberScriptPipelineTest, GraphicsPipelineMissingFragmentShader) {
EXPECT_EQ("graphics pipeline requires a fragment shader", r.Error());
}
-TEST_F(AmberScriptPipelineTest, GraphicsPipelineMissingVertexShader) {
+TEST_F(PipelineTest, GraphicsPipelineMissingVertexShader) {
Shader f(kShaderTypeFragment);
Shader g(kShaderTypeGeometry);
@@ -273,8 +251,7 @@ TEST_F(AmberScriptPipelineTest, GraphicsPipelineMissingVertexShader) {
EXPECT_EQ("graphics pipeline requires a vertex shader", r.Error());
}
-TEST_F(AmberScriptPipelineTest,
- GraphicsPipelineMissingVertexAndFragmentShader) {
+TEST_F(PipelineTest, GraphicsPipelineMissingVertexAndFragmentShader) {
Shader g(kShaderTypeGeometry);
Pipeline p(PipelineType::kGraphics);
@@ -290,7 +267,7 @@ TEST_F(AmberScriptPipelineTest,
r.Error());
}
-TEST_F(AmberScriptPipelineTest, GraphicsPipelineWihoutShaders) {
+TEST_F(PipelineTest, GraphicsPipelineWihoutShaders) {
Pipeline p(PipelineType::kGraphics);
SetupColorAttachment(&p, 0);
SetupDepthAttachment(&p);
@@ -301,7 +278,7 @@ TEST_F(AmberScriptPipelineTest, GraphicsPipelineWihoutShaders) {
r.Error());
}
-TEST_F(AmberScriptPipelineTest, ComputePipelineRequiresComputeShader) {
+TEST_F(PipelineTest, ComputePipelineRequiresComputeShader) {
Shader c(kShaderTypeCompute);
Pipeline p(PipelineType::kCompute);
@@ -315,7 +292,7 @@ TEST_F(AmberScriptPipelineTest, ComputePipelineRequiresComputeShader) {
EXPECT_TRUE(r.IsSuccess()) << r.Error();
}
-TEST_F(AmberScriptPipelineTest, ComputePipelineWithoutShader) {
+TEST_F(PipelineTest, ComputePipelineWithoutShader) {
Pipeline p(PipelineType::kCompute);
SetupColorAttachment(&p, 0);
SetupDepthAttachment(&p);
@@ -325,7 +302,7 @@ TEST_F(AmberScriptPipelineTest, ComputePipelineWithoutShader) {
EXPECT_EQ("compute pipeline requires a compute shader", r.Error());
}
-TEST_F(AmberScriptPipelineTest, SetEntryPointForMissingShader) {
+TEST_F(PipelineTest, SetEntryPointForMissingShader) {
Shader c(kShaderTypeCompute);
c.SetName("my_shader");
@@ -335,14 +312,14 @@ TEST_F(AmberScriptPipelineTest, SetEntryPointForMissingShader) {
EXPECT_EQ("unknown shader specified for entry point: my_shader", r.Error());
}
-TEST_F(AmberScriptPipelineTest, SetEntryPointForNullShader) {
+TEST_F(PipelineTest, SetEntryPointForNullShader) {
Pipeline p(PipelineType::kCompute);
Result r = p.SetShaderEntryPoint(nullptr, "test");
EXPECT_FALSE(r.IsSuccess());
EXPECT_EQ("invalid shader specified for entry point", r.Error());
}
-TEST_F(AmberScriptPipelineTest, SetBlankEntryPoint) {
+TEST_F(PipelineTest, SetBlankEntryPoint) {
Shader c(kShaderTypeCompute);
Pipeline p(PipelineType::kCompute);
Result r = p.AddShader(&c, kShaderTypeCompute);
@@ -353,7 +330,7 @@ TEST_F(AmberScriptPipelineTest, SetBlankEntryPoint) {
EXPECT_EQ("entry point should not be blank", r.Error());
}
-TEST_F(AmberScriptPipelineTest, ShaderDefaultEntryPoint) {
+TEST_F(PipelineTest, ShaderDefaultEntryPoint) {
Shader c(kShaderTypeCompute);
Pipeline p(PipelineType::kCompute);
Result r = p.AddShader(&c, kShaderTypeCompute);
@@ -364,7 +341,7 @@ TEST_F(AmberScriptPipelineTest, ShaderDefaultEntryPoint) {
EXPECT_EQ("main", shaders[0].GetEntryPoint());
}
-TEST_F(AmberScriptPipelineTest, SetShaderEntryPoint) {
+TEST_F(PipelineTest, SetShaderEntryPoint) {
Shader c(kShaderTypeCompute);
Pipeline p(PipelineType::kCompute);
Result r = p.AddShader(&c, kShaderTypeCompute);
@@ -378,7 +355,7 @@ TEST_F(AmberScriptPipelineTest, SetShaderEntryPoint) {
EXPECT_EQ("my_main", shaders[0].GetEntryPoint());
}
-TEST_F(AmberScriptPipelineTest, SetEntryPointMulitpleTimes) {
+TEST_F(PipelineTest, SetEntryPointMulitpleTimes) {
Shader c(kShaderTypeCompute);
Pipeline p(PipelineType::kCompute);
Result r = p.AddShader(&c, kShaderTypeCompute);
@@ -392,4 +369,65 @@ TEST_F(AmberScriptPipelineTest, SetEntryPointMulitpleTimes) {
EXPECT_EQ("multiple entry points given for the same shader", r.Error());
}
+TEST_F(PipelineTest, Clone) {
+ Pipeline p(PipelineType::kGraphics);
+ p.SetName("my_pipeline");
+ p.SetFramebufferWidth(800);
+ p.SetFramebufferHeight(600);
+
+ SetupColorAttachment(&p, 0);
+ SetupDepthAttachment(&p);
+
+ Shader f(kShaderTypeFragment);
+ p.AddShader(&f, kShaderTypeFragment);
+ Shader v(kShaderTypeVertex);
+ p.AddShader(&v, kShaderTypeVertex);
+ p.SetShaderEntryPoint(&v, "my_main");
+
+ auto vtex_buf = MakeUnique<DataBuffer>(BufferType::kVertex);
+ vtex_buf->SetName("vertex_buffer");
+ p.AddVertexBuffer(vtex_buf.get(), 1);
+
+ auto idx_buf = MakeUnique<DataBuffer>(BufferType::kIndex);
+ idx_buf->SetName("Index Buffer");
+ p.SetIndexBuffer(idx_buf.get());
+
+ auto buf1 = MakeUnique<DataBuffer>(BufferType::kStorage);
+ buf1->SetName("buf1");
+ p.AddBuffer(buf1.get(), 1, 1);
+
+ auto buf2 = MakeUnique<DataBuffer>(BufferType::kStorage);
+ buf2->SetName("buf2");
+ p.AddBuffer(buf2.get(), 1, 2);
+
+ auto clone = p.Clone();
+ EXPECT_EQ("", clone->GetName());
+ EXPECT_EQ(800U, clone->GetFramebufferWidth());
+ EXPECT_EQ(600U, clone->GetFramebufferHeight());
+
+ auto shaders = clone->GetShaders();
+ ASSERT_EQ(2U, shaders.size());
+ EXPECT_EQ(kShaderTypeFragment, shaders[0].GetShaderType());
+ EXPECT_EQ(kShaderTypeVertex, shaders[1].GetShaderType());
+ EXPECT_EQ("my_main", shaders[1].GetEntryPoint());
+
+ ASSERT_TRUE(clone->GetIndexBuffer() != nullptr);
+ EXPECT_EQ("Index Buffer", clone->GetIndexBuffer()->GetName());
+
+ auto vtex_buffers = clone->GetVertexBuffers();
+ ASSERT_EQ(1U, vtex_buffers.size());
+ EXPECT_EQ(1, vtex_buffers[0].location);
+ EXPECT_EQ("vertex_buffer", vtex_buffers[0].buffer->GetName());
+
+ auto bufs = clone->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
+ EXPECT_EQ("buf1", bufs[0].buffer->GetName());
+ EXPECT_EQ(1U, bufs[0].descriptor_set);
+ EXPECT_EQ(1U, bufs[0].binding);
+
+ EXPECT_EQ("buf2", bufs[1].buffer->GetName());
+ EXPECT_EQ(1U, bufs[1].descriptor_set);
+ EXPECT_EQ(2U, bufs[1].binding);
+}
+
} // namespace amber
diff --git a/src/vkscript/command_parser.cc b/src/vkscript/command_parser.cc
index d4f7625..e0b3292 100644
--- a/src/vkscript/command_parser.cc
+++ b/src/vkscript/command_parser.cc
@@ -595,7 +595,7 @@ Result CommandParser::ProcessSSBO() {
b->SetName("AutoBuf-" + std::to_string(script_->GetBuffers().size()));
buffer = b.get();
script_->AddBuffer(std::move(b));
- pipeline_->AddBuffer(buffer, set, binding, 0);
+ pipeline_->AddBuffer(buffer, set, binding);
}
cmd->SetBuffer(buffer);
}
@@ -727,7 +727,7 @@ Result CommandParser::ProcessUniform() {
b->SetName("AutoBuf-" + std::to_string(script_->GetBuffers().size()));
buffer = b.get();
script_->AddBuffer(std::move(b));
- pipeline_->AddBuffer(buffer, set, binding, 0);
+ pipeline_->AddBuffer(buffer, set, binding);
}
cmd->SetBuffer(buffer);
}
diff --git a/src/vkscript/parser.cc b/src/vkscript/parser.cc
index deab09b..cda97d4 100644
--- a/src/vkscript/parser.cc
+++ b/src/vkscript/parser.cc
@@ -367,10 +367,11 @@ Result Parser::ProcessVertexDataBlock(const SectionParser::Section& section) {
auto& value_data = values[j];
if (header.format->GetPackSize() > 0) {
- if (!token->IsHex())
+ if (!token->IsHex()) {
return Result(
make_error(tokenizer, "Invalid packed value in Vertex Data: " +
token->ToOriginalString()));
+ }
Value v;
v.SetIntValue(token->AsHex());
@@ -379,9 +380,10 @@ Result Parser::ProcessVertexDataBlock(const SectionParser::Section& section) {
auto& comps = header.format->GetComponents();
for (size_t i = 0; i < comps.size();
++i, token = tokenizer.NextToken()) {
- if (token->IsEOS() || token->IsEOL())
+ if (token->IsEOS() || token->IsEOL()) {
return Result(make_error(tokenizer,
"Too few cells in given vertex data row"));
+ }
auto& comp = comps[i];
diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc
index 64df5bb..dc10f01 100644
--- a/src/vulkan/engine_vulkan.cc
+++ b/src/vulkan/engine_vulkan.cc
@@ -212,7 +212,7 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
}
for (const auto& buf_info : pipeline->GetBuffers()) {
- BufferCommand::BufferType type = BufferCommand::BufferType::kSSBO;
+ auto type = BufferCommand::BufferType::kSSBO;
if (buf_info.buffer->GetBufferType() == BufferType::kUniform) {
type = BufferCommand::BufferType::kUniform;
} else if (buf_info.buffer->GetBufferType() == BufferType::kPushConstant) {
diff --git a/src/vulkan/graphics_pipeline.cc b/src/vulkan/graphics_pipeline.cc
index 7536ee1..81b9bc9 100644
--- a/src/vulkan/graphics_pipeline.cc
+++ b/src/vulkan/graphics_pipeline.cc
@@ -785,6 +785,8 @@ Result GraphicsPipeline::ClearBuffer(const VkClearValue& clear_value,
return cmd_buf_guard.GetResult();
frame_->ChangeFrameToWriteLayout(GetCommandBuffer());
+ frame_->CopyBuffersToImages();
+ frame_->TransferColorImagesToDevice(GetCommandBuffer());
{
RenderPassGuard render_pass_guard(this);
@@ -846,6 +848,8 @@ Result GraphicsPipeline::Draw(const DrawArraysCommand* command,
return r;
frame_->ChangeFrameToWriteLayout(GetCommandBuffer());
+ frame_->CopyBuffersToImages();
+ frame_->TransferColorImagesToDevice(GetCommandBuffer());
{
RenderPassGuard render_pass_guard(this);
diff --git a/tests/cases/draw_rectangles.amber b/tests/cases/draw_rectangles.amber
new file mode 100644
index 0000000..9e2e3cb
--- /dev/null
+++ b/tests/cases/draw_rectangles.amber
@@ -0,0 +1,80 @@
+#!amber
+#
+# 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
+#
+# https://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 implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DEVICE_FEATURE vertexPipelineStoresAndAtomics
+
+SHADER vertex vtex_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 0) buffer block1 {
+ vec4 in_color;
+};
+
+void main() {
+ gl_Position = position;
+ frag_color = in_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+BUFFER data_buf1 DATA_TYPE vec4<float> DATA 1 0 0 1 END
+BUFFER data_buf2 DATA_TYPE vec4<float> DATA 0 1 0 1 END
+BUFFER data_buf3 DATA_TYPE vec4<float> DATA 0 0 1 1 END
+BUFFER data_buf4 DATA_TYPE vec4<float> DATA .5 0 .5 1 END
+
+BUFFER frame FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics pipeline1
+ ATTACH vtex_shader
+ ATTACH frag_shader
+
+ FRAMEBUFFER_SIZE 800 600
+ BIND BUFFER frame AS color LOCATION 0
+ BIND BUFFER data_buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+DERIVE_PIPELINE pipeline2 FROM pipeline1
+ BIND BUFFER data_buf2 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+DERIVE_PIPELINE pipeline3 FROM pipeline1
+ BIND BUFFER data_buf3 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+DERIVE_PIPELINE pipeline4 FROM pipeline1
+ BIND BUFFER data_buf4 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 0 0 SIZE 400 300
+RUN pipeline2 DRAW_RECT POS 0 300 SIZE 400 300
+RUN pipeline3 DRAW_RECT POS 400 0 SIZE 400 300
+RUN pipeline4 DRAW_RECT POS 400 300 SIZE 400 300
+
+EXPECT frame IDX 0 0 SIZE 400 300 EQ_RGBA 255 0 0 255
+EXPECT frame IDX 0 300 SIZE 400 300 EQ_RGBA 0 255 0 255
+EXPECT frame IDX 400 0 SIZE 400 300 EQ_RGBA 0 0 255 255
+EXPECT frame IDX 400 300 SIZE 400 300 EQ_RGBA 128 0 128 255
diff --git a/tools/amber-syntax.vim b/tools/amber-syntax.vim
index e292611..3a6ba9a 100644
--- a/tools/amber-syntax.vim
+++ b/tools/amber-syntax.vim
@@ -36,7 +36,7 @@ syn keyword amberBlockCmd FRAMEBUFFER ENTRY_POINT SHADER_OPTIMIZATION
syn keyword amberBlockCmd FORMAT FRAMEBUFFER_SIZE LOCATION BIND SAMPLER
syn keyword amberBlockCmd VERTEX_DATA INDEX_DATA INDEXED IMAGE_ATTACHMENT
syn keyword amberBlockCmd DEPTH_STENCIL_ATTACHMENT DEVICE_FEATURE TOLERANCE
-syn keyword amberBlockCmd REPEAT COPY
+syn keyword amberBlockCmd REPEAT COPY DERIVE_PIPELINE FROM
syn keyword amberComparator EQ NE LT LE GT GE EQ_RGB EQ_RGBA
diff --git a/tools/amber.sublime-syntax b/tools/amber.sublime-syntax
index 9aee90a..4a9ad3a 100644
--- a/tools/amber.sublime-syntax
+++ b/tools/amber.sublime-syntax
@@ -27,7 +27,9 @@ contexts:
scope: keyword.control.amber
- match: '\b(FORMAT|FRAMEBUFFER_SIZE|BIND|SAMPLER|VERTEX_DATA|INDEX_DATA|INDEXED)\b'
scope: keyword.control.amber
- - match : '\b(IMAGE_ATTACHMENT|DEPTH_STENCIL_ATTACHMENT|LOCATION|DEVICE_FEATURE)\b'
+ - match: '\b(IMAGE_ATTACHMENT|DEPTH_STENCIL_ATTACHMENT|LOCATION|DEVICE_FEATURE)\b'
+ scope: keyword.control.amber
+ - match: '\b(DERIVE_PIPELINE|FROM)\b'
scope: keyword.control.amber
- match : '\b(COPY|TOLERANCE|REPEAT)\b'
scope: keyword.control.amber