diff options
author | Ilkka Saarelainen <ilkka.saarelainen@siru.fi> | 2021-04-28 15:45:00 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-28 13:45:00 +0100 |
commit | 8797ee109e7a6ea4d1f58f387f757545fa35325b (patch) | |
tree | 874e5aa21bad7d724a57b8bb35ebcd84acfe3134 | |
parent | f0a16b4615d142ff156942fc32820368bb9d8cf3 (diff) | |
download | amber-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
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 |