Added allocator for buffers
This commit is contained in:
parent
4b500bed23
commit
63b08cd219
136
src/vulkan_allocator.cpp
Normal file
136
src/vulkan_allocator.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "vulkan_allocator.hpp"
|
||||
|
||||
#include "exceptions.hpp"
|
||||
#include "vulkan_instance.hpp"
|
||||
|
||||
#include <glm/vector_relational.hpp>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace gz::vk {
|
||||
struct MemoryBlock {
|
||||
size_t size;
|
||||
size_t offset;
|
||||
bool free;
|
||||
};
|
||||
|
||||
struct DeviceMemory {
|
||||
DeviceMemory() = delete;
|
||||
DeviceMemory(VkDeviceSize size_) {
|
||||
size = size_;
|
||||
memory = VK_NULL_HANDLE;
|
||||
blocks.emplace_back(MemoryBlock{size, 0, true});
|
||||
};
|
||||
|
||||
VkDeviceSize size;
|
||||
VkDeviceMemory memory;
|
||||
std::list<MemoryBlock> blocks;
|
||||
};
|
||||
|
||||
|
||||
VulkanAllocator::VulkanAllocator(VulkanInstance& instance)
|
||||
: vk(instance)
|
||||
{
|
||||
LogCreateInfo logCI{};
|
||||
logCI.logfile = "vulkan_allocator.log";
|
||||
logCI.storeLog = false;
|
||||
logCI.prefix = "Allocator";
|
||||
logCI.prefixColor = Color::BLACK;
|
||||
logCI.timeColor = VULKAN_MESSAGE_TIME_COLOR;
|
||||
aLog = Log(std::move(logCI));
|
||||
|
||||
}
|
||||
|
||||
void VulkanAllocator::allocate(const VkMemoryAllocateInfo& allocI, MemoryInfo& memoryInfo) {
|
||||
// go through allocated memories with matching memoryType
|
||||
for (auto& deviceMemory : memory[allocI.memoryTypeIndex]) {
|
||||
/* auto bestBlockIt = deviceMemory.blocks.end(); */
|
||||
// go through blocks in memory
|
||||
for (auto block = deviceMemory.blocks.begin(); block != deviceMemory.blocks.end(); block++) {
|
||||
if (!block->free) { continue; }
|
||||
if (block->size >= allocI.allocationSize) {
|
||||
aLog.log0("allocate: memoryTypeIndex:", allocI.memoryTypeIndex,
|
||||
"size:", allocI.allocationSize, "block size:", block->size, "block offset:", block->offset);
|
||||
// if the block is larger than the needed size, split the block
|
||||
if (block->size > allocI.allocationSize) {
|
||||
// emplace free part of block after used part of block
|
||||
auto newBlock = ++block;
|
||||
block--;
|
||||
deviceMemory.blocks.emplace(newBlock,
|
||||
MemoryBlock{ block->size - allocI.allocationSize, block->offset + allocI.allocationSize, true });
|
||||
block--;
|
||||
block->size = allocI.allocationSize;
|
||||
}
|
||||
block->free = false;
|
||||
memoryInfo.memory = deviceMemory.memory;
|
||||
memoryInfo.offset = block->offset;
|
||||
memoryInfo.memoryTypeIndex = allocI.memoryTypeIndex;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if nothing is found, allocate new memory
|
||||
memory[allocI.memoryTypeIndex].emplace_back(DeviceMemory(allocI.allocationSize * TODO_ALLOCATION_SIZE_MULTIPLIIER));
|
||||
VkMemoryAllocateInfo allocI_{};
|
||||
allocI_.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocI_.memoryTypeIndex = allocI.memoryTypeIndex;
|
||||
allocI_.allocationSize = memory[allocI.memoryTypeIndex].back().size;
|
||||
aLog.log0("allocate: Allocating new memory of size:", allocI_.allocationSize, " and memoryTypeIndex:", allocI.memoryTypeIndex);
|
||||
VkResult result = vkAllocateMemory(vk.getDevice(), &allocI_, NO_ALLOC, &memory[allocI.memoryTypeIndex].back().memory);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to allocate memory", "VulkanAllocator::allocate");
|
||||
}
|
||||
|
||||
// return block of new memory
|
||||
DeviceMemory& deviceMemory = memory[allocI.memoryTypeIndex].back();
|
||||
deviceMemory.blocks.front().offset = allocI.allocationSize;
|
||||
deviceMemory.blocks.front().size -= allocI.allocationSize;
|
||||
deviceMemory.blocks.emplace_front(MemoryBlock{ allocI.allocationSize, 0, false });
|
||||
|
||||
memoryInfo.memory = deviceMemory.memory;
|
||||
memoryInfo.offset = 0;
|
||||
memoryInfo.memoryTypeIndex = allocI.memoryTypeIndex;
|
||||
}
|
||||
|
||||
|
||||
void VulkanAllocator::free(MemoryInfo& memoryInfo) {
|
||||
// go through allocated memories with matching memoryType
|
||||
for (auto deviceMemory = memory[memoryInfo.memoryTypeIndex].begin(); deviceMemory != memory[memoryInfo.memoryTypeIndex].end(); deviceMemory++) {
|
||||
if (deviceMemory->memory != memoryInfo.memory) { continue; }
|
||||
// go through blocks in memory
|
||||
for (auto block = deviceMemory->blocks.begin(); block != deviceMemory->blocks.end(); block++) {
|
||||
if (block->offset != memoryInfo.offset) { continue; }
|
||||
aLog.log0("free: Freeing block at offset", memoryInfo.offset);
|
||||
block->free = true;
|
||||
// merge with previous block, if free
|
||||
auto otherBlock = block;
|
||||
otherBlock--;
|
||||
if (otherBlock->free) {
|
||||
otherBlock->size += block->size;
|
||||
deviceMemory->blocks.erase(block);
|
||||
block = otherBlock;
|
||||
}
|
||||
// merge with next block, if free
|
||||
otherBlock = block;
|
||||
otherBlock++;
|
||||
if (otherBlock->free) {
|
||||
block->size += otherBlock->size;
|
||||
deviceMemory->blocks.erase(otherBlock);
|
||||
}
|
||||
memoryInfo.memory = VK_NULL_HANDLE;
|
||||
memoryInfo.offset = 0;
|
||||
|
||||
// if now there is only one free block, everything is free -> deallocate memory
|
||||
if (deviceMemory->blocks.size() == 1) {
|
||||
aLog.log0("free: Deallocting memory of size:", deviceMemory->size, "and memoryTypeIndex:", memoryInfo.memoryTypeIndex);
|
||||
vkFreeMemory(vk.getDevice(), deviceMemory->memory, NO_ALLOC);
|
||||
memory[memoryInfo.memoryTypeIndex].erase(deviceMemory);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw VkUserError("Trying to free block that was not allocated from this VulkanAllocator", "VulkanAllocator::free");
|
||||
}
|
||||
|
||||
|
||||
} // namespace gz::vk
|
58
src/vulkan_allocator.hpp
Normal file
58
src/vulkan_allocator.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
/* #include "vulkan_util.hpp" */
|
||||
#include <gz-util/log.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
/* #include <vulkan/vulkan_core.h> */
|
||||
|
||||
namespace gz::vk {
|
||||
struct MemoryInfo {
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
VkDeviceSize offset = 0;
|
||||
uint32_t memoryTypeIndex = 0;
|
||||
};
|
||||
// if no memory is available, allocate a chunk of multiplier * requested size
|
||||
constexpr VkDeviceSize TODO_ALLOCATION_SIZE_MULTIPLIIER = 10;
|
||||
|
||||
|
||||
// defined in vulkan_instance.hpp
|
||||
class VulkanInstance;
|
||||
// defined in vulkan_allocator.cpp
|
||||
struct DeviceMemory;
|
||||
|
||||
class VulkanAllocator {
|
||||
public:
|
||||
VulkanAllocator(VulkanInstance& instance);
|
||||
/**
|
||||
* @brief Get a block from a VkDeviceMemory
|
||||
* @details
|
||||
* Get a block of allocI.allocationSize of a VkDeviceMemory that was allocated with allocI.memoryTypeIndex.
|
||||
*
|
||||
* When this function returns, memoryInfo will contain the information about the available block.
|
||||
* You can then bind something of size allocI.allocationSize to memoryInfo.memory at offset memoryInfo.offset.
|
||||
* @throws VkException when a call to vkAllocateMemory is needed and fails
|
||||
* @todo Determine the size of new allocations
|
||||
*/
|
||||
void allocate(const VkMemoryAllocateInfo& allocI, MemoryInfo& memoryInfo);
|
||||
/**
|
||||
* @brief Free a block allocated with allocate()
|
||||
*
|
||||
* When this function returns, memoryInfo will be reset to default values
|
||||
* @throws VkUserError if memoryInfo was not allocated from this allocator
|
||||
*/
|
||||
void free(MemoryInfo& memoryInfo);
|
||||
private:
|
||||
/// allocated memory for memoryIndexType
|
||||
std::map<uint32_t, std::vector<DeviceMemory>> memory;
|
||||
Log aLog;
|
||||
VulkanInstance& vk;
|
||||
|
||||
}; // class VulkanAllocator
|
||||
} // namespace gz::vk
|
Loading…
Reference in New Issue
Block a user