aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlkka Saarelainen <ilkka.saarelainen@siru.fi>2021-04-28 15:45:00 +0300
committerGitHub <noreply@github.com>2021-04-28 13:45:00 +0100
commit8797ee109e7a6ea4d1f58f387f757545fa35325b (patch)
tree874e5aa21bad7d724a57b8bb35ebcd84acfe3134
parentf0a16b4615d142ff156942fc32820368bb9d8cf3 (diff)
downloadamber-8797ee109e7a6ea4d1f58f387f757545fa35325b.tar.gz
Support descriptor buffer offset and range (#947)
Descriptor buffers can now be bound with byte offset and range via DESCRIPTOR_OFFSET and DESCRIPTOR_RANGE parameters. Descriptor array elements can now point to same buffer. Fixes problem with dynamic uniform/storage buffers: when a dynamic offset is > 0, VK_WHOLE_SIZE must not be used as buffer range. Fixes #945
-rw-r--r--docs/amber_script.md22
-rw-r--r--src/amberscript/parser.cc52
-rw-r--r--src/amberscript/parser_bind_test.cc257
-rw-r--r--src/command.h12
-rw-r--r--src/pipeline.cc8
-rw-r--r--src/pipeline.h9
-rw-r--r--src/pipeline_test.cc10
-rw-r--r--src/vkscript/command_parser.cc6
-rw-r--r--src/vulkan/buffer_backed_descriptor.cc42
-rw-r--r--src/vulkan/buffer_backed_descriptor.h6
-rw-r--r--src/vulkan/buffer_descriptor.cc76
-rw-r--r--src/vulkan/buffer_descriptor.h22
-rw-r--r--src/vulkan/descriptor.h2
-rw-r--r--src/vulkan/engine_vulkan.cc2
-rw-r--r--src/vulkan/image_descriptor.cc64
-rw-r--r--src/vulkan/image_descriptor.h6
-rw-r--r--src/vulkan/pipeline.cc13
-rw-r--r--src/vulkan/pipeline_test.cc4
-rw-r--r--tests/cases/compute_descriptor_array_ssbo.amber21
-rw-r--r--tests/cases/compute_descriptor_array_storagetexelbuffer.amber23
-rw-r--r--tests/cases/compute_dynamic_buffers.amber9
-rw-r--r--tests/cases/compute_dynamic_buffers_descriptor_array.amber17
-rw-r--r--tests/cases/graphics_descriptor_array_combined_image_sampler.amber13
-rw-r--r--tests/cases/graphics_descriptor_array_sampled_image.amber14
24 files changed, 599 insertions, 111 deletions
diff --git a/docs/amber_script.md b/docs/amber_script.md
index 3d88b5a..295cdc7 100644
--- a/docs/amber_script.md
+++ b/docs/amber_script.md
@@ -500,7 +500,7 @@ The following commands are all specified within the `PIPELINE` command.
# and be less than or equal to maxSubgroupSize
# - MIN to set the required subgroup size to the minSubgroupSize
# - MAX to set the required subgroup size to the maxSubgroupSize
- SUBROUP {name_of_shader}
+ SUBGROUP {name_of_shader}
FULLY_POPULATED {fully_populated_enable}
VARYING_SIZE {varying_size_enable}
REQUIRED_SIZE {subgroup_size}
@@ -565,13 +565,18 @@ All BIND BUFFER and BIND SAMPLER commands below define a descriptor set and bind
These commands can be replaced with BIND BUFFER_ARRAY and BIND SAMPLER_ARRAY commands.
In these cases multiple buffer or sampler names need to be provided, separated by spaces.
This creates a descriptor array of buffers or samplers bound to the same descriptor set
-and binding ID. An array of offsets should be provided via `OFFSET offset1 offset2 ...`
-when using dynamic buffers with BUFFER_ARRAY.
+and binding ID. An array of dynamic offsets should be provided via `OFFSET offset1 offset2 ...`
+when using dynamic buffers with BUFFER_ARRAY. Optional descriptor binding offset(s) and range(s)
+can be defined via `DESCRIPTOR_OFFSET offset1 offset2 ...` and
+`DESCRIPTOR_RANGE range1 range2 ...` when using uniform or storage buffers. Offsets and
+ranges can be used also with dynamic buffers.
```groovy
# Bind the buffer of the given |buffer_type| at the given descriptor set
- # and binding. The buffer will use a start index of 0.
+ # and binding. The buffer will use a byte offset |descriptor_offset|
+ # with range |range|.
BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS {buffer_type} DESCRIPTOR_SET _id_ \
- BINDING _id_
+ BINDING _id_ [ DESCRIPTOR_OFFSET _descriptor_offset_ (default 0) ] \
+ [ DESCRIPTOR_RANGE _range_ (default -1 == VK_WHOLE_SIZE) ]
# Attach |buffer_name| as a storage image. The MIP level will have a base
# value of |level|.
@@ -592,9 +597,12 @@ when using dynamic buffers with BUFFER_ARRAY.
BIND {SAMPLER | SAMPLER_ARRAY} {sampler_name} DESCRIPTOR_SET _id_ BINDING _id_
# Bind |buffer_name| as dynamic uniform/storage buffer at the given descriptor set
- # and binding. The buffer will use a start index of |offset|.
+ # and binding. The buffer will use a byte offset |offset| + |descriptor_offset|
+ # with range |range|.
BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS {uniform_dynamic | storage_dynamic} \
- DESCRIPTOR_SET _id_ BINDING _id_ OFFSET _offset_
+ DESCRIPTOR_SET _id_ BINDING _id_ OFFSET _offset_ \
+ [ DESCRIPTOR_OFFSET _descriptor_offset_ (default 0) ] \
+ [ DESCRIPTOR_RANGE _range_ (default -1 == VK_WHOLE_SIZE) ]
```
```groovy
diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc
index 3b40d00..809baa1 100644
--- a/src/amberscript/parser.cc
+++ b/src/amberscript/parser.cc
@@ -1243,10 +1243,60 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) {
}
}
+ // Set default descriptor buffer offsets to 0 and descriptor buffer
+ // ranges to VK_WHOLE_SIZE (~0ULL).
+ std::vector<uint64_t> descriptor_offsets(buffers.size(), 0);
+ std::vector<uint64_t> descriptor_ranges(buffers.size(), ~0ULL);
+ if (buffer_type == BufferType::kUniformDynamic ||
+ buffer_type == BufferType::kStorageDynamic ||
+ buffer_type == BufferType::kStorage ||
+ buffer_type == BufferType::kUniform) {
+ token = tokenizer_->PeekNextToken();
+ if (token->IsIdentifier() &&
+ token->AsString() == "DESCRIPTOR_OFFSET") {
+ token = tokenizer_->NextToken();
+ for (size_t i = 0; i < buffers.size(); i++) {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger()) {
+ if (i > 0) {
+ return Result(
+ "expecting a DESCRIPTOR_OFFSET value for each buffer in "
+ "the array");
+ } else {
+ return Result(
+ "expecting an integer value for DESCRIPTOR_OFFSET");
+ }
+ }
+ descriptor_offsets[i] = token->AsUint64();
+ }
+ }
+
+ token = tokenizer_->PeekNextToken();
+ if (token->IsIdentifier() &&
+ token->AsString() == "DESCRIPTOR_RANGE") {
+ token = tokenizer_->NextToken();
+ for (size_t i = 0; i < buffers.size(); i++) {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger()) {
+ if (i > 0) {
+ return Result(
+ "expecting a DESCRIPTOR_RANGE value for each buffer in "
+ "the array");
+ } else {
+ return Result(
+ "expecting an integer value for DESCRIPTOR_RANGE");
+ }
+ }
+ descriptor_ranges[i] = token->AsUint64();
+ }
+ }
+ }
+
pipeline->ClearBuffers(descriptor_set, binding);
for (size_t i = 0; i < buffers.size(); i++) {
pipeline->AddBuffer(buffers[i], buffer_type, descriptor_set, binding,
- base_mip_level, dynamic_offsets[i]);
+ base_mip_level, dynamic_offsets[i],
+ descriptor_offsets[i], descriptor_ranges[i]);
}
} else if (token->IsIdentifier() && token->AsString() == "KERNEL") {
token = tokenizer_->NextToken();
diff --git a/src/amberscript/parser_bind_test.cc b/src/amberscript/parser_bind_test.cc
index 64f3948..a39b69e 100644
--- a/src/amberscript/parser_bind_test.cc
+++ b/src/amberscript/parser_bind_test.cc
@@ -1393,11 +1393,53 @@ END
EXPECT_EQ(BufferType::kUniform, bufs[0].type);
EXPECT_EQ(1U, bufs[0].descriptor_set);
EXPECT_EQ(2U, bufs[0].binding);
+ EXPECT_EQ(0U, bufs[0].descriptor_offset);
+ EXPECT_EQ(~0ULL, bufs[0].descriptor_range);
EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location);
EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
bufs[0].buffer->GetFormat()->GetFormatType());
}
+TEST_F(AmberScriptParserTest, BindBufferDescriptorOffsetAndRange) {
+ 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 DESCRIPTOR_OFFSET 256 DESCRIPTOR_RANGE 512
+ BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 3 DESCRIPTOR_OFFSET 256 DESCRIPTOR_RANGE -1
+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(2U, bufs.size());
+ EXPECT_EQ(BufferType::kUniform, bufs[0].type);
+ EXPECT_EQ(1U, bufs[0].descriptor_set);
+ EXPECT_EQ(2U, bufs[0].binding);
+ EXPECT_EQ(256U, bufs[0].descriptor_offset);
+ EXPECT_EQ(512U, bufs[0].descriptor_range);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[0].buffer->GetFormat()->GetFormatType());
+ // Verify the descriptor range from the second buffer.
+ EXPECT_EQ(~0ULL, bufs[1].descriptor_range);
+}
+
TEST_F(AmberScriptParserTest, BindBufferMissingBindingValue) {
std::string in = R"(
SHADER vertex my_shader PASSTHROUGH
@@ -2859,6 +2901,33 @@ END)";
EXPECT_EQ("13: missing BINDING for BIND command", r.Error());
}
+TEST_F(AmberScriptParserTest, BindDescriptorOffsetWithUnsupportedBufferType) {
+ std::string unsupported_buffer_types[] = {"uniform_texel_buffer",
+ "storage_texel_buffer",
+ "sampled_image", "storage_image"};
+
+ for (const auto& buffer_type : unsupported_buffer_types) {
+ std::ostringstream in;
+ in << R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS )"
+ << buffer_type << R"( DESCRIPTOR_SET 0 BINDING 0 DESCRIPTOR_OFFSET 0
+END)";
+ Parser parser;
+ Result r = parser.Parse(in.str());
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: extra parameters after BIND command: DESCRIPTOR_OFFSET",
+ r.Error());
+ }
+}
+
TEST_F(AmberScriptParserTest, BindBufferArray) {
std::string in = R"(
SHADER vertex my_shader PASSTHROUGH
@@ -2891,12 +2960,200 @@ END
EXPECT_EQ(BufferType::kUniform, bufs[i].type);
EXPECT_EQ(1U, bufs[i].descriptor_set);
EXPECT_EQ(2U, bufs[i].binding);
+ EXPECT_EQ(0U, bufs[i].descriptor_offset);
+ EXPECT_EQ(~0ULL, bufs[i].descriptor_range);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[i].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[i].buffer->GetFormat()->GetFormatType());
+ }
+}
+
+TEST_F(AmberScriptParserTest, BindBufferArrayWithDescriptorOffsetAndRange) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf1 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER_ARRAY my_buf1 my_buf1 AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET 256 512 DESCRIPTOR_RANGE 1024 2048
+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(2U, bufs.size());
+ for (size_t i = 0; i < 2; i++) {
+ EXPECT_EQ(BufferType::kUniform, bufs[i].type);
+ EXPECT_EQ(1U, bufs[i].descriptor_set);
+ EXPECT_EQ(2U, bufs[i].binding);
+ EXPECT_EQ(256U * (i + 1), bufs[i].descriptor_offset);
+ EXPECT_EQ(1024U * (i + 1), bufs[i].descriptor_range);
EXPECT_EQ(static_cast<uint32_t>(0), bufs[i].location);
EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
bufs[i].buffer->GetFormat()->GetFormatType());
}
}
+TEST_F(AmberScriptParserTest,
+ BindDynamicBufferArrayWithDescriptorOffsetAndRange) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf1 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER_ARRAY my_buf1 my_buf1 AS uniform_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET 16 32 DESCRIPTOR_OFFSET 256 512 DESCRIPTOR_RANGE 1024 2048
+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(2U, bufs.size());
+ for (size_t i = 0; i < 2; i++) {
+ EXPECT_EQ(BufferType::kUniformDynamic, bufs[i].type);
+ EXPECT_EQ(1U, bufs[i].descriptor_set);
+ EXPECT_EQ(2U, bufs[i].binding);
+ EXPECT_EQ(16U * (i + 1), bufs[i].dynamic_offset);
+ EXPECT_EQ(256U * (i + 1), bufs[i].descriptor_offset);
+ EXPECT_EQ(1024U * (i + 1), bufs[i].descriptor_range);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[i].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[i].buffer->GetFormat()->GetFormatType());
+ }
+}
+
+TEST_F(AmberScriptParserTest, BindBufferArrayOnlyOneDescriptorOffset) {
+ 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_ARRAY my_buf my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET 256
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "13: expecting a DESCRIPTOR_OFFSET value for each buffer in the array",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferArrayOnlyOneDescriptorRange) {
+ 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_ARRAY my_buf my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_RANGE 256
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "13: expecting a DESCRIPTOR_RANGE value for each buffer in the array",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferEmptyDescriptorOffset) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: expecting an integer value for DESCRIPTOR_OFFSET", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferInvalidDescriptorOffset) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_OFFSET foo
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: expecting an integer value for DESCRIPTOR_OFFSET", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferEmptyDescriptorRange) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_RANGE
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: expecting an integer value for DESCRIPTOR_RANGE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferInvalidDescriptorRange) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2 DESCRIPTOR_RANGE foo
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: expecting an integer value for DESCRIPTOR_RANGE", r.Error());
+}
+
TEST_F(AmberScriptParserTest, BindBufferArrayOnlyOneBuffer) {
std::string in = R"(
SHADER vertex my_shader PASSTHROUGH
diff --git a/src/command.h b/src/command.h
index 207c336..26b9dc2 100644
--- a/src/command.h
+++ b/src/command.h
@@ -546,6 +546,16 @@ class BufferCommand : public BindableResourceCommand {
}
uint32_t GetDynamicOffset() const { return dynamic_offset_; }
+ void SetDescriptorOffset(uint64_t descriptor_offset) {
+ descriptor_offset_ = descriptor_offset;
+ }
+ uint64_t GetDescriptorOffset() const { return descriptor_offset_; }
+
+ void SetDescriptorRange(uint64_t descriptor_range) {
+ descriptor_range_ = descriptor_range;
+ }
+ uint64_t GetDescriptorRange() const { return descriptor_range_; }
+
void SetValues(std::vector<Value>&& values) { values_ = std::move(values); }
const std::vector<Value>& GetValues() const { return values_; }
@@ -565,6 +575,8 @@ class BufferCommand : public BindableResourceCommand {
uint32_t offset_ = 0;
uint32_t base_mip_level_ = 0;
uint32_t dynamic_offset_ = 0;
+ uint64_t descriptor_offset_ = 0;
+ uint64_t descriptor_range_ = ~0ULL;
std::vector<Value> values_;
};
diff --git a/src/pipeline.cc b/src/pipeline.cc
index 55c6bae..9b73041 100644
--- a/src/pipeline.cc
+++ b/src/pipeline.cc
@@ -516,7 +516,9 @@ void Pipeline::AddBuffer(Buffer* buf,
uint32_t descriptor_set,
uint32_t binding,
uint32_t base_mip_level,
- uint32_t dynamic_offset) {
+ uint32_t dynamic_offset,
+ uint64_t descriptor_offset,
+ uint64_t descriptor_range) {
buffers_.push_back(BufferInfo{buf});
auto& info = buffers_.back();
@@ -526,6 +528,8 @@ void Pipeline::AddBuffer(Buffer* buf,
info.base_mip_level = base_mip_level;
info.dynamic_offset = dynamic_offset;
info.sampler = buf->GetSampler();
+ info.descriptor_offset = descriptor_offset;
+ info.descriptor_range = descriptor_range;
}
void Pipeline::AddBuffer(Buffer* buf,
@@ -862,7 +866,7 @@ Result Pipeline::GenerateOpenCLPodBuffers() {
opencl_pod_buffer_map_.insert(
buf_iter,
std::make_pair(std::make_pair(descriptor_set, binding), buffer));
- AddBuffer(buffer, buffer_type, descriptor_set, binding, 0, 0);
+ AddBuffer(buffer, buffer_type, descriptor_set, binding, 0, 0, 0, ~0ULL);
} else {
buffer = buf_iter->second;
}
diff --git a/src/pipeline.h b/src/pipeline.h
index ec38a3a..7a80ad4 100644
--- a/src/pipeline.h
+++ b/src/pipeline.h
@@ -203,6 +203,8 @@ class Pipeline {
uint32_t offset = 0;
uint32_t stride = 0;
Sampler* sampler = nullptr;
+ uint64_t descriptor_offset = 0;
+ uint64_t descriptor_range = ~0ULL; // ~0ULL == VK_WHOLE_SIZE
};
/// Information on a sampler attached to the pipeline.
@@ -335,13 +337,16 @@ class Pipeline {
Buffer* GetIndexBuffer() const { return index_buffer_; }
/// Adds |buf| of |type| to the pipeline at the given |descriptor_set|,
- /// |binding|, |base_mip_level|, and |dynamic_offset|.
+ /// |binding|, |base_mip_level|, |descriptor_offset|, |descriptor_range| and
+ /// |dynamic_offset|.
void AddBuffer(Buffer* buf,
BufferType type,
uint32_t descriptor_set,
uint32_t binding,
uint32_t base_mip_level,
- uint32_t dynamic_offset);
+ uint32_t dynamic_offset,
+ uint64_t descriptor_offset,
+ uint64_t descriptor_range);
/// Adds |buf| to the pipeline at the given |arg_name|.
void AddBuffer(Buffer* buf, BufferType type, const std::string& arg_name);
/// Adds |buf| to the pipeline at the given |arg_no|.
diff --git a/src/pipeline_test.cc b/src/pipeline_test.cc
index e16313c..c0d2f67 100644
--- a/src/pipeline_test.cc
+++ b/src/pipeline_test.cc
@@ -262,7 +262,7 @@ TEST_F(PipelineTest, PipelineBufferWithoutFormat) {
auto buf = MakeUnique<Buffer>();
buf->SetName("MyBuffer");
- p.AddBuffer(buf.get(), BufferType::kStorage, 0, 0, 0, 0);
+ p.AddBuffer(buf.get(), BufferType::kStorage, 0, 0, 0, 0, 0, 0);
Result r = p.Validate();
EXPECT_FALSE(r.IsSuccess()) << r.Error();
@@ -365,11 +365,11 @@ TEST_F(PipelineTest, Clone) {
auto buf1 = MakeUnique<Buffer>();
buf1->SetName("buf1");
- p.AddBuffer(buf1.get(), BufferType::kStorage, 1, 1, 0, 0);
+ p.AddBuffer(buf1.get(), BufferType::kStorage, 1, 1, 0, 0, 0, 0);
auto buf2 = MakeUnique<Buffer>();
buf2->SetName("buf2");
- p.AddBuffer(buf2.get(), BufferType::kStorage, 1, 2, 0, 16);
+ p.AddBuffer(buf2.get(), BufferType::kStorage, 1, 2, 0, 16, 256, 512);
auto clone = p.Clone();
EXPECT_EQ("", clone->GetName());
@@ -400,11 +400,15 @@ TEST_F(PipelineTest, Clone) {
EXPECT_EQ(1U, bufs[0].descriptor_set);
EXPECT_EQ(1U, bufs[0].binding);
EXPECT_EQ(0U, bufs[0].dynamic_offset);
+ EXPECT_EQ(0U, bufs[0].descriptor_offset);
+ EXPECT_EQ(0U, bufs[0].descriptor_range);
EXPECT_EQ("buf2", bufs[1].buffer->GetName());
EXPECT_EQ(1U, bufs[1].descriptor_set);
EXPECT_EQ(2U, bufs[1].binding);
EXPECT_EQ(16U, bufs[1].dynamic_offset);
+ EXPECT_EQ(256U, bufs[1].descriptor_offset);
+ EXPECT_EQ(512U, bufs[1].descriptor_range);
}
TEST_F(PipelineTest, OpenCLUpdateBindings) {
diff --git a/src/vkscript/command_parser.cc b/src/vkscript/command_parser.cc
index 0953344..610c7e6 100644
--- a/src/vkscript/command_parser.cc
+++ b/src/vkscript/command_parser.cc
@@ -577,7 +577,8 @@ Result CommandParser::ProcessSSBO() {
buffer = b.get();
script_->AddBuffer(std::move(b));
pipeline_->ClearBuffers(set, binding);
- pipeline_->AddBuffer(buffer, BufferType::kStorage, set, binding, 0, 0);
+ pipeline_->AddBuffer(buffer, BufferType::kStorage, set, binding, 0, 0, 0,
+ ~0ULL);
}
cmd->SetBuffer(buffer);
}
@@ -729,7 +730,8 @@ Result CommandParser::ProcessUniform() {
buffer = b.get();
script_->AddBuffer(std::move(b));
pipeline_->ClearBuffers(set, binding);
- pipeline_->AddBuffer(buffer, BufferType::kUniform, set, binding, 0, 0);
+ pipeline_->AddBuffer(buffer, BufferType::kUniform, set, binding, 0, 0, 0,
+ ~0ULL);
}
cmd->SetBuffer(buffer);
diff --git a/src/vulkan/buffer_backed_descriptor.cc b/src/vulkan/buffer_backed_descriptor.cc
index 2f7aa98..d1b1aed 100644
--- a/src/vulkan/buffer_backed_descriptor.cc
+++ b/src/vulkan/buffer_backed_descriptor.cc
@@ -35,26 +35,18 @@ BufferBackedDescriptor::~BufferBackedDescriptor() = default;
Result BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded(
CommandBuffer* command) {
- auto resources = GetResources();
- auto buffers = GetAmberBuffers();
- if (resources.size() != buffers.size())
- return Result(
- "Vulkan: BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded() "
- "resource and buffer vector sizes are not matching");
-
- for (size_t i = 0; i < resources.size(); i++) {
- if (!buffers[i]->ValuePtr()->empty()) {
- resources[i]->UpdateMemoryWithRawData(*buffers[i]->ValuePtr());
+ for (const auto& resource : GetResources()) {
+ if (!resource.first->ValuePtr()->empty()) {
+ resource.second->UpdateMemoryWithRawData(*resource.first->ValuePtr());
// If the resource is read-only, keep the buffer data; Amber won't copy
// read-only resources back into the host buffers, so it makes sense to
// leave the buffer intact.
if (!IsReadOnly())
- buffers[i]->ValuePtr()->clear();
+ resource.first->ValuePtr()->clear();
}
- resources[i]->CopyToDevice(command);
+ resource.second->CopyToDevice(command);
}
-
return {};
}
@@ -67,7 +59,7 @@ Result BufferBackedDescriptor::RecordCopyDataToHost(CommandBuffer* command) {
}
for (const auto& r : GetResources())
- r->CopyToHost(command);
+ r.second->CopyToHost(command);
}
return {};
@@ -79,11 +71,6 @@ Result BufferBackedDescriptor::MoveResourceToBufferOutput() {
return {};
auto resources = GetResources();
- auto buffers = GetAmberBuffers();
- if (resources.size() != buffers.size())
- return Result(
- "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() resource "
- "and buffer vector sizes are not matching");
if (resources.empty()) {
return Result(
@@ -91,26 +78,25 @@ Result BufferBackedDescriptor::MoveResourceToBufferOutput() {
"transfer resource");
}
- for (size_t i = 0; i < resources.size(); i++) {
- void* resource_memory_ptr = resources[i]->HostAccessibleMemoryPtr();
+ for (const auto& resource : resources) {
+ void* resource_memory_ptr = resource.second->HostAccessibleMemoryPtr();
+ auto* buffer = resource.first;
if (!resource_memory_ptr) {
return Result(
"Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() "
"no host accessible memory pointer");
}
- if (!buffers[i]->ValuePtr()->empty()) {
+ if (!buffer->ValuePtr()->empty()) {
return Result(
"Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() "
"output buffer is not empty");
}
- auto size_in_bytes = resources[i]->GetSizeInBytes();
- buffers[i]->SetElementCount(size_in_bytes /
- buffers[i]->GetFormat()->SizeInBytes());
- buffers[i]->ValuePtr()->resize(size_in_bytes);
- std::memcpy(buffers[i]->ValuePtr()->data(), resource_memory_ptr,
- size_in_bytes);
+ auto size_in_bytes = resource.second->GetSizeInBytes();
+ buffer->SetElementCount(size_in_bytes / buffer->GetFormat()->SizeInBytes());
+ buffer->ValuePtr()->resize(size_in_bytes);
+ std::memcpy(buffer->ValuePtr()->data(), resource_memory_ptr, size_in_bytes);
}
return {};
diff --git a/src/vulkan/buffer_backed_descriptor.h b/src/vulkan/buffer_backed_descriptor.h
index c626dd8..8f9df41 100644
--- a/src/vulkan/buffer_backed_descriptor.h
+++ b/src/vulkan/buffer_backed_descriptor.h
@@ -16,6 +16,7 @@
#define SRC_VULKAN_BUFFER_BACKED_DESCRIPTOR_H_
#include <memory>
+#include <utility>
#include <vector>
#include "amber/result.h"
@@ -51,7 +52,10 @@ class BufferBackedDescriptor : public Descriptor {
bool IsReadOnly() const;
protected:
- virtual std::vector<Resource*> GetResources() = 0;
+ /// Returns a list of unique transfer buffer resources. Note that this list
+ /// may contain less items than the |amber_buffers| vector contains if two or
+ /// more amber buffers use same Vulkan buffer.
+ virtual std::vector<std::pair<Buffer*, Resource*>> GetResources() = 0;
private:
std::vector<Buffer*> amber_buffers_;
diff --git a/src/vulkan/buffer_descriptor.cc b/src/vulkan/buffer_descriptor.cc
index 510565d..aa8b2dd 100644
--- a/src/vulkan/buffer_descriptor.cc
+++ b/src/vulkan/buffer_descriptor.cc
@@ -14,6 +14,8 @@
#include "src/vulkan/buffer_descriptor.h"
+#include <algorithm>
+#include <utility>
#include <vector>
#include "src/make_unique.h"
@@ -39,17 +41,23 @@ Result BufferDescriptor::CreateResourceIfNeeded() {
"only when |transfer_buffers| is empty");
}
- transfer_buffers_.reserve(GetAmberBuffers().size());
+ descriptor_offsets_.reserve(GetAmberBuffers().size());
+ descriptor_ranges_.reserve(GetAmberBuffers().size());
for (const auto& amber_buffer : GetAmberBuffers()) {
if (amber_buffer->ValuePtr()->empty())
continue;
- uint32_t size_in_bytes =
- amber_buffer ? static_cast<uint32_t>(amber_buffer->ValuePtr()->size())
- : 0;
- transfer_buffers_.emplace_back(MakeUnique<TransferBuffer>(
- device_, size_in_bytes, amber_buffer->GetFormat()));
+ // Check if the transfer buffer is already created.
+ if (transfer_buffers_.count(amber_buffer) > 0)
+ continue;
+
+ auto size_in_bytes =
+ static_cast<uint32_t>(amber_buffer->ValuePtr()->size());
+
+ auto transfer_buffer = MakeUnique<TransferBuffer>(
+ device_, size_in_bytes, amber_buffer->GetFormat());
+
VkBufferUsageFlags flags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
if (IsUniformBuffer() || IsUniformBufferDynamic()) {
@@ -64,9 +72,10 @@ Result BufferDescriptor::CreateResourceIfNeeded() {
return Result("Unexpected buffer type when deciding usage flags");
}
- Result r = transfer_buffers_.back()->Initialize(flags);
+ Result r = transfer_buffer->Initialize(flags);
if (!r.IsSuccess())
return r;
+ transfer_buffers_[amber_buffer] = std::move(transfer_buffer);
}
is_descriptor_set_update_needed_ = true;
@@ -89,12 +98,33 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded(
std::vector<VkDescriptorBufferInfo> buffer_infos;
std::vector<VkBufferView> buffer_views;
- for (const auto& buffer : transfer_buffers_) {
- VkDescriptorBufferInfo buffer_info;
- buffer_info.buffer = buffer->GetVkBuffer();
- buffer_info.offset = 0;
- buffer_info.range = VK_WHOLE_SIZE;
- buffer_infos.push_back(buffer_info);
+ // Create VkDescriptorBufferInfo for every descriptor buffer.
+ for (uint32_t i = 0; i < GetAmberBuffers().size(); i++) {
+ const auto& buffer = transfer_buffers_[GetAmberBuffers()[i]];
+ assert(buffer->GetVkBuffer() && "Unexpected descriptor type");
+ // Add buffer infos for uniform and storage buffers.
+ if (IsUniformBuffer() || IsUniformBufferDynamic() || IsStorageBuffer() ||
+ IsStorageBufferDynamic()) {
+ uint64_t range = descriptor_ranges_[i];
+ // If dynamic offset is used, we must change range with VK_WHOLE_SIZE to
+ // an actual range.
+ // From vulkan spec: For each dynamic uniform or storage buffer binding in
+ // pDescriptorSets, the sum of the effective offset, as defined above, and
+ // the range of the binding must be less than or equal to the size of the
+ // buffer.
+ if ((IsUniformBufferDynamic() || IsStorageBufferDynamic()) &&
+ descriptor_ranges_[i] == VK_WHOLE_SIZE) {
+ range = buffer->GetSizeInBytes() - dynamic_offsets_[i] -
+ descriptor_offsets_[i];
+ }
+
+ VkDescriptorBufferInfo buffer_info;
+ buffer_info.buffer = buffer->GetVkBuffer();
+ buffer_info.offset = descriptor_offsets_[i];
+ buffer_info.range = range;
+
+ buffer_infos.push_back(buffer_info);
+ }
if (IsUniformTexelBuffer() || IsStorageTexelBuffer()) {
buffer_views.push_back(*buffer->GetVkBufferView());
@@ -106,7 +136,7 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded(
write.dstSet = descriptor_set;
write.dstBinding = binding_;
write.dstArrayElement = 0;
- write.descriptorCount = static_cast<uint32_t>(buffer_infos.size());
+ write.descriptorCount = static_cast<uint32_t>(GetAmberBuffers().size());
write.descriptorType = GetVkDescriptorType();
write.pBufferInfo = buffer_infos.data();
write.pTexelBufferView = buffer_views.data();
@@ -116,10 +146,20 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded(
is_descriptor_set_update_needed_ = false;
}
-std::vector<Resource*> BufferDescriptor::GetResources() {
- std::vector<Resource*> ret;
- for (auto& b : transfer_buffers_) {
- ret.push_back(b.get());
+std::vector<std::pair<Buffer*, Resource*>> BufferDescriptor::GetResources() {
+ std::vector<std::pair<Buffer*, Resource*>> ret;
+ // Add unique amber buffers and related transfer buffers to the vector.
+ for (const auto& amber_buffer : GetAmberBuffers()) {
+ // Skip duplicate values.
+ const auto& image =
+ std::find_if(ret.begin(), ret.end(),
+ [&](const std::pair<Buffer*, Resource*>& buffer) {
+ return buffer.first == amber_buffer;
+ });
+ if (image != ret.end())
+ continue;
+
+ ret.emplace_back(amber_buffer, transfer_buffers_[amber_buffer].get());
}
return ret;
}
diff --git a/src/vulkan/buffer_descriptor.h b/src/vulkan/buffer_descriptor.h
index d481ae5..2c3f390 100644
--- a/src/vulkan/buffer_descriptor.h
+++ b/src/vulkan/buffer_descriptor.h
@@ -16,6 +16,8 @@
#define SRC_VULKAN_BUFFER_DESCRIPTOR_H_
#include <memory>
+#include <unordered_map>
+#include <utility>
#include <vector>
#include "amber/result.h"
@@ -50,14 +52,30 @@ class BufferDescriptor : public BufferBackedDescriptor {
return dynamic_offsets_;
}
void AddDynamicOffset(uint32_t offset) { dynamic_offsets_.push_back(offset); }
+ std::vector<uint64_t> GetDescriptorOffsets() override {
+ return descriptor_offsets_;
+ }
+ void AddDescriptorOffset(uint64_t descriptor_offset) {
+ descriptor_offsets_.push_back(descriptor_offset);
+ }
+ std::vector<uint64_t> GetDescriptorRanges() override {
+ return descriptor_ranges_;
+ }
+ void AddDescriptorRange(uint64_t descriptor_range) {
+ descriptor_ranges_.push_back(descriptor_range);
+ }
+
BufferDescriptor* AsBufferDescriptor() override { return this; }
protected:
- std::vector<Resource*> GetResources() override;
+ std::vector<std::pair<Buffer*, Resource*>> GetResources() override;
private:
- std::vector<std::unique_ptr<TransferBuffer>> transfer_buffers_;
+ std::unordered_map<Buffer*, std::unique_ptr<TransferBuffer>>
+ transfer_buffers_;
std::vector<uint32_t> dynamic_offsets_;
+ std::vector<VkDeviceSize> descriptor_offsets_;
+ std::vector<VkDeviceSize> descriptor_ranges_;
};
} // namespace vulkan
diff --git a/src/vulkan/descriptor.h b/src/vulkan/descriptor.h
index ba702ff..03434a3 100644
--- a/src/vulkan/descriptor.h
+++ b/src/vulkan/descriptor.h
@@ -64,6 +64,8 @@ class Descriptor {
virtual Result AddToBuffer(const std::vector<Value>&, uint32_t) { return {}; }
virtual uint32_t GetDescriptorCount() { return 1; }
virtual std::vector<uint32_t> GetDynamicOffsets() { return {}; }
+ virtual std::vector<VkDeviceSize> GetDescriptorOffsets() { return {}; }
+ virtual std::vector<VkDeviceSize> GetDescriptorRanges() { return {}; }
virtual BufferDescriptor* AsBufferDescriptor() { return nullptr; }
virtual BufferBackedDescriptor* AsBufferBackedDescriptor() { return nullptr; }
virtual SamplerDescriptor* AsSamplerDescriptor() { return nullptr; }
diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc
index 536993f..6bed716 100644
--- a/src/vulkan/engine_vulkan.cc
+++ b/src/vulkan/engine_vulkan.cc
@@ -254,6 +254,8 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
cmd->SetBinding(buf_info.binding);
cmd->SetBaseMipLevel(buf_info.base_mip_level);
cmd->SetDynamicOffset(buf_info.dynamic_offset);
+ cmd->SetDescriptorOffset(buf_info.descriptor_offset);
+ cmd->SetDescriptorRange(buf_info.descriptor_range);
cmd->SetBuffer(buf_info.buffer);
cmd->SetSampler(buf_info.sampler);
diff --git a/src/vulkan/image_descriptor.cc b/src/vulkan/image_descriptor.cc
index 550ce0b..94e67d0 100644
--- a/src/vulkan/image_descriptor.cc
+++ b/src/vulkan/image_descriptor.cc
@@ -14,6 +14,10 @@
#include "src/vulkan/image_descriptor.h"
+#include <algorithm>
+#include <unordered_map>
+#include <utility>
+
#include "src/vulkan/device.h"
#include "src/vulkan/resource.h"
@@ -34,9 +38,12 @@ ImageDescriptor::~ImageDescriptor() = default;
Result ImageDescriptor::RecordCopyDataToResourceIfNeeded(
CommandBuffer* command) {
- for (auto& image : transfer_images_) {
- image->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- VK_PIPELINE_STAGE_TRANSFER_BIT);
+ const auto transfer_images = GetResources();
+ for (const auto& image : transfer_images) {
+ // Static cast is safe, because the type is known to be TransferImage*.
+ static_cast<TransferImage*>(image.second)
+ ->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT);
}
Result r = BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded(command);
@@ -44,9 +51,10 @@ Result ImageDescriptor::RecordCopyDataToResourceIfNeeded(
return r;
// Just do this as early as possible.
- for (auto& image : transfer_images_) {
- image->ImageBarrier(command, VK_IMAGE_LAYOUT_GENERAL,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
+ for (const auto& image : transfer_images) {
+ static_cast<TransferImage*>(image.second)
+ ->ImageBarrier(command, VK_IMAGE_LAYOUT_GENERAL,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
}
return {};
@@ -59,12 +67,14 @@ Result ImageDescriptor::CreateResourceIfNeeded() {
"only when |transfer_images| is empty");
}
- transfer_images_.reserve(GetAmberBuffers().size());
-
for (const auto& amber_buffer : GetAmberBuffers()) {
if (amber_buffer->ValuePtr()->empty())
continue;
+ // Check if the transfer image is already created.
+ if (transfer_images_.count(amber_buffer) > 0)
+ continue;
+
// Default to 2D image.
VkImageType image_type = VK_IMAGE_TYPE_2D;
switch (amber_buffer->GetImageDimension()) {
@@ -93,11 +103,11 @@ Result ImageDescriptor::CreateResourceIfNeeded() {
aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}
- transfer_images_.emplace_back(MakeUnique<TransferImage>(
+ auto transfer_image = MakeUnique<TransferImage>(
device_, *fmt, aspect, image_type, amber_buffer->GetWidth(),
amber_buffer->GetHeight(), amber_buffer->GetDepth(),
amber_buffer->GetMipLevels(), base_mip_level_, VK_REMAINING_MIP_LEVELS,
- amber_buffer->GetSamples()));
+ amber_buffer->GetSamples());
VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
@@ -109,10 +119,11 @@ Result ImageDescriptor::CreateResourceIfNeeded() {
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
- Result r = transfer_images_.back()->Initialize(usage);
-
+ Result r = transfer_image->Initialize(usage);
if (!r.IsSuccess())
return r;
+
+ transfer_images_[amber_buffer] = std::move(transfer_image);
}
if (amber_sampler_) {
@@ -127,9 +138,10 @@ Result ImageDescriptor::CreateResourceIfNeeded() {
Result ImageDescriptor::RecordCopyDataToHost(CommandBuffer* command) {
if (!IsReadOnly()) {
- for (auto& image : transfer_images_) {
- image->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- VK_PIPELINE_STAGE_TRANSFER_BIT);
+ for (auto& image : GetResources()) {
+ static_cast<TransferImage*>(image.second)
+ ->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT);
}
BufferBackedDescriptor::RecordCopyDataToHost(command);
@@ -156,7 +168,9 @@ void ImageDescriptor::UpdateDescriptorSetIfNeeded(
std::vector<VkDescriptorImageInfo> image_infos;
- for (const auto& image : transfer_images_) {
+ // Create VkDescriptorImageInfo for every descriptor image.
+ for (const auto& amber_buffer : GetAmberBuffers()) {
+ const auto& image = transfer_images_[amber_buffer];
VkDescriptorImageInfo image_info = {vulkan_sampler_.GetVkSampler(),
image->GetVkImageView(), layout};
image_infos.push_back(image_info);
@@ -177,10 +191,20 @@ void ImageDescriptor::UpdateDescriptorSetIfNeeded(
is_descriptor_set_update_needed_ = false;
}
-std::vector<Resource*> ImageDescriptor::GetResources() {
- std::vector<Resource*> ret;
- for (auto& i : transfer_images_) {
- ret.push_back(i.get());
+std::vector<std::pair<Buffer*, Resource*>> ImageDescriptor::GetResources() {
+ std::vector<std::pair<Buffer*, Resource*>> ret;
+ // Add unique amber buffers and related transfer images to the vector.
+ for (const auto& amber_buffer : GetAmberBuffers()) {
+ // Skip duplicate values.
+ const auto& image =
+ std::find_if(ret.begin(), ret.end(),
+ [&](const std::pair<Buffer*, Resource*>& buffer) {
+ return buffer.first == amber_buffer;
+ });
+ if (image != ret.end())
+ continue;
+
+ ret.emplace_back(amber_buffer, transfer_images_[amber_buffer].get());
}
return ret;
}
diff --git a/src/vulkan/image_descriptor.h b/src/vulkan/image_descriptor.h
index 5f8dd0e..908ed17 100644
--- a/src/vulkan/image_descriptor.h
+++ b/src/vulkan/image_descriptor.h
@@ -16,6 +16,8 @@
#define SRC_VULKAN_IMAGE_DESCRIPTOR_H_
#include <memory>
+#include <unordered_map>
+#include <utility>
#include <vector>
#include "src/vulkan/buffer_backed_descriptor.h"
@@ -43,11 +45,11 @@ class ImageDescriptor : public BufferBackedDescriptor {
void SetAmberSampler(amber::Sampler* sampler) { amber_sampler_ = sampler; }
protected:
- std::vector<Resource*> GetResources() override;
+ std::vector<std::pair<Buffer*, Resource*>> GetResources() override;
private:
uint32_t base_mip_level_ = 0;
- std::vector<std::unique_ptr<TransferImage>> transfer_images_;
+ std::unordered_map<Buffer*, std::unique_ptr<TransferImage>> transfer_images_;
amber::Sampler* amber_sampler_ = nullptr;
amber::vulkan::Sampler vulkan_sampler_;
};
diff --git a/src/vulkan/pipeline.cc b/src/vulkan/pipeline.cc
index e0b875c..259f375 100644
--- a/src/vulkan/pipeline.cc
+++ b/src/vulkan/pipeline.cc
@@ -322,6 +322,7 @@ Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) {
cmd->GetDescriptorSet(), cmd->GetBinding());
if (cmd->IsCombinedImageSampler())
image_desc->SetAmberSampler(cmd->GetSampler());
+
descriptors.push_back(std::move(image_desc));
} else {
auto buffer_desc = MakeUnique<BufferDescriptor>(
@@ -336,18 +337,18 @@ Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) {
"Descriptors bound to the same binding needs to have matching "
"descriptor types");
}
- // Check that the buffer is not added already.
- const auto& buffers = desc->AsBufferBackedDescriptor()->GetAmberBuffers();
- if (std::find(buffers.begin(), buffers.end(), cmd->GetBuffer()) !=
- buffers.end()) {
- return Result("Buffer has been added already");
- }
desc->AsBufferBackedDescriptor()->AddAmberBuffer(cmd->GetBuffer());
}
if (cmd->IsUniformDynamic() || cmd->IsSSBODynamic())
desc->AsBufferDescriptor()->AddDynamicOffset(cmd->GetDynamicOffset());
+ if (cmd->IsUniform() || cmd->IsUniformDynamic() || cmd->IsSSBO() ||
+ cmd->IsSSBODynamic()) {
+ desc->AsBufferDescriptor()->AddDescriptorOffset(cmd->GetDescriptorOffset());
+ desc->AsBufferDescriptor()->AddDescriptorRange(cmd->GetDescriptorRange());
+ }
+
if (cmd->IsSSBO() && !desc->IsStorageBuffer()) {
return Result(
"Vulkan::AddBufferDescriptor BufferCommand for SSBO uses wrong "
diff --git a/src/vulkan/pipeline_test.cc b/src/vulkan/pipeline_test.cc
index 53eca44..9f60174 100644
--- a/src/vulkan/pipeline_test.cc
+++ b/src/vulkan/pipeline_test.cc
@@ -44,9 +44,9 @@ TEST_F(VulkanPipelineTest, AddBufferDescriptorAddBufferTwice) {
&amber_pipeline);
Result r = pipeline.AddBufferDescriptor(cmd.get());
ASSERT_TRUE(r.IsSuccess()) << r.Error();
- // Adding same buffer again should fail.
+ // Adding same buffer again shouldn't fail.
r = pipeline.AddBufferDescriptor(cmd.get());
- ASSERT_FALSE(r.IsSuccess());
+ ASSERT_TRUE(r.IsSuccess());
}
} // namespace vulkan
diff --git a/tests/cases/compute_descriptor_array_ssbo.amber b/tests/cases/compute_descriptor_array_ssbo.amber
index bd4c773..0eb6b7d 100644
--- a/tests/cases/compute_descriptor_array_ssbo.amber
+++ b/tests/cases/compute_descriptor_array_ssbo.amber
@@ -20,12 +20,19 @@ layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(binding = 0) buffer block0
{
int data;
-} ssbo_array[2];
+} ssbo_array_0[2];
+
+layout(binding = 1) buffer block1
+{
+ int data;
+} ssbo_array_1[2];
void main()
{
- ssbo_array[0].data = 1;
- ssbo_array[1].data = 2;
+ ssbo_array_0[0].data = 1;
+ ssbo_array_0[1].data = 2;
+ ssbo_array_1[0].data = 3;
+ ssbo_array_1[1].data = 4;
}
END
@@ -37,13 +44,21 @@ BUFFER buf1 DATA_TYPE int32 DATA
0
END
+# The Vulkan spec lists the maximum value of minStorageBufferOffsetAlignment
+# (i.e. the maximum possible alignment requirement) as 256 bytes, so we will use
+# buffer with size of 65 int32 values = 260 bytes.
+BUFFER buf2 DATA_TYPE int32 SIZE 65 FILL 0
+
PIPELINE compute pipeline
ATTACH compute_shader
BIND BUFFER_ARRAY buf0 buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER_ARRAY buf2 buf2 AS storage DESCRIPTOR_SET 0 BINDING 1 DESCRIPTOR_OFFSET 0 256 DESCRIPTOR_RANGE 256 4
END
RUN pipeline 1 1 1
EXPECT buf0 IDX 0 EQ 1
EXPECT buf1 IDX 0 EQ 2
+EXPECT buf2 IDX 0 EQ 3
+EXPECT buf2 IDX 256 EQ 4
diff --git a/tests/cases/compute_descriptor_array_storagetexelbuffer.amber b/tests/cases/compute_descriptor_array_storagetexelbuffer.amber
index d4a1994..3ddae3c 100644
--- a/tests/cases/compute_descriptor_array_storagetexelbuffer.amber
+++ b/tests/cases/compute_descriptor_array_storagetexelbuffer.amber
@@ -17,13 +17,20 @@ SHADER compute compute_shader GLSL
#version 430
layout(local_size_x=4,local_size_y=1) in;
uniform layout(set=0, binding=0) samplerBuffer texelsIn;
-uniform layout(set=0, binding=1, rgba32f) imageBuffer texelsOut[2];
+uniform layout(set=0, binding=1, rgba32f) imageBuffer texelsOut_0[2];
+uniform layout(set=0, binding=2, rgba32f) imageBuffer texelsOut_1[2];
void main ()
{
vec4 color = texelFetch(texelsIn, int(gl_GlobalInvocationID));
- imageStore(texelsOut[0], int(gl_GlobalInvocationID), color);
- imageStore(texelsOut[1], int(gl_GlobalInvocationID), color * 2.0);
+ imageStore(texelsOut_0[0], int(gl_GlobalInvocationID), color);
+ imageStore(texelsOut_0[1], int(gl_GlobalInvocationID), color * 2.0);
+ if (int(gl_GlobalInvocationID) < 2) {
+ imageStore(texelsOut_1[0], int(gl_GlobalInvocationID), color * 3.0);
+ }
+ else {
+ imageStore(texelsOut_1[1], int(gl_GlobalInvocationID), color * 4.0);
+ }
}
END
@@ -36,6 +43,7 @@ END
BUFFER texel_buffer_out0 DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0
BUFFER texel_buffer_out1 DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0
+BUFFER texel_buffer_out2 DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0
BUFFER ref0 DATA_TYPE R32G32B32A32_SFLOAT DATA
1.0 0.0 0.0 1.0
@@ -51,13 +59,22 @@ BUFFER ref1 DATA_TYPE R32G32B32A32_SFLOAT DATA
0.0 2.0 2.0 2.0
END
+BUFFER ref2 DATA_TYPE R32G32B32A32_SFLOAT DATA
+3.0 0.0 0.0 3.0
+0.0 3.0 0.0 3.0
+0.0 0.0 4.0 4.0
+0.0 4.0 4.0 4.0
+END
+
PIPELINE compute pipeline
ATTACH compute_shader
BIND BUFFER texel_buffer_in AS uniform_texel_buffer DESCRIPTOR_SET 0 BINDING 0
BIND BUFFER_ARRAY texel_buffer_out0 texel_buffer_out1 AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING 1
+ BIND BUFFER_ARRAY texel_buffer_out2 texel_buffer_out2 AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING 2
END
RUN pipeline 1 1 1
EXPECT texel_buffer_out0 EQ_BUFFER ref0
EXPECT texel_buffer_out1 EQ_BUFFER ref1
+EXPECT texel_buffer_out2 EQ_BUFFER ref2
diff --git a/tests/cases/compute_dynamic_buffers.amber b/tests/cases/compute_dynamic_buffers.amber
index 9412e30..9dd4149 100644
--- a/tests/cases/compute_dynamic_buffers.amber
+++ b/tests/cases/compute_dynamic_buffers.amber
@@ -25,11 +25,16 @@ layout(set = 0, binding = 1) buffer block1
{
vec4 data1;
};
+layout(set = 0, binding = 2) buffer block2
+{
+ vec4 data2;
+};
void main()
{
data0 = vec4(1, 2, 3, 4);
data1 = vec4(5, 6, 7, 8);
+ data2 = vec4(9, 10, 11, 12);
}
END
@@ -39,6 +44,7 @@ END
# after the alignment (256 / 16 + 1).
BUFFER buf0 DATA_TYPE vec4<float> SIZE 17 FILL 0.0
BUFFER buf1 DATA_TYPE vec4<float> SIZE 17 FILL 0.0
+BUFFER buf2 DATA_TYPE vec4<float> SIZE 33 FILL 0.0
PIPELINE compute pipeline
ATTACH compute_shader
@@ -46,9 +52,12 @@ PIPELINE compute pipeline
BIND BUFFER buf0 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 0 OFFSET 0
# Using the maximum possible required offset alignment of 256 bytes to support all implementations.
BIND BUFFER buf1 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 1 OFFSET 256
+ # Same as above, but with buffer offset of 256 bytes (total offset of 512).
+ BIND BUFFER buf2 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 2 OFFSET 256 DESCRIPTOR_OFFSET 256
END
RUN pipeline 1 1 1
EXPECT buf0 IDX 0 EQ 1.0 2.0 3.0 4.0
EXPECT buf1 IDX 256 EQ 5.0 6.0 7.0 8.0
+EXPECT buf2 IDX 512 EQ 9.0 10.0 11.0 12.0
diff --git a/tests/cases/compute_dynamic_buffers_descriptor_array.amber b/tests/cases/compute_dynamic_buffers_descriptor_array.amber
index fe07b6c..ccc4c42 100644
--- a/tests/cases/compute_dynamic_buffers_descriptor_array.amber
+++ b/tests/cases/compute_dynamic_buffers_descriptor_array.amber
@@ -19,12 +19,18 @@ SHADER compute compute_shader GLSL
layout(set = 0, binding = 0) buffer block0
{
vec4 data;
-} ssbo_array[2];
+} ssbo_array_0[2];
+layout(set = 0, binding = 1) buffer block1
+{
+ int data;
+} ssbo_array_1[2];
void main()
{
- ssbo_array[0].data = vec4(1, 2, 3, 4);
- ssbo_array[1].data = vec4(5, 6, 7, 8);
+ ssbo_array_0[0].data = vec4(1, 2, 3, 4);
+ ssbo_array_0[1].data = vec4(5, 6, 7, 8);
+ ssbo_array_1[0].data = 9;
+ ssbo_array_1[1].data = 10;
}
END
@@ -34,15 +40,20 @@ END
# after the alignment (256 / 16 + 1).
BUFFER buf0 DATA_TYPE vec4<float> SIZE 17 FILL 0.0
BUFFER buf1 DATA_TYPE vec4<float> SIZE 17 FILL 0.0
+# Same as above, but with int32 and double size: 512 / 4 + 1
+BUFFER buf2 DATA_TYPE int32 SIZE 129 FILL 0
PIPELINE compute pipeline
ATTACH compute_shader
# Using the maximum possible required offset alignment of 256 bytes to support all implementations.
BIND BUFFER_ARRAY buf0 buf1 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 0 OFFSET 0 256
+ BIND BUFFER_ARRAY buf2 buf2 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 1 OFFSET 0 256 DESCRIPTOR_OFFSET 0 256
END
RUN pipeline 1 1 1
EXPECT buf0 IDX 0 EQ 1.0 2.0 3.0 4.0
EXPECT buf1 IDX 256 EQ 5.0 6.0 7.0 8.0
+EXPECT buf2 IDX 0 EQ 9
+EXPECT buf2 IDX 512 EQ 10
diff --git a/tests/cases/graphics_descriptor_array_combined_image_sampler.amber b/tests/cases/graphics_descriptor_array_combined_image_sampler.amber
index d7fe3c0..e80ca1e 100644
--- a/tests/cases/graphics_descriptor_array_combined_image_sampler.amber
+++ b/tests/cases/graphics_descriptor_array_combined_image_sampler.amber
@@ -40,10 +40,16 @@ SHADER fragment frag_shader_tex GLSL
#version 430
layout(location = 0) in vec2 texcoords_in;
layout(location = 0) out vec4 color_out;
-uniform layout(set=0, binding=0) sampler2D tex_sampler[2];
+uniform layout(set=0, binding=0) sampler2D tex_sampler_0[2];
+uniform layout(set=0, binding=1) sampler2D tex_sampler_1[2];
void main() {
- color_out = texture(tex_sampler[0], texcoords_in);
- color_out = color_out + texture(tex_sampler[1], texcoords_in);
+ vec4 tex_0_color = texture(tex_sampler_0[0], texcoords_in);
+ color_out = tex_0_color + texture(tex_sampler_0[1], texcoords_in);
+ // tex_sampler_1[0] and tex_sampler_1[1] should be equal to tex_sampler_0[0]
+ if (tex_0_color != texture(tex_sampler_1[0], texcoords_in) ||
+ tex_0_color != texture(tex_sampler_1[1], texcoords_in)) {
+ color_out = vec4(1.0);
+ }
}
END
@@ -75,6 +81,7 @@ PIPELINE graphics pipeline
ATTACH vert_shader_tex
ATTACH frag_shader_tex
BIND BUFFER_ARRAY texture0 texture1 AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER_ARRAY texture0 texture0 AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1
VERTEX_DATA position LOCATION 0
VERTEX_DATA texcoords LOCATION 1
BIND BUFFER framebuffer AS color LOCATION 0
diff --git a/tests/cases/graphics_descriptor_array_sampled_image.amber b/tests/cases/graphics_descriptor_array_sampled_image.amber
index 497f6da..3342054 100644
--- a/tests/cases/graphics_descriptor_array_sampled_image.amber
+++ b/tests/cases/graphics_descriptor_array_sampled_image.amber
@@ -40,11 +40,18 @@ SHADER fragment frag_shader_tex GLSL
#version 430
layout(location = 0) in vec2 texcoords_in;
layout(location = 0) out vec4 color_out;
-uniform layout(set=0, binding=0) texture2D tex[2];
+uniform layout(set=0, binding=0) texture2D tex_0[2];
uniform layout(set=0, binding=1) sampler tex_sampler;
+uniform layout(set=0, binding=2) texture2D tex_1[2];
void main() {
- color_out = texture(sampler2D(tex[0], tex_sampler), texcoords_in);
- color_out += texture(sampler2D(tex[1], tex_sampler), texcoords_in);
+ vec4 tex_0_color = texture(sampler2D(tex_0[0], tex_sampler), texcoords_in);
+ color_out = tex_0_color;
+ color_out += texture(sampler2D(tex_0[1], tex_sampler), texcoords_in);
+ // tex_1[0] and tex_1[1] should be equal to tex_0[0].
+ if (tex_0_color != texture(sampler2D(tex_1[0], tex_sampler), texcoords_in) ||
+ tex_0_color != texture(sampler2D(tex_1[1], tex_sampler), texcoords_in)) {
+ color_out = vec4(1.0);
+ }
}
END
@@ -77,6 +84,7 @@ PIPELINE graphics pipeline
ATTACH frag_shader_tex
BIND BUFFER_ARRAY texture0 texture1 AS sampled_image DESCRIPTOR_SET 0 BINDING 0
BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1
+ BIND BUFFER_ARRAY texture0 texture0 AS sampled_image DESCRIPTOR_SET 0 BINDING 2
VERTEX_DATA position LOCATION 0
VERTEX_DATA texcoords LOCATION 1
BIND BUFFER framebuffer AS color LOCATION 0