aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorasuonpaa <34128694+asuonpaa@users.noreply.github.com>2020-06-11 15:47:57 +0300
committerGitHub <noreply@github.com>2020-06-11 13:47:57 +0100
commit89a1b56197d0682c78541182a620638dd7d142e1 (patch)
treebf6ef6307da6ad6641cf465f63181b661d152a5f
parent298b24a4379658949bcc5256f9b54678613208c1 (diff)
downloadamber-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.md7
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/amberscript/parser.cc17
-rw-r--r--src/amberscript/parser_bind_test.cc84
-rw-r--r--src/buffer.h5
-rw-r--r--src/pipeline.cc5
-rw-r--r--src/pipeline.h6
-rw-r--r--src/pipeline_test.cc2
-rw-r--r--src/vkscript/parser.cc2
-rw-r--r--src/vulkan/device.h8
-rw-r--r--src/vulkan/engine_vulkan.cc6
-rw-r--r--src/vulkan/graphics_pipeline.cc36
-rw-r--r--src/vulkan/vertex_buffer.cc77
-rw-r--r--src/vulkan/vertex_buffer.h31
-rw-r--r--src/vulkan/vertex_buffer_test.cc172
-rw-r--r--tests/cases/vertex_rate_instance.amber70
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