Experimentation using Vulkan.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

186 lines
4.1 KiB

#include <Vulkan/Buffer.h>
#include <Vulkan/Context.h>
#include <Logger.h>
#include <stdexcept>
#include <algorithm>
#include <cassert>
namespace Vulkan {
Buffer::Buffer() noexcept {
}
Buffer::Buffer(
Context& context,
VkDeviceSize size,
VkBufferUsageFlags usage
)
: m_context(&context)
{
assert(*m_context);
VkBufferCreateInfo create_info {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = size,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
if(vkCreateBuffer(
m_context->device(),
&create_info,
nullptr,
&m_buffer
) != VK_SUCCESS)
throw std::runtime_error("failed to create buffer");
}
Buffer::Buffer(
Context& context,
VkDeviceSize size,
VkBufferUsageFlags usage,
VkMemoryPropertyFlags memory_properties
)
: Buffer(context, size, usage)
{
allocate_and_bind_memory(memory_properties);
}
Buffer::Buffer(Buffer&& other) noexcept
: m_context(other.m_context)
, m_buffer(other.m_buffer)
, m_memory(std::move(other.m_memory))
{
other.m_context = nullptr;
other.m_buffer = VK_NULL_HANDLE;
}
Buffer::~Buffer() noexcept {
if(is_valid()) {
logger.warning() << "Buffer deleted before being destroyed";
destroy();
}
}
Buffer& Buffer::operator=(Buffer&& other) noexcept {
if(&other != this) {
using std::swap;
swap(m_context, other.m_context);
swap(m_buffer, other.m_buffer);
swap(m_memory, other.m_memory);
}
return *this;
}
VkMemoryRequirements Buffer::memory_requirements() const noexcept {
assert(is_valid());
assert(*m_context);
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(
m_context->device(),
m_buffer,
&memory_requirements
);
return memory_requirements;
}
void Buffer::bind_memory(const MemoryBlock& memory_block, VkDeviceSize offset) {
assert(is_valid());
assert(*m_context);
assert(memory_block);
// m_memory = std::move(memory_block);
if(vkBindBufferMemory(
m_context->device(),
m_buffer,
memory_block.device_memory(),
memory_block.offset() + offset
) != VK_SUCCESS)
throw std::runtime_error("failed to bind buffer memory");
}
void Buffer::bind_memory(MemoryBlock&& memory_block) {
bind_memory(memory_block);
m_memory = std::move(memory_block);
}
void Buffer::allocate_and_bind_memory(VkMemoryPropertyFlags memory_properties) {
assert(is_valid());
assert(*m_context);
const auto memory_requirements = this->memory_requirements();
m_memory = m_context->allocator().allocate(
memory_requirements.size,
memory_requirements.memoryTypeBits,
memory_properties
);
bind_memory(m_memory);
}
void Buffer::upload(size_t size, void* src_buffer, uint32_t dst_queue_family) {
assert(is_valid());
assert(*m_context);
const bool use_staging_buffer =
(m_memory.memory_type_info(*m_context).propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0;
if(use_staging_buffer) {
auto staging_buffer = Buffer(
*m_context,
size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
);
void* dst_buffer = staging_buffer.memory().map(*m_context);
std::memcpy(dst_buffer, src_buffer, size);
staging_buffer.memory().unmap(*m_context);
m_context->copy_buffer(
m_buffer,
staging_buffer,
dst_queue_family,
size
);
staging_buffer.destroy();
}
else {
void* dst_buffer = m_memory.map(*m_context);
std::memcpy(dst_buffer, src_buffer, size);
m_memory.unmap(*m_context);
}
}
void Buffer::destroy() noexcept {
assert(is_valid());
assert(*m_context);
if(m_memory) {
m_memory.free();
m_memory = MemoryBlock();
}
vkDestroyBuffer(
m_context->device(),
m_buffer,
nullptr
);
m_context = nullptr;
m_buffer = VK_NULL_HANDLE;
}
}