diff options
author | asuonpaa <34128694+asuonpaa@users.noreply.github.com> | 2020-06-11 15:47:57 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-11 13:47:57 +0100 |
commit | 89a1b56197d0682c78541182a620638dd7d142e1 (patch) | |
tree | bf6ef6307da6ad6641cf465f63181b661d152a5f | |
parent | 298b24a4379658949bcc5256f9b54678613208c1 (diff) | |
download | amber-89a1b56197d0682c78541182a620638dd7d142e1.tar.gz |
Add input rate support for Amber script (#871)
Also modified Vulkan engine to use separate bindings for each vertex buffer as input rate is set per binding.
Fixes #870
-rw-r--r-- | docs/amber_script.md | 7 | ||||
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/amberscript/parser.cc | 17 | ||||
-rw-r--r-- | src/amberscript/parser_bind_test.cc | 84 | ||||
-rw-r--r-- | src/buffer.h | 5 | ||||
-rw-r--r-- | src/pipeline.cc | 5 | ||||
-rw-r--r-- | src/pipeline.h | 6 | ||||
-rw-r--r-- | src/pipeline_test.cc | 2 | ||||
-rw-r--r-- | src/vkscript/parser.cc | 2 | ||||
-rw-r--r-- | src/vulkan/device.h | 8 | ||||
-rw-r--r-- | src/vulkan/engine_vulkan.cc | 6 | ||||
-rw-r--r-- | src/vulkan/graphics_pipeline.cc | 36 | ||||
-rw-r--r-- | src/vulkan/vertex_buffer.cc | 77 | ||||
-rw-r--r-- | src/vulkan/vertex_buffer.h | 31 | ||||
-rw-r--r-- | src/vulkan/vertex_buffer_test.cc | 172 | ||||
-rw-r--r-- | tests/cases/vertex_rate_instance.amber | 70 |
16 files changed, 380 insertions, 150 deletions
diff --git a/docs/amber_script.md b/docs/amber_script.md index d1ed0a9..fbe2c8c 100644 --- a/docs/amber_script.md +++ b/docs/amber_script.md @@ -565,15 +565,16 @@ and binding ID. # Attach |buffer_name| as a combined image sampler. A sampler |sampler_name| # must also be specified. The MIP level will have a base value of 0. BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS combined_image_sampler SAMPLER {sampler_name} \ - DESCRIPTOR_SET _id_ BINDING _id_ [ BASE_MIP_LEVEL _level_ (default) 0) ] + DESCRIPTOR_SET _id_ BINDING _id_ [ BASE_MIP_LEVEL _level_ (default 0) ] # Bind the sampler at the given descriptor set and binding. BIND {SAMPLER | SAMPLER_ARRAY} {sampler_name} DESCRIPTOR_SET _id_ BINDING _id_ ``` ```groovy - # Set |buffer_name| as the vertex data at location |val|. - VERTEX_DATA {buffer_name} LOCATION _val_ + # Set |buffer_name| as the vertex data at location |val|. `RATE` defines the + # input rate for vertex attribute reading. + VERTEX_DATA {buffer_name} LOCATION _val_ [ RATE { vertex | instance } (default vertex) ] # Set |buffer_name| as the index data to use for `INDEXED` draw commands. INDEX_DATA {buffer_name} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 72a7a8c..5618bff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -203,7 +203,7 @@ if (${AMBER_ENABLE_TESTS}) add_test(NAME amber_unittests COMMAND amber_unittests) if (${Vulkan_FOUND}) - target_include_directories(amber_unittests PRIVATE "${VulkanHeaders_INCLUDE_DIR}") + target_include_directories(amber_unittests PRIVATE "${VulkanHeaders_INCLUDE_DIR}" "${CMAKE_BINARY_DIR}") endif() if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc index 33bce22..eda31f4 100644 --- a/src/amberscript/parser.cc +++ b/src/amberscript/parser.cc @@ -1257,8 +1257,23 @@ Result Parser::ParsePipelineVertexData(Pipeline* pipeline) { token = tokenizer_->NextToken(); if (!token->IsInteger()) return Result("invalid value for VERTEX_DATA LOCATION"); + const uint32_t location = token->AsUint32(); - Result r = pipeline->AddVertexBuffer(buffer, token->AsUint32()); + InputRate rate = InputRate::kVertex; + token = tokenizer_->PeekNextToken(); + if (token->IsIdentifier() && token->AsString() == "RATE") { + tokenizer_->NextToken(); + token = tokenizer_->NextToken(); + if (!token->IsIdentifier()) + return Result("missing input rate value for RATE"); + if (token->AsString() == "instance") { + rate = InputRate::kInstance; + } else if (token->AsString() != "vertex") { + return Result("expecting 'vertex' or 'instance' for RATE value"); + } + } + + Result r = pipeline->AddVertexBuffer(buffer, location, rate); if (!r.IsSuccess()) return r; diff --git a/src/amberscript/parser_bind_test.cc b/src/amberscript/parser_bind_test.cc index 64b084e..8a1623f 100644 --- a/src/amberscript/parser_bind_test.cc +++ b/src/amberscript/parser_bind_test.cc @@ -685,10 +685,12 @@ END)"; const auto& info1 = vertex_buffers[0]; ASSERT_TRUE(info1.buffer != nullptr); EXPECT_EQ(0, info1.location); + EXPECT_EQ(InputRate::kVertex, info1.input_rate); const auto& info2 = vertex_buffers[1]; ASSERT_TRUE(info2.buffer != nullptr); EXPECT_EQ(1, info2.location); + EXPECT_EQ(InputRate::kVertex, info2.input_rate); } TEST_F(AmberScriptParserTest, BindVertexDataDuplicateLocation) { @@ -842,6 +844,88 @@ END)"; EXPECT_EQ("12: extra parameters after VERTEX_DATA command: EXTRA", r.Error()); } +TEST_F(AmberScriptParserTest, BindVertexDataInputRate) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5 +BUFFER my_buf2 DATA_TYPE int8 SIZE 5 FILL 5 + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + + VERTEX_DATA my_buf LOCATION 0 RATE vertex + VERTEX_DATA my_buf2 LOCATION 1 RATE instance +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& vertex_buffers = pipeline->GetVertexBuffers(); + ASSERT_EQ(2, vertex_buffers.size()); + + const auto& info1 = vertex_buffers[0]; + ASSERT_TRUE(info1.buffer != nullptr); + EXPECT_EQ(0, info1.location); + EXPECT_EQ(InputRate::kVertex, info1.input_rate); + + const auto& info2 = vertex_buffers[1]; + ASSERT_TRUE(info2.buffer != nullptr); + EXPECT_EQ(1, info2.location); + EXPECT_EQ(InputRate::kInstance, info2.input_rate); +} + +TEST_F(AmberScriptParserTest, BindVertexDataInputRateMissingValue) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5 + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + + VERTEX_DATA my_buf LOCATION 0 RATE +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("13: missing input rate value for RATE", r.Error()); +} + +TEST_F(AmberScriptParserTest, BindVertexDataInputRateInvalidValue) { + std::string in = R"( +SHADER vertex my_shader PASSTHROUGH +SHADER fragment my_fragment GLSL +# GLSL Shader +END +BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5 + +PIPELINE graphics my_pipeline + ATTACH my_shader + ATTACH my_fragment + + VERTEX_DATA my_buf LOCATION 0 RATE foo +END)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("12: expecting 'vertex' or 'instance' for RATE value", r.Error()); +} + TEST_F(AmberScriptParserTest, BindIndexData) { std::string in = R"( SHADER vertex my_shader PASSTHROUGH diff --git a/src/buffer.h b/src/buffer.h index 49bb627..347f876 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -61,6 +61,11 @@ enum class BufferType : int8_t { kStorageTexelBuffer }; +enum class InputRate : int8_t { + kVertex = 0, + kInstance, +}; + /// A buffer stores data. The buffer maybe provided from the input script, or /// maybe created as needed. A buffer must have a unique name. class Buffer { diff --git a/src/pipeline.cc b/src/pipeline.cc index 845983d..334479c 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -420,7 +420,9 @@ Result Pipeline::SetIndexBuffer(Buffer* buf) { return {}; } -Result Pipeline::AddVertexBuffer(Buffer* buf, uint32_t location) { +Result Pipeline::AddVertexBuffer(Buffer* buf, + uint32_t location, + InputRate rate) { for (const auto& vtex : vertex_buffers_) { if (vtex.location == location) return Result("can not bind two vertex buffers to the same LOCATION"); @@ -431,6 +433,7 @@ Result Pipeline::AddVertexBuffer(Buffer* buf, uint32_t location) { vertex_buffers_.push_back(BufferInfo{buf}); vertex_buffers_.back().location = location; vertex_buffers_.back().type = BufferType::kVertex; + vertex_buffers_.back().input_rate = rate; return {}; } diff --git a/src/pipeline.h b/src/pipeline.h index 5d6cc9c..0e0ec51 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -192,6 +192,7 @@ class Pipeline { std::string arg_name = ""; uint32_t arg_no = 0; BufferType type = BufferType::kUnknown; + InputRate input_rate = InputRate::kVertex; }; /// Information on a sampler attached to the pipeline. @@ -306,8 +307,9 @@ class Pipeline { const std::vector<BufferInfo>& GetVertexBuffers() const { return vertex_buffers_; } - /// Adds |buf| as a vertex buffer at |location| in the pipeline. - Result AddVertexBuffer(Buffer* buf, uint32_t location); + /// Adds |buf| as a vertex buffer at |location| in the pipeline using |rate| + /// as the input rate. + Result AddVertexBuffer(Buffer* buf, uint32_t location, InputRate rate); /// Binds |buf| as the index buffer for this pipeline. Result SetIndexBuffer(Buffer* buf); diff --git a/src/pipeline_test.cc b/src/pipeline_test.cc index 28443c0..86c7821 100644 --- a/src/pipeline_test.cc +++ b/src/pipeline_test.cc @@ -353,7 +353,7 @@ TEST_F(PipelineTest, Clone) { auto vtex_buf = MakeUnique<Buffer>(); vtex_buf->SetName("vertex_buffer"); - p.AddVertexBuffer(vtex_buf.get(), 1); + p.AddVertexBuffer(vtex_buf.get(), 1, InputRate::kVertex); auto idx_buf = MakeUnique<Buffer>(); idx_buf->SetName("Index Buffer"); diff --git a/src/vkscript/parser.cc b/src/vkscript/parser.cc index d8cecb6..aae55cc 100644 --- a/src/vkscript/parser.cc +++ b/src/vkscript/parser.cc @@ -444,7 +444,7 @@ Result Parser::ProcessVertexDataBlock(const SectionParser::Section& section) { script_->AddBuffer(std::move(buffer)); - pipeline->AddVertexBuffer(buf, headers[i].location); + pipeline->AddVertexBuffer(buf, headers[i].location, InputRate::kVertex); } return {}; diff --git a/src/vulkan/device.h b/src/vulkan/device.h index 5eccc64..eccab8b 100644 --- a/src/vulkan/device.h +++ b/src/vulkan/device.h @@ -41,7 +41,7 @@ class Device { uint32_t queue_family_index, VkDevice device, VkQueue queue); - ~Device(); + virtual ~Device(); Result Initialize( PFN_vkGetInstanceProcAddr getInstanceProcAddr, @@ -69,15 +69,15 @@ class Device { bool IsDescriptorSetInBounds(uint32_t descriptor_set) const; /// Returns true if the memory at |memory_type_index| has |flags| set. - bool HasMemoryFlags(uint32_t memory_type_index, - const VkMemoryPropertyFlags flags) const; + virtual bool HasMemoryFlags(uint32_t memory_type_index, + const VkMemoryPropertyFlags flags) const; /// Returns true if the memory at |memory_type_index| is host accessible. bool IsMemoryHostAccessible(uint32_t memory_type_index) const; /// Returns true if the memory at |memory_type_index| is host corherent. bool IsMemoryHostCoherent(uint32_t memory_type_index) const; /// Returns the pointers to the Vulkan API methods. - const VulkanPtrs* GetPtrs() const { return &ptrs_; } + virtual const VulkanPtrs* GetPtrs() const { return &ptrs_; } /// Returns true if the required subgroup size is supported for given stage bool IsRequiredSubgroupSizeSupported( diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc index 870f2ce..4f2731d 100644 --- a/src/vulkan/engine_vulkan.cc +++ b/src/vulkan/engine_vulkan.cc @@ -216,7 +216,7 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) { info.vertex_buffer = MakeUnique<VertexBuffer>(device_.get()); info.vertex_buffer->SetData(static_cast<uint8_t>(vtex_info.location), - vtex_info.buffer); + vtex_info.buffer, vtex_info.input_rate); } if (pipeline->GetIndexBuffer()) { @@ -495,7 +495,7 @@ Result EngineVulkan::DoDrawRect(const DrawRectCommand* command) { buf->SetData(std::move(values)); auto vertex_buffer = MakeUnique<VertexBuffer>(device_.get()); - vertex_buffer->SetData(0, buf.get()); + vertex_buffer->SetData(0, buf.get(), InputRate::kVertex); DrawArraysCommand draw(command->GetPipeline(), *command->GetPipelineData()); draw.SetTopology(command->IsPatch() ? Topology::kPatchList @@ -582,7 +582,7 @@ Result EngineVulkan::DoDrawGrid(const DrawGridCommand* command) { buf->SetData(std::move(values)); auto vertex_buffer = MakeUnique<VertexBuffer>(device_.get()); - vertex_buffer->SetData(0, buf.get()); + vertex_buffer->SetData(0, buf.get(), InputRate::kVertex); DrawArraysCommand draw(command->GetPipeline(), *command->GetPipelineData()); draw.SetTopology(Topology::kTriangleList); diff --git a/src/vulkan/graphics_pipeline.cc b/src/vulkan/graphics_pipeline.cc index 78513d2..b83bea1 100644 --- a/src/vulkan/graphics_pipeline.cc +++ b/src/vulkan/graphics_pipeline.cc @@ -546,31 +546,25 @@ Result GraphicsPipeline::CreateVkGraphicsPipeline( "null"); } + std::vector<VkVertexInputBindingDescription> vertex_bindings; + std::vector<VkVertexInputAttributeDescription> vertex_attribs; + if (vertex_buffer != nullptr) { + vertex_bindings = vertex_buffer->GetVkVertexInputBinding(); + vertex_attribs = vertex_buffer->GetVkVertexInputAttr(); + } + VkPipelineVertexInputStateCreateInfo vertex_input_info = VkPipelineVertexInputStateCreateInfo(); vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertex_input_info.vertexBindingDescriptionCount = 1; - - VkVertexInputBindingDescription vertex_binding_desc = - VkVertexInputBindingDescription(); - if (vertex_buffer != nullptr) { - vertex_binding_desc = vertex_buffer->GetVkVertexInputBinding(); - const auto& vertex_attr_desc = vertex_buffer->GetVkVertexInputAttr(); - - vertex_input_info.pVertexBindingDescriptions = &vertex_binding_desc; - vertex_input_info.vertexAttributeDescriptionCount = - static_cast<uint32_t>(vertex_attr_desc.size()); - vertex_input_info.pVertexAttributeDescriptions = vertex_attr_desc.data(); - } else { - vertex_binding_desc.binding = 0; - vertex_binding_desc.stride = 0; - vertex_binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - vertex_input_info.pVertexBindingDescriptions = &vertex_binding_desc; - vertex_input_info.vertexAttributeDescriptionCount = 0; - vertex_input_info.pVertexAttributeDescriptions = nullptr; - } + vertex_input_info.vertexBindingDescriptionCount = + static_cast<uint32_t>(vertex_bindings.size()); + vertex_input_info.pVertexBindingDescriptions = + vertex_bindings.empty() ? nullptr : vertex_bindings.data(); + vertex_input_info.vertexAttributeDescriptionCount = + static_cast<uint32_t>(vertex_attribs.size()); + vertex_input_info.pVertexAttributeDescriptions = + vertex_attribs.empty() ? nullptr : vertex_attribs.data(); VkPipelineInputAssemblyStateCreateInfo input_assembly_info = VkPipelineInputAssemblyStateCreateInfo(); diff --git a/src/vulkan/vertex_buffer.cc b/src/vulkan/vertex_buffer.cc index 402a68b..409c0f1 100644 --- a/src/vulkan/vertex_buffer.cc +++ b/src/vulkan/vertex_buffer.cc @@ -23,71 +23,70 @@ namespace amber { namespace vulkan { +namespace { + +VkVertexInputRate GetVkInputRate(InputRate rate) { + return rate == InputRate::kVertex ? VK_VERTEX_INPUT_RATE_VERTEX + : VK_VERTEX_INPUT_RATE_INSTANCE; +} + +} // namespace VertexBuffer::VertexBuffer(Device* device) : device_(device) {} VertexBuffer::~VertexBuffer() = default; -void VertexBuffer::SetData(uint8_t location, Buffer* buffer) { +void VertexBuffer::SetData(uint8_t location, Buffer* buffer, InputRate rate) { auto format = buffer->GetFormat(); - + const uint32_t binding = static_cast<uint32_t>(vertex_attr_desc_.size()); vertex_attr_desc_.emplace_back(); - // TODO(jaebaek): Support multiple binding - vertex_attr_desc_.back().binding = 0; + vertex_attr_desc_.back().binding = binding; vertex_attr_desc_.back().location = location; - vertex_attr_desc_.back().offset = stride_in_bytes_; + vertex_attr_desc_.back().offset = 0u; vertex_attr_desc_.back().format = device_->GetVkFormat(*format); - stride_in_bytes_ += format->SizeInBytes(); - data_.push_back(buffer); -} - -Result VertexBuffer::FillVertexBufferWithData(CommandBuffer* command) { - // Send vertex data from host to device. - uint8_t* ptr_in_stride_begin = - static_cast<uint8_t*>(transfer_buffer_->HostAccessibleMemoryPtr()); - for (uint32_t i = 0; i < GetVertexCount(); ++i) { - uint8_t* ptr = ptr_in_stride_begin; - for (uint32_t j = 0; j < data_.size(); ++j) { - size_t bytes = data_[j]->GetFormat()->SizeInBytes(); - std::memcpy(ptr, data_[j]->GetValues<uint8_t>() + (i * bytes), bytes); - ptr += bytes; - } - ptr_in_stride_begin += Get4BytesAlignedStride(); - } + vertex_binding_desc_.emplace_back(); + vertex_binding_desc_.back().binding = binding; + vertex_binding_desc_.back().stride = format->SizeInBytes(); + vertex_binding_desc_.back().inputRate = GetVkInputRate(rate); - transfer_buffer_->CopyToDevice(command); - return {}; + data_.push_back(buffer); } void VertexBuffer::BindToCommandBuffer(CommandBuffer* command) { - const VkDeviceSize offset = 0; - const VkBuffer buffer = transfer_buffer_->GetVkBuffer(); - // TODO(jaebaek): Support multiple binding - device_->GetPtrs()->vkCmdBindVertexBuffers(command->GetVkCommandBuffer(), 0, - 1, &buffer, &offset); + std::vector<VkBuffer> buffers; + std::vector<VkDeviceSize> offsets; + + for (const auto& buf : transfer_buffers_) { + buffers.push_back(buf->GetVkBuffer()); + offsets.push_back(0); + } + device_->GetPtrs()->vkCmdBindVertexBuffers( + command->GetVkCommandBuffer(), 0, + static_cast<uint32_t>(transfer_buffers_.size()), buffers.data(), + offsets.data()); } Result VertexBuffer::SendVertexData(CommandBuffer* command) { if (!is_vertex_data_pending_) return Result("Vulkan::Vertices data was already sent"); - const uint32_t n_vertices = GetVertexCount(); - if (n_vertices == 0) - return Result("Vulkan::Data for VertexBuffer is empty"); + for (const auto& buf : data_) { + uint32_t bytes = buf->GetSizeInBytes(); + + transfer_buffers_.push_back( + MakeUnique<TransferBuffer>(device_, bytes, nullptr)); + Result r = transfer_buffers_.back()->Initialize( + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); - uint32_t bytes = Get4BytesAlignedStride() * n_vertices; + std::memcpy(transfer_buffers_.back()->HostAccessibleMemoryPtr(), + buf->GetValues<void>(), bytes); + transfer_buffers_.back()->CopyToDevice(command); - if (!transfer_buffer_) { - transfer_buffer_ = MakeUnique<TransferBuffer>(device_, bytes, nullptr); - Result r = transfer_buffer_->Initialize(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT); if (!r.IsSuccess()) return r; } - FillVertexBufferWithData(command); - is_vertex_data_pending_ = false; return {}; } diff --git a/src/vulkan/vertex_buffer.h b/src/vulkan/vertex_buffer.h index e7b216e..2837a9e 100644 --- a/src/vulkan/vertex_buffer.h +++ b/src/vulkan/vertex_buffer.h @@ -40,47 +40,28 @@ class VertexBuffer { Result SendVertexData(CommandBuffer* command); bool VertexDataSent() const { return !is_vertex_data_pending_; } - void SetData(uint8_t location, Buffer* buffer); + void SetData(uint8_t location, Buffer* buffer, InputRate rate); const std::vector<VkVertexInputAttributeDescription>& GetVkVertexInputAttr() const { return vertex_attr_desc_; } - - VkVertexInputBindingDescription GetVkVertexInputBinding() const { - VkVertexInputBindingDescription vertex_binding_desc = - VkVertexInputBindingDescription(); - vertex_binding_desc.binding = 0; - vertex_binding_desc.stride = Get4BytesAlignedStride(); - vertex_binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - return vertex_binding_desc; - } - - uint32_t GetVertexCount() const { - if (data_.empty()) - return 0; - return data_[0]->ElementCount(); + const std::vector<VkVertexInputBindingDescription>& GetVkVertexInputBinding() + const { + return vertex_binding_desc_; } void BindToCommandBuffer(CommandBuffer* command); - void SetBufferForTest(std::unique_ptr<TransferBuffer> buffer); - private: - Result FillVertexBufferWithData(CommandBuffer* command); - - uint32_t Get4BytesAlignedStride() const { - return ((stride_in_bytes_ + 3) / 4) * 4; - } - Device* device_ = nullptr; bool is_vertex_data_pending_ = true; - std::unique_ptr<TransferBuffer> transfer_buffer_; - uint32_t stride_in_bytes_ = 0; + std::vector<std::unique_ptr<TransferBuffer>> transfer_buffers_; std::vector<Buffer*> data_; + std::vector<VkVertexInputBindingDescription> vertex_binding_desc_; std::vector<VkVertexInputAttributeDescription> vertex_attr_desc_; }; diff --git a/src/vulkan/vertex_buffer_test.cc b/src/vulkan/vertex_buffer_test.cc index 12bed3a..2691557 100644 --- a/src/vulkan/vertex_buffer_test.cc +++ b/src/vulkan/vertex_buffer_test.cc @@ -21,75 +21,151 @@ #include "src/format.h" #include "src/make_unique.h" #include "src/type_parser.h" -#include "src/vulkan/transfer_buffer.h" +#include "src/vulkan/command_buffer.h" +#include "src/vulkan/command_pool.h" +#include "src/vulkan/device.h" namespace amber { namespace vulkan { namespace { -class BufferForTest : public TransferBuffer { +class DummyDevice : public Device { public: - BufferForTest(Device* device, uint32_t size_in_bytes) - : TransferBuffer(device, size_in_bytes, nullptr) { - memory_.resize(4096); - SetMemoryPtr(memory_.data()); + DummyDevice() + : Device(VkInstance(), + VkPhysicalDevice(), + 0u, + VkDevice(this), + VkQueue()) { + memory_.resize(64); + dummyPtrs_.vkCreateBuffer = vkCreateBuffer; + dummyPtrs_.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements; + dummyPtrs_.vkAllocateMemory = vkAllocateMemory; + dummyPtrs_.vkBindBufferMemory = vkBindBufferMemory; + dummyPtrs_.vkMapMemory = vkMapMemory; + dummyPtrs_.vkCmdPipelineBarrier = vkCmdPipelineBarrier; + dummyPtrs_.vkAllocateCommandBuffers = vkAllocateCommandBuffers; + dummyPtrs_.vkCreateFence = vkCreateFence; + dummyPtrs_.vkDestroyBufferView = vkDestroyBufferView; + dummyPtrs_.vkFreeMemory = vkFreeMemory; + dummyPtrs_.vkDestroyBuffer = vkDestroyBuffer; } - ~BufferForTest() override = default; + ~DummyDevice() override {} - void CopyToDevice(CommandBuffer*) override {} + const VulkanPtrs* GetPtrs() const override { return &dummyPtrs_; } + + bool HasMemoryFlags(uint32_t, const VkMemoryPropertyFlags) const override { + return true; + } + + void* GetMemoryPtr() { return memory_.data(); } private: + VulkanPtrs dummyPtrs_; std::vector<uint8_t> memory_; + + static VkResult vkCreateBuffer(VkDevice, + const VkBufferCreateInfo*, + const VkAllocationCallbacks*, + VkBuffer* pBuffer) { + *pBuffer = VkBuffer(1); + return VK_SUCCESS; + } + static void vkGetBufferMemoryRequirements( + VkDevice, + VkBuffer, + VkMemoryRequirements* pMemoryRequirements) { + pMemoryRequirements->alignment = 0; + pMemoryRequirements->size = 0; + pMemoryRequirements->memoryTypeBits = 0xffffffff; + } + static VkResult vkAllocateMemory(VkDevice, + const VkMemoryAllocateInfo*, + const VkAllocationCallbacks*, + VkDeviceMemory*) { + return VK_SUCCESS; + } + static VkResult vkBindBufferMemory(VkDevice, + VkBuffer, + VkDeviceMemory, + VkDeviceSize) { + return VK_SUCCESS; + } + static VkResult vkMapMemory(VkDevice device, + VkDeviceMemory, + VkDeviceSize, + VkDeviceSize, + VkMemoryMapFlags, + void** ppData) { + DummyDevice* devicePtr = reinterpret_cast<DummyDevice*>(device); + *ppData = devicePtr->GetMemoryPtr(); + return VK_SUCCESS; + } + static void vkCmdPipelineBarrier(VkCommandBuffer, + VkPipelineStageFlags, + VkPipelineStageFlags, + VkDependencyFlags, + uint32_t, + const VkMemoryBarrier*, + uint32_t, + const VkBufferMemoryBarrier*, + uint32_t, + const VkImageMemoryBarrier*) {} + static VkResult vkAllocateCommandBuffers(VkDevice, + const VkCommandBufferAllocateInfo*, + VkCommandBuffer*) { + return VK_SUCCESS; + } + static VkResult vkCreateFence(VkDevice, + const VkFenceCreateInfo*, + const VkAllocationCallbacks*, + VkFence*) { + return VK_SUCCESS; + } + static void vkDestroyBufferView(VkDevice, + VkBufferView, + const VkAllocationCallbacks*) {} + static void vkFreeMemory(VkDevice, + VkDeviceMemory, + const VkAllocationCallbacks*) {} + static void vkDestroyBuffer(VkDevice, + VkBuffer, + const VkAllocationCallbacks*) {} }; class VertexBufferTest : public testing::Test { public: - VertexBufferTest() { - vertex_buffer_ = MakeUnique<VertexBuffer>(nullptr); - - std::unique_ptr<TransferBuffer> buffer = - MakeUnique<BufferForTest>(nullptr, 0U); - buffer_memory_ = buffer->HostAccessibleMemoryPtr(); - vertex_buffer_->SetBufferForTest(std::move(buffer)); + VertexBufferTest() + : device_(MakeUnique<DummyDevice>()), + commandPool_(MakeUnique<CommandPool>(device_.get())), + commandBuffer_( + MakeUnique<CommandBuffer>(device_.get(), commandPool_.get())), + vertex_buffer_(MakeUnique<VertexBuffer>(device_.get())) { + commandBuffer_->Initialize(); } - ~VertexBufferTest() = default; - - Result SetIntData(uint8_t location, - Format* format, - std::vector<Value> values) { - auto buffer = MakeUnique<Buffer>(); - buffer->SetFormat(format); - buffer->SetData(std::move(values)); - - vertex_buffer_->SetData(location, buffer.get()); - return vertex_buffer_->SendVertexData(nullptr); - } + ~VertexBufferTest() { vertex_buffer_.reset(); } - Result SetDoubleData(uint8_t location, - Format* format, - std::vector<Value> values) { + Result SetData(uint8_t location, Format* format, std::vector<Value> values) { auto buffer = MakeUnique<Buffer>(); buffer->SetFormat(format); buffer->SetData(std::move(values)); - vertex_buffer_->SetData(location, buffer.get()); - return vertex_buffer_->SendVertexData(nullptr); + vertex_buffer_->SetData(location, buffer.get(), InputRate::kVertex); + return vertex_buffer_->SendVertexData(commandBuffer_.get()); } - const void* GetVkBufferPtr() const { return buffer_memory_; } + const void* GetVkBufferPtr() { return device_->GetMemoryPtr(); } private: + std::unique_ptr<DummyDevice> device_; + std::unique_ptr<CommandPool> commandPool_; + std::unique_ptr<CommandBuffer> commandBuffer_; std::unique_ptr<VertexBuffer> vertex_buffer_; - const void* buffer_memory_ = nullptr; }; } // namespace -void VertexBuffer::SetBufferForTest(std::unique_ptr<TransferBuffer> buffer) { - transfer_buffer_ = std::move(buffer); -} - TEST_F(VertexBufferTest, R8G8B8A8_UINT) { std::vector<Value> values(4); values[0].SetIntValue(55); @@ -100,7 +176,7 @@ TEST_F(VertexBufferTest, R8G8B8A8_UINT) { TypeParser parser; auto type = parser.Parse("R8G8B8A8_UINT"); Format fmt(type.get()); - Result r = SetIntData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const uint8_t* ptr = static_cast<const uint8_t*>(GetVkBufferPtr()); EXPECT_EQ(55, ptr[0]); @@ -119,7 +195,7 @@ TEST_F(VertexBufferTest, R16G16B16A16_UINT) { TypeParser parser; auto type = parser.Parse("R16G16B16A16_UINT"); Format fmt(type.get()); - Result r = SetIntData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const uint16_t* ptr = static_cast<const uint16_t*>(GetVkBufferPtr()); EXPECT_EQ(55, ptr[0]); @@ -138,7 +214,7 @@ TEST_F(VertexBufferTest, R32G32B32A32_UINT) { TypeParser parser; auto type = parser.Parse("R32G32B32A32_UINT"); Format fmt(type.get()); - Result r = SetIntData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const uint32_t* ptr = static_cast<const uint32_t*>(GetVkBufferPtr()); EXPECT_EQ(55, ptr[0]); @@ -157,7 +233,7 @@ TEST_F(VertexBufferTest, R64G64B64A64_UINT) { TypeParser parser; auto type = parser.Parse("R64G64B64A64_UINT"); Format fmt(type.get()); - Result r = SetIntData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const uint64_t* ptr = static_cast<const uint64_t*>(GetVkBufferPtr()); EXPECT_EQ(55, ptr[0]); @@ -176,7 +252,7 @@ TEST_F(VertexBufferTest, R8G8B8A8_SNORM) { TypeParser parser; auto type = parser.Parse("R8G8B8A8_SNORM"); Format fmt(type.get()); - Result r = SetIntData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const int8_t* ptr = static_cast<const int8_t*>(GetVkBufferPtr()); EXPECT_EQ(-55, ptr[0]); @@ -195,7 +271,7 @@ TEST_F(VertexBufferTest, R16G16B16A16_SNORM) { TypeParser parser; auto type = parser.Parse("R16G16B16A16_SNORM"); Format fmt(type.get()); - Result r = SetIntData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const int16_t* ptr = static_cast<const int16_t*>(GetVkBufferPtr()); EXPECT_EQ(-55, ptr[0]); @@ -214,7 +290,7 @@ TEST_F(VertexBufferTest, R32G32B32A32_SINT) { TypeParser parser; auto type = parser.Parse("R32G32B32A32_SINT"); Format fmt(type.get()); - Result r = SetIntData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const int32_t* ptr = static_cast<const int32_t*>(GetVkBufferPtr()); EXPECT_EQ(-55, ptr[0]); @@ -233,7 +309,7 @@ TEST_F(VertexBufferTest, R64G64B64A64_SINT) { TypeParser parser; auto type = parser.Parse("R64G64B64A64_SINT"); Format fmt(type.get()); - Result r = SetIntData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const int64_t* ptr = static_cast<const int64_t*>(GetVkBufferPtr()); EXPECT_EQ(-55, ptr[0]); @@ -251,7 +327,7 @@ TEST_F(VertexBufferTest, R32G32B32_SFLOAT) { TypeParser parser; auto type = parser.Parse("R32G32B32_SFLOAT"); Format fmt(type.get()); - Result r = SetDoubleData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const float* ptr = static_cast<const float*>(GetVkBufferPtr()); EXPECT_FLOAT_EQ(-6.0f, ptr[0]); @@ -268,7 +344,7 @@ TEST_F(VertexBufferTest, R64G64B64_SFLOAT) { TypeParser parser; auto type = parser.Parse("R64G64B64_SFLOAT"); Format fmt(type.get()); - Result r = SetDoubleData(0, &fmt, values); + Result r = SetData(0, &fmt, values); const double* ptr = static_cast<const double*>(GetVkBufferPtr()); EXPECT_DOUBLE_EQ(-6.0, ptr[0]); diff --git a/tests/cases/vertex_rate_instance.amber b/tests/cases/vertex_rate_instance.amber new file mode 100644 index 0000000..edee1a6 --- /dev/null +++ b/tests/cases/vertex_rate_instance.amber @@ -0,0 +1,70 @@ +#!amber +# Copyright 2020 The Amber Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +SHADER vertex vtex_shader GLSL +#version 430 +layout(location = 0) in vec2 position; +layout(location = 1) in vec4 color_in; +layout(location = 0) out vec4 color_out; +void main() +{ + gl_Position = vec4(position.x + float(gl_InstanceIndex) * 0.5, position.y, 0, 1); + color_out = color_in; +} +END + +SHADER fragment frag_shader GLSL +#version 430 +layout(location = 0) in vec4 color_in; +layout(location = 0) out vec4 color_out; +void main() +{ + color_out = color_in; +} +END + +BUFFER position_buf DATA_TYPE vec2<float> DATA +-1.0 1.0 +-1.0 -1.0 +-0.5 1.0 +-0.5 -1.0 +-1.0 -1.0 +-0.5 1.0 +END + +BUFFER color_buf DATA_TYPE R8G8B8A8_UNORM DATA +255 0 0 255 + 0 255 0 255 + 0 0 255 255 + 0 255 255 255 +END + +BUFFER framebuffer FORMAT B8G8R8A8_UNORM + +PIPELINE graphics pipeline + ATTACH vtex_shader + ATTACH frag_shader + + VERTEX_DATA position_buf LOCATION 0 RATE vertex + VERTEX_DATA color_buf LOCATION 1 RATE instance + FRAMEBUFFER_SIZE 40 40 + BIND BUFFER framebuffer AS color LOCATION 0 +END + +RUN pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6 START_INSTANCE 0 INSTANCE_COUNT 4 +EXPECT framebuffer IDX 0 0 SIZE 10 40 EQ_RGBA 255 0 0 255 +EXPECT framebuffer IDX 10 0 SIZE 10 40 EQ_RGBA 0 255 0 255 +EXPECT framebuffer IDX 20 0 SIZE 10 40 EQ_RGBA 0 0 255 255 +EXPECT framebuffer IDX 30 0 SIZE 10 40 EQ_RGBA 0 255 255 255 |