10 changed files with 918 additions and 189 deletions
@ -0,0 +1,186 @@ |
|||||
|
#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; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <utils.h> |
||||
|
|
||||
|
#include <Vulkan/forward.h> |
||||
|
#include <Vulkan/Memory.h> |
||||
|
|
||||
|
#include <vulkan/vulkan.h> |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <vector> |
||||
|
|
||||
|
|
||||
|
namespace Vulkan { |
||||
|
|
||||
|
|
||||
|
class Buffer { |
||||
|
public: |
||||
|
Buffer() noexcept; |
||||
|
Buffer( |
||||
|
Context& context, |
||||
|
VkDeviceSize size, |
||||
|
VkBufferUsageFlags usage |
||||
|
); |
||||
|
Buffer( |
||||
|
Context& context, |
||||
|
VkDeviceSize size, |
||||
|
VkBufferUsageFlags usage, |
||||
|
VkMemoryPropertyFlags memory_properties |
||||
|
); |
||||
|
Buffer(const Buffer&) = delete; |
||||
|
Buffer(Buffer&& other) noexcept; |
||||
|
~Buffer() noexcept; |
||||
|
|
||||
|
Buffer& operator=(const Buffer&) = delete; |
||||
|
Buffer& operator=(Buffer&& other) noexcept; |
||||
|
|
||||
|
inline explicit operator bool() const { |
||||
|
return is_valid(); |
||||
|
} |
||||
|
|
||||
|
inline bool is_valid() const { |
||||
|
return m_buffer != VK_NULL_HANDLE; |
||||
|
} |
||||
|
|
||||
|
inline Context* context() noexcept { |
||||
|
return m_context; |
||||
|
} |
||||
|
|
||||
|
inline const Context* context() const noexcept { |
||||
|
return m_context; |
||||
|
} |
||||
|
|
||||
|
inline operator VkBuffer() const noexcept { |
||||
|
return m_buffer; |
||||
|
} |
||||
|
|
||||
|
inline VkBuffer buffer() const noexcept { |
||||
|
return m_buffer; |
||||
|
} |
||||
|
|
||||
|
inline const MemoryBlock& memory() const noexcept { |
||||
|
return m_memory; |
||||
|
} |
||||
|
|
||||
|
inline MemoryBlock& memory() noexcept { |
||||
|
return m_memory; |
||||
|
} |
||||
|
|
||||
|
VkMemoryRequirements memory_requirements() const noexcept; |
||||
|
|
||||
|
void bind_memory(const MemoryBlock& memory_block, VkDeviceSize offset=0); |
||||
|
void bind_memory(MemoryBlock&& memory_block); |
||||
|
void allocate_and_bind_memory(VkMemoryPropertyFlags memory_properties); |
||||
|
|
||||
|
void upload(size_t size, void* src_buffer, uint32_t dst_queue_family); |
||||
|
|
||||
|
void destroy() noexcept; |
||||
|
|
||||
|
private: |
||||
|
Context* m_context = nullptr; |
||||
|
VkBuffer m_buffer = VK_NULL_HANDLE; |
||||
|
MemoryBlock m_memory; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,375 @@ |
|||||
|
#include <Vulkan/Memory.h> |
||||
|
#include <Vulkan/Context.h> |
||||
|
|
||||
|
#include <stdexcept> |
||||
|
#include <algorithm> |
||||
|
#include <cassert> |
||||
|
|
||||
|
|
||||
|
namespace Vulkan { |
||||
|
|
||||
|
|
||||
|
MemoryBlock::MemoryBlock() noexcept { |
||||
|
} |
||||
|
|
||||
|
MemoryBlock::MemoryBlock( |
||||
|
VkDeviceMemory device_memory, |
||||
|
VkDeviceSize size, |
||||
|
VkDeviceSize offset, |
||||
|
MemoryPage* memory_page, |
||||
|
uint32_t memory_type |
||||
|
) noexcept |
||||
|
: m_size(size) |
||||
|
, m_offset(offset) |
||||
|
, m_device_memory(device_memory) |
||||
|
, m_memory_page(memory_page) |
||||
|
, m_memory_type(memory_type) |
||||
|
{} |
||||
|
|
||||
|
MemoryBlock::MemoryBlock(MemoryBlock&& other) noexcept |
||||
|
: m_size(other.m_size) |
||||
|
, m_offset(other.m_offset) |
||||
|
, m_device_memory(other.m_device_memory) |
||||
|
, m_memory_page(other.m_memory_page) |
||||
|
, m_memory_type(other.m_memory_type) |
||||
|
{ |
||||
|
other.m_size = 0; |
||||
|
other.m_offset = 0; |
||||
|
other.m_device_memory = VK_NULL_HANDLE; |
||||
|
other.m_memory_page = nullptr; |
||||
|
other.m_memory_type = 0; |
||||
|
} |
||||
|
|
||||
|
MemoryBlock::~MemoryBlock() noexcept { |
||||
|
if(is_valid()) { |
||||
|
logger.warning() << "MemoryBlock deleted before being freed"; |
||||
|
free(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
MemoryBlock& MemoryBlock::operator=(MemoryBlock&& other) noexcept { |
||||
|
if(&other != this) { |
||||
|
using std::swap; |
||||
|
swap(m_size, other.m_size); |
||||
|
swap(m_offset, other.m_offset); |
||||
|
swap(m_device_memory, other.m_device_memory); |
||||
|
swap(m_memory_page, other.m_memory_page); |
||||
|
swap(m_memory_type, other.m_memory_type); |
||||
|
} |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
VkMemoryType MemoryBlock::memory_type_info(Context& context) const noexcept { |
||||
|
assert(is_valid()); |
||||
|
assert(context); |
||||
|
|
||||
|
return context.memory_properties().memoryTypes[m_memory_type]; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void MemoryBlock::free() noexcept { |
||||
|
assert(is_valid()); |
||||
|
|
||||
|
m_memory_page->p_free(*this); |
||||
|
|
||||
|
m_size = 0; |
||||
|
m_offset = 0; |
||||
|
m_device_memory = VK_NULL_HANDLE; |
||||
|
m_memory_page = nullptr; |
||||
|
m_memory_type = 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void* MemoryBlock::map(Context& context) { |
||||
|
return map(context, 0, m_size); |
||||
|
} |
||||
|
|
||||
|
void* MemoryBlock::map(Context& context, VkDeviceSize offset, VkDeviceSize size) { |
||||
|
assert(is_valid()); |
||||
|
assert(context); |
||||
|
assert(offset + size <= m_size); |
||||
|
|
||||
|
void* ptr; |
||||
|
if(vkMapMemory( |
||||
|
context.device(), |
||||
|
m_device_memory, |
||||
|
m_offset + offset, |
||||
|
size, |
||||
|
0, |
||||
|
&ptr |
||||
|
) != VK_SUCCESS) |
||||
|
throw std::runtime_error("failed to map memory"); |
||||
|
|
||||
|
return ptr; |
||||
|
} |
||||
|
|
||||
|
void MemoryBlock::unmap(Context& context) noexcept { |
||||
|
assert(is_valid()); |
||||
|
|
||||
|
vkUnmapMemory( |
||||
|
context.device(), |
||||
|
m_device_memory |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void MemoryBlock::flush(Context& context) { |
||||
|
assert(is_valid()); |
||||
|
|
||||
|
VkMappedMemoryRange range { |
||||
|
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, |
||||
|
.memory = m_device_memory, |
||||
|
.offset = m_offset, |
||||
|
.size = m_size, |
||||
|
}; |
||||
|
if(vkFlushMappedMemoryRanges( |
||||
|
context.device(), |
||||
|
1, |
||||
|
&range |
||||
|
) != VK_SUCCESS) |
||||
|
std::runtime_error("failed to flush memory"); |
||||
|
} |
||||
|
|
||||
|
void MemoryBlock::invalidate(Context& context) { |
||||
|
assert(is_valid()); |
||||
|
|
||||
|
VkMappedMemoryRange range { |
||||
|
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, |
||||
|
.memory = m_device_memory, |
||||
|
.offset = m_offset, |
||||
|
.size = m_size, |
||||
|
}; |
||||
|
if(vkInvalidateMappedMemoryRanges( |
||||
|
context.device(), |
||||
|
1, |
||||
|
&range |
||||
|
) != VK_SUCCESS) |
||||
|
std::runtime_error("failed to invalidate memory"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
MemoryPage::MemoryPage( |
||||
|
VkDeviceSize size, |
||||
|
VkDeviceMemory device_memory, |
||||
|
uint32_t memory_type |
||||
|
) noexcept |
||||
|
: m_size(size) |
||||
|
, m_device_memory(device_memory) |
||||
|
, m_blocks{ |
||||
|
Block{ 0, true }, |
||||
|
Block{ m_size, false }, |
||||
|
} |
||||
|
, m_memory_type(memory_type) |
||||
|
{} |
||||
|
|
||||
|
MemoryPage::MemoryPage(MemoryPage&&) = default; |
||||
|
MemoryPage::~MemoryPage() = default; |
||||
|
|
||||
|
MemoryPage& MemoryPage::operator=(MemoryPage&&) = default; |
||||
|
|
||||
|
MemoryBlock MemoryPage::allocate(VkDeviceSize size) noexcept { |
||||
|
const auto block_it = find_free_block(size); |
||||
|
if(block_it == m_blocks.end()) |
||||
|
return MemoryBlock(); |
||||
|
|
||||
|
block_it[0].is_free = false; |
||||
|
if (block_it[0].offset != block_it[1].offset) { |
||||
|
m_blocks.emplace(std::next(block_it), Block{ |
||||
|
block_it[0].offset + size, true |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return MemoryBlock(m_device_memory, size, block_it[0].offset, this, m_memory_type); |
||||
|
} |
||||
|
|
||||
|
void MemoryPage::p_free(MemoryBlock& block) noexcept { |
||||
|
assert(block.device_memory() == m_device_memory); |
||||
|
|
||||
|
const auto offset = block.offset(); |
||||
|
const auto block_it = std::lower_bound( |
||||
|
m_blocks.begin(), |
||||
|
m_blocks.end(), |
||||
|
Block{ offset, false }, |
||||
|
[] (const Block& lhs, const Block& rhs) { |
||||
|
return lhs.offset < rhs.offset; |
||||
|
} |
||||
|
); |
||||
|
assert(block_it != m_blocks.end() && block_it->offset == offset); |
||||
|
|
||||
|
// Merge with next block if also free
|
||||
|
if (block_it[1].is_free) { |
||||
|
m_blocks.erase(std::next(block_it)); |
||||
|
} |
||||
|
|
||||
|
// Merge with previous block if also free
|
||||
|
if (block_it != m_blocks.begin() && block_it[-1].is_free) { |
||||
|
m_blocks.erase(block_it); |
||||
|
} |
||||
|
else { |
||||
|
block_it->is_free = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void MemoryPage::free_device_memory(Context& context) noexcept { |
||||
|
vkFreeMemory( |
||||
|
context.device(), |
||||
|
m_device_memory, |
||||
|
nullptr |
||||
|
); |
||||
|
|
||||
|
m_size = 0; |
||||
|
m_device_memory = VK_NULL_HANDLE; |
||||
|
m_blocks.clear(); |
||||
|
m_memory_type = 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
MemoryPage::BlockList::iterator MemoryPage::find_free_block(VkDeviceSize size) { |
||||
|
const auto block_end = std::prev(m_blocks.end()); |
||||
|
for(auto block_it = m_blocks.begin(); block_it != block_end; ++block_it) { |
||||
|
if(block_it[0].is_free && block_it[1].offset - block_it[0].offset >= size) |
||||
|
return block_it; |
||||
|
} |
||||
|
|
||||
|
return m_blocks.end(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
Allocator::Allocator(Context* context) noexcept |
||||
|
: m_context(context) |
||||
|
, m_memory_properties{} |
||||
|
, m_page_map{} |
||||
|
{ |
||||
|
vkGetPhysicalDeviceMemoryProperties( |
||||
|
m_context->physical_device(), |
||||
|
&m_memory_properties |
||||
|
); |
||||
|
|
||||
|
for(auto& next_page_size: m_next_page_sizes) |
||||
|
next_page_size = BasePageSize; |
||||
|
} |
||||
|
|
||||
|
Allocator::~Allocator() = default; |
||||
|
|
||||
|
|
||||
|
int32_t Allocator::find_memory_type( |
||||
|
uint32_t type_mask, |
||||
|
VkMemoryPropertyFlags required_properties |
||||
|
) noexcept { |
||||
|
for(uint32_t type_index = 0; |
||||
|
type_index < m_memory_properties.memoryTypeCount; |
||||
|
type_index += 1 |
||||
|
) { |
||||
|
if((type_mask & (1 << type_index)) == 0) |
||||
|
continue; |
||||
|
|
||||
|
const auto memory_properties = |
||||
|
m_memory_properties.memoryTypes[type_index].propertyFlags; |
||||
|
if((memory_properties & required_properties) == required_properties) |
||||
|
return type_index; |
||||
|
} |
||||
|
|
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
int32_t Allocator::find_memory_type( |
||||
|
uint32_t type_mask, |
||||
|
std::initializer_list<VkMemoryPropertyFlags> properties_list |
||||
|
) noexcept { |
||||
|
for(const auto& properties: properties_list) { |
||||
|
const auto memory_type = find_memory_type(type_mask, properties); |
||||
|
if(memory_type >= 0) |
||||
|
return memory_type; |
||||
|
} |
||||
|
|
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
MemoryBlock Allocator::allocate( |
||||
|
VkDeviceSize size, |
||||
|
uint32_t memory_type |
||||
|
) noexcept { |
||||
|
assert(memory_type < VK_MAX_MEMORY_TYPES); |
||||
|
|
||||
|
auto& pages = m_page_map[memory_type]; |
||||
|
|
||||
|
for(auto& page: pages) { |
||||
|
auto block = page.allocate(size); |
||||
|
if(block) |
||||
|
return block; |
||||
|
} |
||||
|
|
||||
|
const VkDeviceSize new_page_size = std::max( |
||||
|
size, |
||||
|
m_next_page_sizes[memory_type] |
||||
|
); |
||||
|
|
||||
|
VkMemoryAllocateInfo allocate_info { |
||||
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
||||
|
.allocationSize = new_page_size, |
||||
|
.memoryTypeIndex = memory_type, |
||||
|
}; |
||||
|
VkDeviceMemory device_memory; |
||||
|
vkAllocateMemory( |
||||
|
m_context->device(), |
||||
|
&allocate_info, |
||||
|
nullptr, |
||||
|
&device_memory |
||||
|
); |
||||
|
|
||||
|
pages.emplace_back( |
||||
|
new_page_size, |
||||
|
device_memory, |
||||
|
memory_type |
||||
|
); |
||||
|
|
||||
|
if(new_page_size == m_next_page_sizes[memory_type]) |
||||
|
m_next_page_sizes[memory_type] *= 2; |
||||
|
|
||||
|
return pages.back().allocate(size); |
||||
|
} |
||||
|
|
||||
|
MemoryBlock Allocator::allocate( |
||||
|
VkDeviceSize size, |
||||
|
uint32_t type_mask, |
||||
|
VkMemoryPropertyFlags properties |
||||
|
) noexcept { |
||||
|
const auto memory_type = find_memory_type( |
||||
|
type_mask, |
||||
|
properties |
||||
|
); |
||||
|
if(memory_type < 0) |
||||
|
return MemoryBlock(); |
||||
|
|
||||
|
return allocate(size, uint32_t(memory_type)); |
||||
|
} |
||||
|
|
||||
|
MemoryBlock Allocator::allocate( |
||||
|
VkDeviceSize size, |
||||
|
uint32_t type_mask, |
||||
|
std::initializer_list<VkMemoryPropertyFlags> properties_list |
||||
|
) noexcept { |
||||
|
const auto memory_type = find_memory_type( |
||||
|
type_mask, |
||||
|
properties_list |
||||
|
); |
||||
|
if(memory_type < 0) |
||||
|
return MemoryBlock(); |
||||
|
|
||||
|
return allocate(size, uint32_t(memory_type)); |
||||
|
} |
||||
|
|
||||
|
void Allocator::free_all_pages() noexcept { |
||||
|
for(uint32_t memory_type = 0; memory_type < VK_MAX_MEMORY_TYPES; memory_type += 1) { |
||||
|
for(auto& page: m_page_map[memory_type]) |
||||
|
page.free_device_memory(*m_context); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,185 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Vulkan/forward.h> |
||||
|
|
||||
|
#include <vulkan/vulkan.h> |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <vector> |
||||
|
|
||||
|
|
||||
|
namespace Vulkan { |
||||
|
|
||||
|
|
||||
|
class MemoryBlock { |
||||
|
public: |
||||
|
MemoryBlock() noexcept; |
||||
|
MemoryBlock( |
||||
|
VkDeviceMemory device_memory, |
||||
|
VkDeviceSize size, |
||||
|
VkDeviceSize offset, |
||||
|
MemoryPage* memory_page, |
||||
|
uint32_t memory_type |
||||
|
) noexcept; |
||||
|
MemoryBlock(const MemoryBlock&) = delete; |
||||
|
MemoryBlock(MemoryBlock&& other) noexcept; |
||||
|
~MemoryBlock() noexcept; |
||||
|
|
||||
|
MemoryBlock& operator=(const MemoryBlock&) = delete; |
||||
|
MemoryBlock& operator=(MemoryBlock&& other) noexcept; |
||||
|
|
||||
|
inline explicit operator bool() const noexcept { |
||||
|
return m_size != 0; |
||||
|
} |
||||
|
|
||||
|
inline bool is_valid() const noexcept { |
||||
|
return m_size != 0; |
||||
|
} |
||||
|
|
||||
|
inline VkDeviceSize size() const noexcept { |
||||
|
return m_size; |
||||
|
} |
||||
|
|
||||
|
inline VkDeviceSize offset() const noexcept { |
||||
|
return m_offset; |
||||
|
} |
||||
|
|
||||
|
inline VkDeviceMemory device_memory() const noexcept { |
||||
|
return m_device_memory; |
||||
|
} |
||||
|
|
||||
|
inline uint32_t memory_type() const noexcept { |
||||
|
return m_memory_type; |
||||
|
} |
||||
|
|
||||
|
inline MemoryPage* memory_page() const noexcept { |
||||
|
return m_memory_page; |
||||
|
} |
||||
|
|
||||
|
VkMemoryType memory_type_info(Context& context) const noexcept; |
||||
|
|
||||
|
void free() noexcept; |
||||
|
|
||||
|
void* map(Context& context); |
||||
|
void* map(Context& context, VkDeviceSize offset, VkDeviceSize size); |
||||
|
void unmap(Context& context) noexcept; |
||||
|
|
||||
|
void flush(Context& context); |
||||
|
void invalidate(Context& context); |
||||
|
|
||||
|
private: |
||||
|
VkDeviceSize m_size = 0; |
||||
|
VkDeviceSize m_offset = 0; |
||||
|
VkDeviceMemory m_device_memory = VK_NULL_HANDLE; |
||||
|
MemoryPage* m_memory_page = nullptr; |
||||
|
uint32_t m_memory_type = 0; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
class MemoryPage { |
||||
|
public: |
||||
|
MemoryPage( |
||||
|
VkDeviceSize size, |
||||
|
VkDeviceMemory device_memory, |
||||
|
uint32_t memory_type |
||||
|
) noexcept; |
||||
|
MemoryPage(const MemoryPage&) = delete; |
||||
|
MemoryPage(MemoryPage&&); |
||||
|
~MemoryPage(); |
||||
|
|
||||
|
MemoryPage& operator=(const MemoryPage&) = delete; |
||||
|
MemoryPage& operator=(MemoryPage&&); |
||||
|
|
||||
|
explicit inline operator bool() const noexcept { |
||||
|
return is_valid(); |
||||
|
} |
||||
|
|
||||
|
inline bool is_valid() const noexcept { |
||||
|
return m_size != 0; |
||||
|
} |
||||
|
|
||||
|
inline VkDeviceSize size() const noexcept { |
||||
|
return m_size; |
||||
|
} |
||||
|
|
||||
|
inline VkDeviceMemory device_memory() const noexcept { |
||||
|
return m_device_memory; |
||||
|
} |
||||
|
|
||||
|
inline uint32_t memory_type() const noexcept { |
||||
|
return m_memory_type; |
||||
|
} |
||||
|
|
||||
|
MemoryBlock allocate(VkDeviceSize size) noexcept; |
||||
|
void p_free(MemoryBlock& block) noexcept; |
||||
|
|
||||
|
void free_device_memory(Context& context) noexcept; |
||||
|
|
||||
|
private: |
||||
|
struct Block { |
||||
|
VkDeviceSize offset; |
||||
|
bool is_free; |
||||
|
}; |
||||
|
using BlockList = std::vector<Block>; |
||||
|
|
||||
|
private: |
||||
|
BlockList::iterator find_free_block(VkDeviceSize size); |
||||
|
|
||||
|
private: |
||||
|
VkDeviceSize m_size = 0; |
||||
|
VkDeviceMemory m_device_memory = VK_NULL_HANDLE; |
||||
|
BlockList m_blocks; |
||||
|
uint32_t m_memory_type = 0; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
class Allocator { |
||||
|
public: |
||||
|
Allocator(Context* context) noexcept; |
||||
|
Allocator(const Allocator&) = delete; |
||||
|
Allocator(Allocator&&) = delete; |
||||
|
~Allocator(); |
||||
|
|
||||
|
Allocator& operator=(const Allocator&) = delete; |
||||
|
Allocator& operator=(Allocator&) = delete; |
||||
|
|
||||
|
int32_t find_memory_type( |
||||
|
uint32_t type_mask, |
||||
|
VkMemoryPropertyFlags properties |
||||
|
) noexcept; |
||||
|
int32_t find_memory_type( |
||||
|
uint32_t type_mask, |
||||
|
std::initializer_list<VkMemoryPropertyFlags> properties_list |
||||
|
) noexcept; |
||||
|
|
||||
|
MemoryBlock allocate( |
||||
|
VkDeviceSize size, |
||||
|
uint32_t memory_type |
||||
|
) noexcept; |
||||
|
MemoryBlock allocate( |
||||
|
VkDeviceSize size, |
||||
|
uint32_t type_mask, |
||||
|
VkMemoryPropertyFlags properties |
||||
|
) noexcept; |
||||
|
MemoryBlock allocate( |
||||
|
VkDeviceSize size, |
||||
|
uint32_t type_mask, |
||||
|
std::initializer_list<VkMemoryPropertyFlags> properties_list |
||||
|
) noexcept; |
||||
|
|
||||
|
void free_all_pages() noexcept; |
||||
|
|
||||
|
private: |
||||
|
using PageList = std::vector<MemoryPage>; |
||||
|
|
||||
|
static constexpr VkDeviceSize BasePageSize = 1 << 24; |
||||
|
|
||||
|
private: |
||||
|
Context* m_context = nullptr; |
||||
|
VkPhysicalDeviceMemoryProperties m_memory_properties; |
||||
|
PageList m_page_map[VK_MAX_MEMORY_TYPES]; |
||||
|
VkDeviceSize m_next_page_sizes[VK_MAX_MEMORY_TYPES]; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
|
||||
|
#include <memory> |
||||
|
|
||||
|
|
||||
|
namespace Vulkan { |
||||
|
|
||||
|
|
||||
|
class MemoryPage; |
||||
|
class MemoryBlock; |
||||
|
class Allocator; |
||||
|
|
||||
|
using MemoryPageSP = std::shared_ptr<MemoryBlock>; |
||||
|
|
||||
|
class Context; |
||||
|
class ContextSettings; |
||||
|
|
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue