#include #include #include #include #include #include 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; } }