yes
This commit is contained in:
110
Runtime/Sources/Renderer/Memory/Chunk.cpp
git.filemode.normal_file
110
Runtime/Sources/Renderer/Memory/Chunk.cpp
git.filemode.normal_file
@@ -0,0 +1,110 @@
|
||||
#include <Renderer/Memory/Chunk.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct MemoryChunkAllocFailedEvent : public EventBase
|
||||
{
|
||||
Event What() const override { return Event::MemoryChunkAllocationFailed; }
|
||||
};
|
||||
}
|
||||
|
||||
MemoryChunk::MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index, bool is_dedicated, std::uint32_t& vram_usage, std::uint32_t& vram_host_visible_usage)
|
||||
: m_device(device), m_physical(physical), m_size(size), m_memory_type_index(memory_type_index), m_is_dedicated(is_dedicated)
|
||||
{
|
||||
Verify(device != VK_NULL_HANDLE, "Memory Chunk : invalid device");
|
||||
VkMemoryAllocateInfo alloc_info{};
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
alloc_info.allocationSize = size;
|
||||
alloc_info.memoryTypeIndex = m_memory_type_index;
|
||||
if(RenderCore::Get().vkAllocateMemory(m_device, &alloc_info, nullptr, &m_memory) != VK_SUCCESS)
|
||||
{
|
||||
EventBus::Send("__ScopDeviceAllocator", Internal::MemoryChunkAllocFailedEvent{});
|
||||
return;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceMemoryProperties properties;
|
||||
RenderCore::Get().vkGetPhysicalDeviceMemoryProperties(m_physical, &properties);
|
||||
if((properties.memoryTypes[m_memory_type_index].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
|
||||
{
|
||||
if(RenderCore::Get().vkMapMemory(m_device, m_memory, 0, VK_WHOLE_SIZE, 0, &p_map) != VK_SUCCESS)
|
||||
FatalError("Vulkan: failed to map a host visible chunk");
|
||||
vram_host_visible_usage += size;
|
||||
}
|
||||
else
|
||||
vram_usage += size;
|
||||
|
||||
MemoryBlock& block = m_blocks.emplace_back();
|
||||
block.memory = m_memory;
|
||||
block.offset = 0;
|
||||
block.size = size;
|
||||
block.free = true;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MemoryBlock> MemoryChunk::Allocate(VkDeviceSize size, VkDeviceSize alignment)
|
||||
{
|
||||
for(std::size_t i = 0; i < m_blocks.size(); i++)
|
||||
{
|
||||
if(!m_blocks[i].free || m_blocks[i].size < size)
|
||||
continue;
|
||||
VkDeviceSize offset_displacement = (m_blocks[i].offset % alignment != 0) ? alignment - m_blocks[i].offset % alignment : 0;
|
||||
VkDeviceSize old_size_available = m_blocks[i].size - offset_displacement;
|
||||
|
||||
if(size + offset_displacement <= m_blocks[i].size)
|
||||
{
|
||||
m_blocks[i].offset += offset_displacement;
|
||||
m_blocks[i].size = size;
|
||||
m_blocks[i].free = false;
|
||||
|
||||
if(p_map != nullptr)
|
||||
m_blocks[i].map = reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(p_map) + m_blocks[i].offset);
|
||||
|
||||
MemoryBlock new_block;
|
||||
new_block.memory = m_memory;
|
||||
new_block.offset = m_blocks[i].offset + m_blocks[i].size;
|
||||
new_block.size = old_size_available - size;
|
||||
new_block.free = true;
|
||||
|
||||
if(new_block.size > 0)
|
||||
m_blocks.emplace(m_blocks.begin() + i + 1, new_block);
|
||||
return m_blocks[i];
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void MemoryChunk::Deallocate(const MemoryBlock& block)
|
||||
{
|
||||
auto it = std::find(m_blocks.begin(), m_blocks.end(), block);
|
||||
if(it == m_blocks.end())
|
||||
FatalError("Memory Chunk : cannot deallocate a block that is owned by another chunk");
|
||||
it->free = true;
|
||||
|
||||
bool end = false;
|
||||
while(!end)
|
||||
{
|
||||
end = true;
|
||||
for(auto it = m_blocks.begin(); it != m_blocks.end(); ++it)
|
||||
{
|
||||
if(it->free && it + 1 != m_blocks.end() && (it + 1)->free)
|
||||
{
|
||||
it->size += (it + 1)->size;
|
||||
m_blocks.erase(it + 1);
|
||||
end = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemoryChunk::~MemoryChunk()
|
||||
{
|
||||
RenderCore::Get().vkFreeMemory(m_device, m_memory, nullptr);
|
||||
}
|
||||
}
|
||||
112
Runtime/Sources/Renderer/Memory/DeviceAllocator.cpp
git.filemode.normal_file
112
Runtime/Sources/Renderer/Memory/DeviceAllocator.cpp
git.filemode.normal_file
@@ -0,0 +1,112 @@
|
||||
#include <Renderer/Memory/DeviceAllocator.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Maths/Constants.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <Core/EventBus.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
#define AlignUp(val, alignment) ((val + alignment - 1) & ~(alignment - 1))
|
||||
|
||||
void DeviceAllocator::AttachToDevice(VkDevice device, VkPhysicalDevice physical) noexcept
|
||||
{
|
||||
m_device = device;
|
||||
m_physical = physical;
|
||||
|
||||
RenderCore::Get().vkGetPhysicalDeviceMemoryProperties(physical, &m_mem_props);
|
||||
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::MemoryChunkAllocationFailed)
|
||||
m_last_chunk_creation_failed = true;
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopDeviceAllocator" });
|
||||
}
|
||||
|
||||
[[nodiscard]] MemoryBlock DeviceAllocator::Allocate(VkDeviceSize size, VkDeviceSize alignment, std::int32_t memory_type_index, bool dedicated_chunk)
|
||||
{
|
||||
Verify(m_device != VK_NULL_HANDLE, "invalid device");
|
||||
Verify(m_physical != VK_NULL_HANDLE, "invalid physical device");
|
||||
const std::lock_guard<std::mutex> guard(m_alloc_mutex);
|
||||
if(!dedicated_chunk)
|
||||
{
|
||||
for(auto& chunk : m_chunks)
|
||||
{
|
||||
if(chunk->GetMemoryTypeIndex() == memory_type_index)
|
||||
{
|
||||
std::optional<MemoryBlock> block = chunk->Allocate(size, alignment);
|
||||
if(block.has_value())
|
||||
return *block;
|
||||
}
|
||||
}
|
||||
}
|
||||
VkDeviceSize chunk_size = dedicated_chunk ? size + alignment : CalcPreferredChunkSize(memory_type_index);
|
||||
if(chunk_size < size + alignment)
|
||||
chunk_size = size + alignment;
|
||||
m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, chunk_size, memory_type_index, dedicated_chunk, m_vram_usage, m_vram_host_visible_usage));
|
||||
|
||||
if(m_last_chunk_creation_failed && !dedicated_chunk)
|
||||
{
|
||||
// Allocation of this size failed? Try 1/2, 1/4, 1/8 of preferred chunk size.
|
||||
std::uint32_t new_block_size_shift = 0;
|
||||
while(m_last_chunk_creation_failed && new_block_size_shift < NEW_BLOCK_SIZE_SHIFT_MAX)
|
||||
{
|
||||
m_last_chunk_creation_failed = false;
|
||||
m_chunks.pop_back();
|
||||
chunk_size /= 2;
|
||||
if(chunk_size < size + alignment)
|
||||
{
|
||||
m_last_chunk_creation_failed = true;
|
||||
break;
|
||||
}
|
||||
m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, chunk_size, memory_type_index, false, m_vram_usage, m_vram_host_visible_usage));
|
||||
}
|
||||
}
|
||||
|
||||
// If we could not recover from allocation failure
|
||||
if(m_last_chunk_creation_failed)
|
||||
FatalError("Device Allocator: could not allocate a memory chunk");
|
||||
|
||||
std::optional<MemoryBlock> block = m_chunks.back()->Allocate(size, alignment);
|
||||
m_allocations_count++;
|
||||
if(block.has_value())
|
||||
return *block;
|
||||
FatalError("Device Allocator: could not allocate a memory block");
|
||||
return {}; // to avoid warnings
|
||||
}
|
||||
|
||||
void DeviceAllocator::Deallocate(const MemoryBlock& block)
|
||||
{
|
||||
Verify(m_device != VK_NULL_HANDLE, "invalid device");
|
||||
Verify(m_physical != VK_NULL_HANDLE, "invalid physical device");
|
||||
const std::lock_guard<std::mutex> guard(m_dealloc_mutex);
|
||||
for(auto it = m_chunks.begin(); it != m_chunks.end(); ++it)
|
||||
{
|
||||
if((*it)->Has(block))
|
||||
{
|
||||
(*it)->Deallocate(block);
|
||||
if((*it)->IsDedicated())
|
||||
{
|
||||
if((*it)->GetMap() != nullptr) // If it is host visible
|
||||
m_vram_host_visible_usage -= (*it)->GetSize();
|
||||
else
|
||||
m_vram_usage -= (*it)->GetSize();
|
||||
m_chunks.erase(it);
|
||||
m_allocations_count--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
Error("Device Allocator: unable to free a block; could not find it's chunk");
|
||||
}
|
||||
|
||||
VkDeviceSize DeviceAllocator::CalcPreferredChunkSize(std::uint32_t mem_type_index)
|
||||
{
|
||||
std::uint32_t heap_index = m_mem_props.memoryTypes[mem_type_index].heapIndex;
|
||||
VkDeviceSize heap_size = m_mem_props.memoryHeaps[heap_index].size;
|
||||
bool is_small_heap = heap_size <= SMALL_HEAP_MAX_SIZE;
|
||||
return AlignUp((is_small_heap ? (heap_size / 8) : DEFAULT_LARGE_HEAP_BLOCK_SIZE), (VkDeviceSize)32);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user