Add VideoMemoryAllocator
This commit is contained in:
parent
a31ef34262
commit
ae707f1f3d
@ -1,5 +1,5 @@
|
|||||||
# NF library CMakeLists.txt
|
# NF library CMakeLists.txt
|
||||||
add_library(NothinFancy STATIC "src/Engine.cpp" "src/include/nf.h" "src/pch.h" "src/util.h" "src/util/log.h" "src/util/log.cpp" "src/include/nf/config.h" "src/util/util.cpp" "src/util/file.h" "src/util/file.cpp" "src/client/Client.h" "src/client/Client.cpp" "src/client/Window.h" "src/client/Window.cpp" "src/client/render/RenderEngine.h" "src/client/render/RenderEngine.cpp" "src/client/render/ShaderModule.h" "src/client/render/ShaderModule.cpp" "src/client/render/VulkanResource.h" "src/client/render/Buffer.h" "src/client/render/Buffer.cpp")
|
add_library(NothinFancy STATIC "src/Engine.cpp" "src/include/nf.h" "src/pch.h" "src/util.h" "src/util/log.h" "src/util/log.cpp" "src/include/nf/config.h" "src/util/util.cpp" "src/util/file.h" "src/util/file.cpp" "src/client/Client.h" "src/client/Client.cpp" "src/client/Window.h" "src/client/Window.cpp" "src/client/render/RenderEngine.h" "src/client/render/RenderEngine.cpp" "src/client/render/ShaderModule.h" "src/client/render/ShaderModule.cpp" "src/client/render/Resource.h" "src/client/render/Buffer.h" "src/client/render/Buffer.cpp" "src/client/render/VideoMemoryAllocator.h" "src/client/render/VideoMemoryAllocator.cpp")
|
||||||
|
|
||||||
# Use C++20
|
# Use C++20
|
||||||
set_property(TARGET NothinFancy PROPERTY CXX_STANDARD 20)
|
set_property(TARGET NothinFancy PROPERTY CXX_STANDARD 20)
|
||||||
|
@ -18,8 +18,18 @@ namespace nf::client {
|
|||||||
std::thread inputThread(&Client::runInputThread, this, std::move(windowPromise));
|
std::thread inputThread(&Client::runInputThread, this, std::move(windowPromise));
|
||||||
m_renderEngine = std::make_unique<render::RenderEngine>(std::move(windowFuture.get()), m_config.display);
|
m_renderEngine = std::make_unique<render::RenderEngine>(std::move(windowFuture.get()), m_config.display);
|
||||||
|
|
||||||
|
auto fpsClock1 = std::chrono::high_resolution_clock::now(), fpsClock2 = fpsClock1;
|
||||||
|
unsigned int frame = 0;
|
||||||
while (m_running) {
|
while (m_running) {
|
||||||
m_renderEngine->doFrame();
|
m_renderEngine->doFrame();
|
||||||
|
frame++;
|
||||||
|
fpsClock2 = std::chrono::high_resolution_clock::now();
|
||||||
|
std::chrono::duration<double> duration = fpsClock2 - fpsClock1;
|
||||||
|
if (duration.count() >= 1.0) {
|
||||||
|
NFLog(std::format("FPS: {}", frame));
|
||||||
|
frame = 0;
|
||||||
|
fpsClock1 = std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputThread.join();
|
inputThread.join();
|
||||||
|
@ -5,18 +5,24 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
Buffer::Buffer(BufferType type, const VkDevice& device, const VkPhysicalDevice& physicalDevice, const VkCommandPool& commandPool, const VkQueue& queue, void* bufferData, size_t bufferSize)
|
Buffer::Buffer(BufferType type, const VkDevice& device, VideoMemoryAllocator& allocator, const VkCommandPool& commandPool, const VkQueue& queue, void* bufferData, size_t bufferSize)
|
||||||
: VulkanResource(device)
|
: Resource(device)
|
||||||
, m_handle()
|
, m_handle()
|
||||||
, m_memory()
|
, m_memory()
|
||||||
|
, m_offset()
|
||||||
|
, m_allocator(allocator)
|
||||||
{
|
{
|
||||||
VkBuffer stagingBuffer = nullptr;
|
VkBuffer stagingBuffer = nullptr;
|
||||||
VkDeviceMemory stagingBufferMemory = nullptr;
|
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, stagingBuffer);
|
||||||
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, physicalDevice, stagingBuffer, stagingBufferMemory);
|
|
||||||
|
|
||||||
void* bufferPointer = nullptr;
|
VkDeviceMemory stagingBufferMemory = nullptr;
|
||||||
vkMapMemory(m_device, stagingBufferMemory, 0, bufferSize, 0, &bufferPointer);
|
VkDeviceSize stagingBufferMemoryOffset = 0;
|
||||||
std::memcpy(bufferPointer, bufferData, bufferSize);
|
m_allocator.allocateForBuffer(stagingBuffer, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBufferMemory, stagingBufferMemoryOffset);
|
||||||
|
vkBindBufferMemory(m_device, stagingBuffer, stagingBufferMemory, stagingBufferMemoryOffset);
|
||||||
|
|
||||||
|
void* stagingBufferPointer = nullptr;
|
||||||
|
vkMapMemory(m_device, stagingBufferMemory, stagingBufferMemoryOffset, bufferSize, 0, &stagingBufferPointer);
|
||||||
|
std::memcpy(stagingBufferPointer, bufferData, bufferSize);
|
||||||
vkUnmapMemory(m_device, stagingBufferMemory);
|
vkUnmapMemory(m_device, stagingBufferMemory);
|
||||||
|
|
||||||
VkBufferUsageFlags mainUsage = NULL;
|
VkBufferUsageFlags mainUsage = NULL;
|
||||||
@ -31,50 +37,26 @@ namespace nf::client::render {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | mainUsage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, physicalDevice, m_handle, m_memory);
|
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | mainUsage, m_handle);
|
||||||
|
m_allocator.allocateForBuffer(m_handle, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_memory, m_offset);
|
||||||
|
vkBindBufferMemory(m_device, m_handle, m_memory, m_offset);
|
||||||
|
|
||||||
copyBuffer(stagingBuffer, m_handle, bufferSize, commandPool, queue);
|
copyBuffer(stagingBuffer, m_handle, bufferSize, commandPool, queue);
|
||||||
|
|
||||||
destroyBuffer(stagingBuffer, stagingBufferMemory);
|
destroyBuffer(stagingBuffer, stagingBufferMemory, stagingBufferMemoryOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
const VkBuffer& Buffer::getHandle() const {
|
const VkBuffer& Buffer::getHandle() const {
|
||||||
return m_handle;
|
return m_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkPhysicalDevice physicalDevice, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
|
void Buffer::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer& buffer) {
|
||||||
VkBufferCreateInfo bufferCI = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
VkBufferCreateInfo bufferCI = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||||
bufferCI.size = size;
|
bufferCI.size = size;
|
||||||
bufferCI.usage = usage;
|
bufferCI.usage = usage;
|
||||||
|
|
||||||
if (vkCreateBuffer(m_device, &bufferCI, nullptr, &buffer) != VK_SUCCESS)
|
if (vkCreateBuffer(m_device, &bufferCI, nullptr, &buffer) != VK_SUCCESS)
|
||||||
NFError("Could not create vertex buffer.");
|
NFError("Could not create vertex buffer.");
|
||||||
|
|
||||||
VkMemoryRequirements memoryRequirements = {};
|
|
||||||
vkGetBufferMemoryRequirements(m_device, buffer, &memoryRequirements);
|
|
||||||
|
|
||||||
VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
|
||||||
allocateInfo.allocationSize = memoryRequirements.size;
|
|
||||||
allocateInfo.memoryTypeIndex = findMemoryType(physicalDevice, memoryRequirements.memoryTypeBits, memoryProperties);
|
|
||||||
|
|
||||||
if (allocateInfo.memoryTypeIndex == -1)
|
|
||||||
NFError("Could not find suitable memory type.");
|
|
||||||
|
|
||||||
if (vkAllocateMemory(m_device, &allocateInfo, nullptr, &bufferMemory) != VK_SUCCESS)
|
|
||||||
NFError("Could not allocate buffer memory.");
|
|
||||||
|
|
||||||
vkBindBufferMemory(m_device, buffer, bufferMemory, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Buffer::findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) {
|
|
||||||
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
|
||||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
|
|
||||||
|
|
||||||
for (int i = 0; i < memoryProperties.memoryTypeCount; i++)
|
|
||||||
if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::copyBuffer(VkBuffer bufferSource, VkBuffer bufferDestination, VkDeviceSize size, VkCommandPool commandPool, VkQueue queue) {
|
void Buffer::copyBuffer(VkBuffer bufferSource, VkBuffer bufferDestination, VkDeviceSize size, VkCommandPool commandPool, VkQueue queue) {
|
||||||
@ -111,12 +93,12 @@ namespace nf::client::render {
|
|||||||
vkFreeCommandBuffers(m_device, commandPool, 1, &commandBuffer);
|
vkFreeCommandBuffers(m_device, commandPool, 1, &commandBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::destroyBuffer(VkBuffer buffer, VkDeviceMemory bufferMemory) {
|
void Buffer::destroyBuffer(VkBuffer buffer, VkDeviceMemory bufferMemory, VkDeviceSize offset) {
|
||||||
vkFreeMemory(m_device, bufferMemory, nullptr);
|
m_allocator.deallocate(bufferMemory, offset);
|
||||||
vkDestroyBuffer(m_device, buffer, nullptr);
|
vkDestroyBuffer(m_device, buffer, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer::~Buffer() {
|
Buffer::~Buffer() {
|
||||||
destroyBuffer(m_handle, m_memory);
|
destroyBuffer(m_handle, m_memory, m_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// Buffer class header
|
// Buffer class header
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "VulkanResource.h"
|
#include "Resource.h"
|
||||||
|
#include "VideoMemoryAllocator.h"
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
enum class BufferType {
|
enum class BufferType {
|
||||||
@ -9,21 +10,24 @@ namespace nf::client::render {
|
|||||||
Index
|
Index
|
||||||
};
|
};
|
||||||
|
|
||||||
class Buffer : VulkanResource {
|
class Buffer : Resource {
|
||||||
public:
|
public:
|
||||||
Buffer(BufferType type, const VkDevice& device, const VkPhysicalDevice& physicalDevice, const VkCommandPool& commandPool, const VkQueue& queue, void* bufferData, size_t bufferSize);
|
Buffer(BufferType type, const VkDevice& device, VideoMemoryAllocator& allocator, const VkCommandPool& commandPool, const VkQueue& queue, void* bufferData, size_t bufferSize);
|
||||||
|
|
||||||
const VkBuffer& getHandle() const;
|
const VkBuffer& getHandle() const;
|
||||||
|
|
||||||
~Buffer();
|
~Buffer();
|
||||||
private:
|
private:
|
||||||
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkPhysicalDevice physicalDevice, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
|
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer& buffer);
|
||||||
uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties);
|
|
||||||
void copyBuffer(VkBuffer sourceBuffer, VkBuffer destinationBuffer, VkDeviceSize size, VkCommandPool commandPool, VkQueue queue);
|
void copyBuffer(VkBuffer sourceBuffer, VkBuffer destinationBuffer, VkDeviceSize size, VkCommandPool commandPool, VkQueue queue);
|
||||||
|
|
||||||
void destroyBuffer(VkBuffer buffer, VkDeviceMemory bufferMemory);
|
void destroyBuffer(VkBuffer buffer, VkDeviceMemory bufferMemory, VkDeviceSize offset);
|
||||||
|
|
||||||
|
|
||||||
VkBuffer m_handle;
|
VkBuffer m_handle;
|
||||||
VkDeviceMemory m_memory;
|
VkDeviceMemory m_memory;
|
||||||
|
VkDeviceSize m_offset;
|
||||||
|
|
||||||
|
VideoMemoryAllocator& m_allocator;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ namespace nf::client::render {
|
|||||||
, m_numFramesInFlight(2)
|
, m_numFramesInFlight(2)
|
||||||
, m_frameExecutors()
|
, m_frameExecutors()
|
||||||
, m_currentFrameExecutor()
|
, m_currentFrameExecutor()
|
||||||
|
, m_allocator()
|
||||||
, m_bufferVertex()
|
, m_bufferVertex()
|
||||||
, m_bufferIndex()
|
, m_bufferIndex()
|
||||||
{
|
{
|
||||||
@ -52,6 +53,7 @@ namespace nf::client::render {
|
|||||||
createOutputPipeline();
|
createOutputPipeline();
|
||||||
createFrameExecutors();
|
createFrameExecutors();
|
||||||
|
|
||||||
|
m_allocator = std::make_unique<VideoMemoryAllocator>(m_device, m_physicalDevice);
|
||||||
createBuffers();
|
createBuffers();
|
||||||
|
|
||||||
m_window->show();
|
m_window->show();
|
||||||
@ -452,14 +454,14 @@ namespace nf::client::render {
|
|||||||
};
|
};
|
||||||
|
|
||||||
size_t triangleVerticesSize = sizeof(triangleVertices[0]) * triangleVertices.size();
|
size_t triangleVerticesSize = sizeof(triangleVertices[0]) * triangleVertices.size();
|
||||||
m_bufferVertex = new Buffer(BufferType::Vertex, m_device, m_physicalDevice, m_commandPool, m_queueGraphics, triangleVertices.data(), triangleVerticesSize);
|
m_bufferVertex = std::make_unique<Buffer>(BufferType::Vertex, m_device, *m_allocator, m_commandPool, m_queueGraphics, triangleVertices.data(), triangleVerticesSize);
|
||||||
|
|
||||||
std::vector<uint32_t> triangleIndices = {
|
std::vector<uint32_t> triangleIndices = {
|
||||||
0, 1, 2, 2, 3, 0
|
0, 1, 2, 2, 3, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t triangleIndicesSize = sizeof(triangleIndices[0]) * triangleIndices.size();
|
size_t triangleIndicesSize = sizeof(triangleIndices[0]) * triangleIndices.size();
|
||||||
m_bufferIndex = new Buffer(BufferType::Index, m_device, m_physicalDevice, m_commandPool, m_queueGraphics, triangleIndices.data(), triangleIndicesSize);
|
m_bufferIndex = std::make_unique<Buffer>(BufferType::Index, m_device, *m_allocator, m_commandPool, m_queueGraphics, triangleIndices.data(), triangleIndicesSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderEngine::doFrame() {
|
void RenderEngine::doFrame() {
|
||||||
@ -591,8 +593,8 @@ namespace nf::client::render {
|
|||||||
RenderEngine::~RenderEngine() {
|
RenderEngine::~RenderEngine() {
|
||||||
waitIdle();
|
waitIdle();
|
||||||
|
|
||||||
delete m_bufferIndex;
|
m_bufferIndex.reset();
|
||||||
delete m_bufferVertex;
|
m_bufferVertex.reset();
|
||||||
|
|
||||||
for (int i = 0; i < m_frameExecutors.size(); i++) {
|
for (int i = 0; i < m_frameExecutors.size(); i++) {
|
||||||
vkDestroyFence(m_device, m_frameExecutors[i].fenceFrameInFlight, nullptr);
|
vkDestroyFence(m_device, m_frameExecutors[i].fenceFrameInFlight, nullptr);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "client/Window.h"
|
#include "client/Window.h"
|
||||||
#include "nf/config.h"
|
#include "nf/config.h"
|
||||||
|
#include "VideoMemoryAllocator.h"
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
@ -68,7 +69,8 @@ namespace nf::client::render {
|
|||||||
std::vector<FrameExecutor> m_frameExecutors;
|
std::vector<FrameExecutor> m_frameExecutors;
|
||||||
uint32_t m_currentFrameExecutor;
|
uint32_t m_currentFrameExecutor;
|
||||||
|
|
||||||
Buffer* m_bufferVertex;
|
std::unique_ptr<VideoMemoryAllocator> m_allocator;
|
||||||
Buffer* m_bufferIndex;
|
std::unique_ptr<Buffer> m_bufferVertex;
|
||||||
|
std::unique_ptr<Buffer> m_bufferIndex;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
class VulkanResource {
|
class Resource {
|
||||||
public:
|
public:
|
||||||
VulkanResource(const VkDevice& device)
|
Resource(const VkDevice& device)
|
||||||
: m_device(device)
|
: m_device(device)
|
||||||
{}
|
{}
|
||||||
|
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
ShaderModule::ShaderModule(const VkDevice& device, const std::string& shaderBinary)
|
ShaderModule::ShaderModule(const VkDevice& device, const std::string& shaderBinary)
|
||||||
: VulkanResource(device)
|
: Resource(device)
|
||||||
, m_shaderModule()
|
, m_shaderModule()
|
||||||
{
|
{
|
||||||
VkShaderModuleCreateInfo shaderModuleCI = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
VkShaderModuleCreateInfo shaderModuleCI = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// ShaderModule class header
|
// ShaderModule class header
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "VulkanResource.h"
|
#include "Resource.h"
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
class ShaderModule : VulkanResource {
|
class ShaderModule : Resource {
|
||||||
public:
|
public:
|
||||||
ShaderModule(const VkDevice& device, const std::string& shaderBinary);
|
ShaderModule(const VkDevice& device, const std::string& shaderBinary);
|
||||||
|
|
||||||
|
130
NothinFancy/src/client/render/VideoMemoryAllocator.cpp
Normal file
130
NothinFancy/src/client/render/VideoMemoryAllocator.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// VideoMemoryAllocator class implementation
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include "VideoMemoryAllocator.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
// 1 GiB
|
||||||
|
#define HEAP_SIZE_LARGE 1024ull * 1024 * 1024
|
||||||
|
// 50 MiB
|
||||||
|
#define DEFAULT_ALLOCATION_AMOUNT 50ull * 1024 * 1024
|
||||||
|
|
||||||
|
namespace nf::client::render {
|
||||||
|
VideoMemoryAllocator::VideoMemoryAllocator(const VkDevice& device, const VkPhysicalDevice& physicalDevice)
|
||||||
|
: m_device(device)
|
||||||
|
, m_physicalDevice(physicalDevice)
|
||||||
|
, m_blocks()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void VideoMemoryAllocator::allocateForBuffer(VkBuffer buffer, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceMemory& outDeviceMemory, VkDeviceSize& outOffset) {
|
||||||
|
VkMemoryRequirements memoryRequirements = {};
|
||||||
|
vkGetBufferMemoryRequirements(m_device, buffer, &memoryRequirements);
|
||||||
|
|
||||||
|
VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||||
|
allocateInfo.allocationSize = memoryRequirements.size;
|
||||||
|
allocateInfo.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryPropertyFlags);
|
||||||
|
|
||||||
|
if (allocateInfo.memoryTypeIndex == -1)
|
||||||
|
NFError("Could not find suitable memory type.");
|
||||||
|
|
||||||
|
allocateFromBlock(allocateInfo, memoryRequirements.alignment, outDeviceMemory, outOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoMemoryAllocator::deallocate(VkDeviceMemory memoryBlock, VkDeviceSize offset) {
|
||||||
|
const auto blockIterator = std::find_if(m_blocks.begin(), m_blocks.end(), [&](const auto& currentBlock) {
|
||||||
|
if (currentBlock.memory == memoryBlock)
|
||||||
|
return true;
|
||||||
|
else return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (blockIterator == m_blocks.end())
|
||||||
|
NFError("Could not find video memory block to deallocate from.");
|
||||||
|
|
||||||
|
MemoryBlock& block = *blockIterator;
|
||||||
|
|
||||||
|
const auto allocationIterator = block.allocations.find(offset);
|
||||||
|
|
||||||
|
if (allocationIterator == block.allocations.end())
|
||||||
|
NFError("Could not find video memory allocation to deallocate.");
|
||||||
|
|
||||||
|
block.allocations.erase(allocationIterator);
|
||||||
|
|
||||||
|
// If block is now empty, free it
|
||||||
|
if (block.allocations.empty()) {
|
||||||
|
vkFreeMemory(m_device, block.memory, nullptr);
|
||||||
|
m_blocks.erase(blockIterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t VideoMemoryAllocator::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags desiredPropertyFlags) {
|
||||||
|
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
||||||
|
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
|
||||||
|
|
||||||
|
for (int i = 0; i < memoryProperties.memoryTypeCount; i++)
|
||||||
|
if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & desiredPropertyFlags) == desiredPropertyFlags)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoMemoryAllocator::allocateFromBlock(VkMemoryAllocateInfo& allocateInfo, uint32_t alignment, VkDeviceMemory& outDeviceMemory, VkDeviceSize& outOffset) {
|
||||||
|
while (true) {
|
||||||
|
for (auto& block : m_blocks) {
|
||||||
|
if (block.memoryTypeIndex == allocateInfo.memoryTypeIndex) {
|
||||||
|
VkDeviceSize lastOffset = 0, lastSize = 0;
|
||||||
|
// Check between existing allocations
|
||||||
|
for (const auto& [currentOffset, currentSize] : block.allocations) {
|
||||||
|
// Check for alignment
|
||||||
|
VkDeviceSize checkOffset = lastOffset + lastSize, remainder = checkOffset % alignment;
|
||||||
|
checkOffset = remainder ? checkOffset + (alignment - remainder) : checkOffset;
|
||||||
|
// Check for enough space
|
||||||
|
if (currentOffset - lastOffset - lastSize >= allocateInfo.allocationSize) {
|
||||||
|
outDeviceMemory = block.memory;
|
||||||
|
outOffset = checkOffset;
|
||||||
|
block.allocations[checkOffset] = allocateInfo.allocationSize;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastOffset = currentOffset, lastSize = currentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check end
|
||||||
|
VkDeviceSize checkOffset = lastOffset + lastSize, remainder = checkOffset % alignment;
|
||||||
|
checkOffset = remainder ? checkOffset + (alignment - remainder) : checkOffset;
|
||||||
|
if ((block.size - 1) - lastOffset - lastSize >= allocateInfo.allocationSize) {
|
||||||
|
outDeviceMemory = block.memory;
|
||||||
|
outOffset = checkOffset;
|
||||||
|
block.allocations[checkOffset] = allocateInfo.allocationSize;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not enough space, continue
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No suitable block found, allocate one
|
||||||
|
VkMemoryAllocateInfo newBlockAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||||
|
newBlockAllocateInfo.memoryTypeIndex = allocateInfo.memoryTypeIndex;
|
||||||
|
newBlockAllocateInfo.allocationSize = calculateBlockSize(allocateInfo.memoryTypeIndex);
|
||||||
|
allocateBlock(newBlockAllocateInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t VideoMemoryAllocator::calculateBlockSize(uint32_t memoryTypeIndex) const {
|
||||||
|
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
||||||
|
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
|
||||||
|
size_t heapSize = memoryProperties.memoryHeaps[memoryProperties.memoryTypes[memoryTypeIndex].heapIndex].size;
|
||||||
|
|
||||||
|
return heapSize > HEAP_SIZE_LARGE ? DEFAULT_ALLOCATION_AMOUNT : (heapSize / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoMemoryAllocator::allocateBlock(VkMemoryAllocateInfo& allocateInfo) {
|
||||||
|
VkDeviceMemory memoryBlock = nullptr;
|
||||||
|
|
||||||
|
if (vkAllocateMemory(m_device, &allocateInfo, nullptr, &memoryBlock) != VK_SUCCESS)
|
||||||
|
NFError("Could not allocate video memory.");
|
||||||
|
|
||||||
|
m_blocks.emplace_back(memoryBlock, allocateInfo.memoryTypeIndex, allocateInfo.allocationSize);
|
||||||
|
}
|
||||||
|
}
|
32
NothinFancy/src/client/render/VideoMemoryAllocator.h
Normal file
32
NothinFancy/src/client/render/VideoMemoryAllocator.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// VideoMemoryAllocator class header
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace nf::client::render {
|
||||||
|
struct MemoryBlock {
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
uint32_t memoryTypeIndex;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
std::map<VkDeviceSize, VkDeviceSize> allocations;
|
||||||
|
// Offset Size
|
||||||
|
};
|
||||||
|
|
||||||
|
class VideoMemoryAllocator {
|
||||||
|
public:
|
||||||
|
VideoMemoryAllocator(const VkDevice& device, const VkPhysicalDevice& physicalDevice);
|
||||||
|
|
||||||
|
void allocateForBuffer(VkBuffer buffer, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceMemory& outDeviceMemory, VkDeviceSize& outOffset);
|
||||||
|
void deallocate(VkDeviceMemory memoryBlock, VkDeviceSize offset);
|
||||||
|
private:
|
||||||
|
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags desiredPropertyFlags);
|
||||||
|
|
||||||
|
void allocateFromBlock(VkMemoryAllocateInfo& allocateInfo, uint32_t alignment, VkDeviceMemory& outDeviceMemory, VkDeviceSize& outOffset);
|
||||||
|
size_t calculateBlockSize(uint32_t memoryTypeIndex) const;
|
||||||
|
void allocateBlock(VkMemoryAllocateInfo& allocateInfo);
|
||||||
|
|
||||||
|
const VkDevice& m_device;
|
||||||
|
const VkPhysicalDevice& m_physicalDevice;
|
||||||
|
|
||||||
|
std::vector<MemoryBlock> m_blocks;
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user