Files
Seele/src/Engine/Graphics/Vulkan/Allocator.cpp
T

441 lines
15 KiB
C++
Raw Normal View History

2023-10-26 18:37:29 +02:00
#include "Allocator.h"
#include "Graphics.h"
#include "Initializer.h"
using namespace Seele::Vulkan;
2023-11-05 10:36:01 +01:00
SubAllocation::SubAllocation(PAllocation owner, VkDeviceSize allocatedOffset, VkDeviceSize size, VkDeviceSize alignedOffset, VkDeviceSize allocatedSize)
: owner(owner)
, size(size)
, allocatedOffset(allocatedOffset)
, alignedOffset(alignedOffset)
, allocatedSize(allocatedSize)
{
}
2020-04-12 15:47:19 +02:00
SubAllocation::~SubAllocation()
{
owner->markFree(this);
2020-04-12 15:47:19 +02:00
}
2023-11-06 14:47:21 +01:00
VkDeviceMemory SubAllocation::getHandle() const
2020-04-12 15:47:19 +02:00
{
return owner->getHandle();
2020-04-12 15:47:19 +02:00
}
2023-11-06 14:47:21 +01:00
bool SubAllocation::isReadable() const
2020-04-12 15:47:19 +02:00
{
return owner->isReadable();
2020-04-12 15:47:19 +02:00
}
2020-05-05 01:51:13 +02:00
void *SubAllocation::getMappedPointer()
2020-04-12 15:47:19 +02:00
{
return (uint8 *)owner->getMappedPointer() + alignedOffset;
2020-04-12 15:47:19 +02:00
}
void SubAllocation::flushMemory()
{
owner->flushMemory();
2020-04-12 15:47:19 +02:00
}
void SubAllocation::invalidateMemory()
{
owner->invalidateMemory();
2020-04-12 15:47:19 +02:00
}
2023-11-01 23:12:30 +01:00
Allocation::Allocation(PGraphics graphics, PAllocator allocator, VkDeviceSize size, uint8 memoryTypeIndex,
VkMemoryPropertyFlags properties, VkMemoryDedicatedAllocateInfo *dedicatedInfo)
: device(graphics->getDevice())
, allocator(allocator)
, bytesAllocated(0)
, bytesUsed(0)
, readable(properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
, properties(properties)
, memoryTypeIndex(memoryTypeIndex)
{
VkMemoryAllocateInfo allocInfo =
init::MemoryAllocateInfo();
allocInfo.allocationSize = size;
allocInfo.memoryTypeIndex = memoryTypeIndex;
isDedicated = dedicatedInfo != nullptr;
allocInfo.pNext = dedicatedInfo;
VK_CHECK(vkAllocateMemory(device, &allocInfo, nullptr, &allocatedMemory));
bytesAllocated = size;
2023-11-05 10:36:01 +01:00
freeRanges[0] = new SubAllocation(this, 0, size, 0, size);
2020-04-12 15:47:19 +02:00
canMap = (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
isMapped = false;
}
2020-04-12 15:47:19 +02:00
Allocation::~Allocation()
{
}
2023-11-05 10:36:01 +01:00
OSubAllocation Allocation::getSuballocation(VkDeviceSize requestedSize, VkDeviceSize alignment)
{
2022-01-12 14:40:26 +01:00
std::scoped_lock lck(lock);
if (isDedicated)
{
if (activeAllocations.empty() && requestedSize == bytesAllocated)
{
2023-11-05 10:36:01 +01:00
OSubAllocation suballoc = std::move(freeRanges[0]);
activeAllocations.add(suballoc);
freeRanges.clear();
bytesUsed += requestedSize;
return suballoc;
}
else
{
return nullptr;
}
}
2023-11-07 16:55:13 +01:00
for (auto& [allocatedOffset, freeAllocation] : freeRanges)
{
2022-04-15 23:45:44 +02:00
assert(allocatedOffset == freeAllocation->allocatedOffset);
2023-11-05 10:36:01 +01:00
VkDeviceSize alignedOffset = allocatedOffset + alignment - 1;
alignedOffset /= alignment;
alignedOffset *= alignment;
VkDeviceSize allocatedSize = requestedSize + (alignedOffset - allocatedOffset);
if (freeAllocation->size == allocatedSize)
{
2023-11-05 10:36:01 +01:00
activeAllocations.add(freeAllocation);
2022-04-17 09:10:20 +02:00
freeRanges.erase(allocatedOffset);
2023-11-05 10:36:01 +01:00
bytesUsed += allocatedSize;
return std::move(freeAllocation);
}
2023-11-05 10:36:01 +01:00
else if (allocatedSize < freeAllocation->allocatedSize)
{
2023-11-05 10:36:01 +01:00
freeAllocation->size -= allocatedSize;
freeAllocation->allocatedSize -= allocatedSize;
freeAllocation->allocatedOffset += allocatedSize;
freeAllocation->alignedOffset += allocatedSize;
OSubAllocation subAlloc = new SubAllocation(this, allocatedOffset, allocatedSize, alignedOffset, allocatedSize);
activeAllocations.add(subAlloc);
freeRanges[freeAllocation->allocatedOffset] = std::move(freeAllocation);
2022-04-15 23:45:44 +02:00
freeRanges.erase(allocatedOffset);
2023-11-05 10:36:01 +01:00
bytesUsed += allocatedSize;
return subAlloc;
}
}
return nullptr;
}
2023-11-05 10:36:01 +01:00
void Allocation::markFree(PSubAllocation allocation)
2020-04-12 15:47:19 +02:00
{
// Dont free if it is already a free allocation, since they also mark themselves on deletion
if (freeRanges.find(allocation->allocatedOffset) != freeRanges.end())
{
return;
}
VkDeviceSize lowerBound = allocation->allocatedOffset;
VkDeviceSize upperBound = allocation->allocatedOffset + allocation->allocatedSize;
PSubAllocation allocHandle;
PSubAllocation freeRangeToDelete;
2020-06-02 11:46:18 +02:00
{
2022-01-12 14:40:26 +01:00
std::scoped_lock lck(lock);
2021-11-19 15:08:56 +01:00
//Join lower bound
2022-04-15 23:45:44 +02:00
for (auto& [allocatedOffset, freeAlloc] : freeRanges)
{
2021-11-19 15:08:56 +01:00
if (freeAlloc->allocatedOffset <= lowerBound
&& freeAlloc->allocatedOffset + freeAlloc->allocatedSize >= upperBound)
{
// allocation is already in a free region
2023-11-05 10:36:01 +01:00
assert(false);
2021-11-19 15:08:56 +01:00
}
if (freeAlloc->allocatedOffset + freeAlloc->allocatedSize == lowerBound)
{
//extend freeAlloc by the allocatedSize
freeAlloc->allocatedSize += allocation->allocatedSize;
allocHandle = freeAlloc;
break;
}
}
2021-11-19 15:08:56 +01:00
//Join upper bound
auto foundAlloc = freeRanges.find(upperBound);
if (foundAlloc != freeRanges.end())
{
2021-11-19 15:08:56 +01:00
// There is a free allocation ending where the new free one ends
2023-11-05 10:36:01 +01:00
freeRangeToDelete = foundAlloc->value;
2021-11-19 15:08:56 +01:00
if (allocHandle != nullptr)
{
// extend allocHandle by another foundAlloc->allocatedSize bytes
2023-11-05 10:36:01 +01:00
allocHandle->allocatedSize += foundAlloc->value->allocatedSize;
freeRanges.erase(foundAlloc->key);
2021-11-19 15:08:56 +01:00
}
else
{
// set foundAlloc back by size amount
2023-11-05 10:36:01 +01:00
allocHandle = foundAlloc->value;
2021-11-19 15:08:56 +01:00
allocHandle->allocatedOffset -= allocation->allocatedSize;
allocHandle->alignedOffset -= allocation->allocatedSize;
allocHandle->size += allocation->allocatedSize;
allocHandle->allocatedSize += allocation->allocatedSize;
2023-11-05 10:36:01 +01:00
// place back at correct offset, move original owning pointer
freeRanges[allocHandle->allocatedOffset] = std::move(foundAlloc->value);
2021-11-19 15:08:56 +01:00
// remove from offset map since key changes
2023-11-05 10:36:01 +01:00
freeRanges.erase(foundAlloc->key);
2021-11-19 15:08:56 +01:00
}
}
2021-11-19 15:08:56 +01:00
if (allocHandle == nullptr)
{
2023-11-05 10:36:01 +01:00
freeRanges[allocation->allocatedOffset] = new SubAllocation(this, allocation->allocatedOffset, allocation->size, allocation->alignedOffset, allocation->allocatedSize);
2021-11-19 15:08:56 +01:00
}
2023-11-05 10:36:01 +01:00
activeAllocations.remove_if([&](const PSubAllocation& a) {return a.getHandle() == allocation.getHandle(); });
}
bytesUsed -= allocation->allocatedSize;
2022-04-17 09:10:20 +02:00
if(bytesUsed == 0)
{
allocator->free(this);
}
2020-04-12 15:47:19 +02:00
}
2023-11-05 10:36:01 +01:00
void Allocation::flushMemory()
{
VkMappedMemoryRange range = {
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.pNext = 0,
.memory = allocatedMemory,
.offset = 0,
.size = bytesAllocated,
};
vkFlushMappedMemoryRanges(device, 1, &range);
}
void Allocation::invalidateMemory()
{
VkMappedMemoryRange range = {
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.pNext = 0,
.memory = allocatedMemory,
.size = bytesAllocated,
};
vkInvalidateMappedMemoryRanges(device, 1, &range);
}
2020-04-01 02:17:49 +02:00
Allocator::Allocator(PGraphics graphics)
: graphics(graphics)
2020-03-13 12:44:33 +01:00
{
vkGetPhysicalDeviceMemoryProperties(graphics->getPhysicalDevice(), &memProperties);
2023-11-05 10:36:01 +01:00
heaps.reserve(memProperties.memoryHeapCount);
for (size_t i = 0; i < memProperties.memoryHeapCount; ++i)
{
VkMemoryHeap memoryHeap = memProperties.memoryHeaps[i];
2023-11-05 10:36:01 +01:00
HeapInfo heapInfo;
heapInfo.maxSize = memoryHeap.size;
2023-11-06 14:47:21 +01:00
std::cout << "Creating heap " << i << " with properties " << memoryHeap.flags << " size " << memoryHeap.size << std::endl;
2023-11-05 10:36:01 +01:00
heaps.add(std::move(heapInfo));
}
}
Allocator::~Allocator()
{
2022-01-12 14:40:26 +01:00
std::scoped_lock lck(lock);
2023-11-05 10:36:01 +01:00
for (auto& heap : heaps)
{
2023-11-05 10:36:01 +01:00
for (auto& alloc : heap.allocations)
{
assert(alloc->activeAllocations.empty());
assert(alloc->freeRanges.size() == 1);
}
heap.allocations.clear();
}
graphics = nullptr;
}
2023-11-05 10:36:01 +01:00
OSubAllocation Allocator::allocate(const VkMemoryRequirements2 &memRequirements2, VkMemoryPropertyFlags properties, VkMemoryDedicatedAllocateInfo *dedicatedInfo)
{
2022-01-12 14:40:26 +01:00
std::scoped_lock lck(lock);
const VkMemoryRequirements &requirements = memRequirements2.memoryRequirements;
2023-02-13 14:56:13 +01:00
uint32 memoryTypeIndex = findMemoryType(requirements.memoryTypeBits, properties);
uint32 heapIndex = memProperties.memoryTypes[memoryTypeIndex].heapIndex;
2020-05-05 01:51:13 +02:00
if (memRequirements2.pNext != nullptr)
{
VkMemoryDedicatedRequirements *dedicatedReq = (VkMemoryDedicatedRequirements *)memRequirements2.pNext;
if (dedicatedReq->prefersDedicatedAllocation)
{
2023-11-05 10:36:01 +01:00
OAllocation newAllocation = new Allocation(graphics, this, requirements.size, memoryTypeIndex, properties, dedicatedInfo);
2022-04-17 09:10:20 +02:00
heaps[heapIndex].inUse += newAllocation->bytesAllocated;
2023-11-06 14:47:21 +01:00
std::cout << "Heap " << heapIndex << " +" <<newAllocation->bytesAllocated << ": " << (float)heaps[heapIndex].inUse / heaps[heapIndex].maxSize << "%" << std::endl;
2023-11-05 10:36:01 +01:00
heaps[heapIndex].allocations.add(std::move(newAllocation));
return heaps[heapIndex].allocations.back()->getSuballocation(requirements.size, requirements.alignment);
2021-12-22 11:42:07 +01:00
}
}
2023-11-05 10:36:01 +01:00
for (auto& alloc : heaps[heapIndex].allocations)
{
2023-02-13 14:56:13 +01:00
if(alloc->memoryTypeIndex == memoryTypeIndex)
{
2023-11-05 10:36:01 +01:00
OSubAllocation suballoc = alloc->getSuballocation(requirements.size, requirements.alignment);
2023-02-13 14:56:13 +01:00
if (suballoc != nullptr)
{
return suballoc;
}
}
}
2020-05-05 01:51:13 +02:00
// no suitable allocations found, allocate new block
2023-11-05 10:36:01 +01:00
OAllocation newAllocation = new Allocation(graphics, this, (requirements.size > MemoryBlockSize) ? requirements.size : (VkDeviceSize)MemoryBlockSize, memoryTypeIndex, properties, nullptr);
2022-04-17 09:10:20 +02:00
heaps[heapIndex].inUse += newAllocation->bytesAllocated;
2023-11-06 14:47:21 +01:00
std::cout << "Heap " << heapIndex << " +" <<newAllocation->bytesAllocated << ": " << (float)heaps[heapIndex].inUse / heaps[heapIndex].maxSize << "%" << std::endl; heaps[heapIndex].allocations.add(std::move(newAllocation));
2023-11-05 10:36:01 +01:00
return heaps[heapIndex].allocations.back()->getSuballocation(requirements.size, requirements.alignment);
}
2023-11-07 16:55:13 +01:00
void Allocator::free(PAllocation allocation)
{
2022-01-12 14:40:26 +01:00
std::scoped_lock lck(lock);
2023-11-07 16:55:13 +01:00
for (uint32 heapIndex = 0; heapIndex < heaps.size(); ++heapIndex)
{
2023-11-07 16:55:13 +01:00
for (uint32 alloc = 0; alloc < heaps[heapIndex].allocations.size(); ++alloc)
{
2023-11-07 16:55:13 +01:00
if (heaps[heapIndex].allocations[alloc] == allocation)
{
2023-11-07 16:55:13 +01:00
heaps[heapIndex].inUse -= allocation->bytesAllocated;
std::cout << "Heap " << heapIndex << " -" <<allocation->bytesAllocated << ": " << (float)heaps[heapIndex].inUse / heaps[heapIndex].maxSize << "%" << std::endl;
heaps[heapIndex].allocations.removeAt(heapIndex, false);
return;
}
}
}
2020-04-12 15:47:19 +02:00
}
2023-02-13 14:56:13 +01:00
uint32 Allocator::findMemoryType(uint32 typeFilter, VkMemoryPropertyFlags properties)
2020-04-12 15:47:19 +02:00
{
2023-02-13 14:56:13 +01:00
for (uint32 i = 0; i < memProperties.memoryTypeCount; i++)
{
2023-02-13 14:56:13 +01:00
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
{
2023-02-13 14:56:13 +01:00
return i;
}
}
2020-05-05 01:51:13 +02:00
2023-02-13 14:56:13 +01:00
throw std::runtime_error("error finding memory");
2020-04-12 15:47:19 +02:00
}
2023-11-05 10:36:01 +01:00
StagingBuffer::StagingBuffer(OSubAllocation allocation, VkBuffer buffer, VkBufferUsageFlags usage, uint8 readable)
: allocation(std::move(allocation))
, buffer(buffer)
, usage(usage)
, readable(readable)
2020-04-12 15:47:19 +02:00
{
}
StagingBuffer::~StagingBuffer()
{
2023-11-05 10:36:01 +01:00
assert(allocation == nullptr);
// buffer went out of scope without being cleaned up
}
void* StagingBuffer::getMappedPointer()
{
return allocation->getMappedPointer();
}
void StagingBuffer::flushMappedMemory()
{
allocation->flushMemory();
}
void StagingBuffer::invalidateMemory()
{
allocation->invalidateMemory();
}
2023-11-06 14:47:21 +01:00
VkDeviceMemory StagingBuffer::getMemoryHandle() const
2023-11-05 10:36:01 +01:00
{
return allocation->getHandle();
}
2023-11-06 14:47:21 +01:00
VkDeviceSize StagingBuffer::getOffset() const
2023-11-05 10:36:01 +01:00
{
return allocation->getOffset();
}
2023-11-06 14:47:21 +01:00
uint64 StagingBuffer::getSize() const
2023-11-05 10:36:01 +01:00
{
return allocation->getSize();
}
2020-04-12 15:47:19 +02:00
StagingManager::StagingManager(PGraphics graphics, PAllocator allocator)
: graphics(graphics), allocator(allocator)
2020-04-12 15:47:19 +02:00
{
}
StagingManager::~StagingManager()
{
}
void StagingManager::clearPending()
{
}
2023-11-05 10:36:01 +01:00
OStagingBuffer StagingManager::allocateStagingBuffer(uint64 size, VkBufferUsageFlags usage, bool readable)
2020-04-12 15:47:19 +02:00
{
2022-01-12 14:40:26 +01:00
std::scoped_lock l(lock);
for (auto it = freeBuffers.begin(); it != freeBuffers.end(); ++it)
{
2023-11-05 10:36:01 +01:00
auto& freeBuffer = *it;
if (freeBuffer->getSize() == size && freeBuffer->isReadable() == readable && freeBuffer->getUsage() == usage)
{
//std::cout << "Reusing staging buffer" << std::endl;
2023-11-05 10:36:01 +01:00
activeBuffers.add(freeBuffer);
2023-11-06 14:47:21 +01:00
OStagingBuffer owner = std::move(freeBuffer);
freeBuffers.remove(it, false);
2023-11-06 14:47:21 +01:00
return owner;
}
}
//std::cout << "Creating new stagingbuffer" << std::endl;
2023-11-05 10:36:01 +01:00
VkBuffer buffer;
VkBufferCreateInfo stagingBufferCreateInfo = init::BufferCreateInfo(usage, size);
stagingBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
2022-01-12 14:40:26 +01:00
uint32 queueIndex = graphics->getFamilyMapping().getQueueTypeFamilyIndex(Gfx::QueueType::DEDICATED_TRANSFER);
stagingBufferCreateInfo.queueFamilyIndexCount = 1;
stagingBufferCreateInfo.pQueueFamilyIndices = &queueIndex;
VkDevice vulkanDevice = graphics->getDevice();
2020-04-12 15:47:19 +02:00
2023-11-05 10:36:01 +01:00
VK_CHECK(vkCreateBuffer(vulkanDevice, &stagingBufferCreateInfo, nullptr, &buffer));
2020-04-12 15:47:19 +02:00
2023-11-05 10:36:01 +01:00
VkMemoryDedicatedRequirements dedicatedReqs = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS,
.pNext = nullptr,
};
VkMemoryRequirements2 memReqs = {
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
.pNext = &dedicatedReqs,
};
VkBufferMemoryRequirementsInfo2 bufferQuery = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2,
.pNext = nullptr,
.buffer = buffer,
};
vkGetBufferMemoryRequirements2(vulkanDevice, &bufferQuery, &memReqs);
2020-04-12 15:47:19 +02:00
memReqs.memoryRequirements.alignment =
(16 > memReqs.memoryRequirements.alignment) ? 16 : memReqs.memoryRequirements.alignment;
2020-05-05 01:51:13 +02:00
2023-11-05 10:36:01 +01:00
OStagingBuffer stagingBuffer = new StagingBuffer(
allocator->allocate(memReqs, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | (readable ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT : VK_MEMORY_PROPERTY_HOST_CACHED_BIT), buffer),
buffer,
usage,
readable
);
vkBindBufferMemory(graphics->getDevice(), buffer, stagingBuffer->getMemoryHandle(), stagingBuffer->getOffset());
2023-11-06 14:47:21 +01:00
std::cout << "Creating new stagingbuffer size " << stagingBuffer->getSize() << std::endl;
2023-11-05 10:36:01 +01:00
activeBuffers.add(stagingBuffer);
return stagingBuffer;
2020-04-12 15:47:19 +02:00
}
2023-11-05 10:36:01 +01:00
void StagingManager::releaseStagingBuffer(OStagingBuffer buffer)
2020-04-12 15:47:19 +02:00
{
2023-11-07 16:55:13 +01:00
if (buffer == nullptr)
2023-11-06 14:47:21 +01:00
{
return;
}
2023-11-07 16:55:13 +01:00
std::scoped_lock l(lock);
2023-11-05 10:36:01 +01:00
activeBuffers.remove(buffer);
2023-11-06 14:47:21 +01:00
std::cout << "Releasing stagingbuffer size " << buffer->getSize() << std::endl;
2023-11-05 10:36:01 +01:00
freeBuffers.add(std::move(buffer));
}