-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tmp - Implement MemoryPool for VkImage and VkBuffer
- Loading branch information
Showing
6 changed files
with
229 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
#include <Graphics/Vulkan/MemoryPool.hpp> | ||
#include <Graphics/Vulkan/Utils.hpp> | ||
#include <Utils/Log.hpp> | ||
#include <vulkan/vulkan_core.h> | ||
|
||
namespace Dynamo::Graphics::Vulkan { | ||
MemoryPool::MemoryPool(VkDevice device, const PhysicalDevice &physical) : | ||
_device(device), _physical(&physical), _groups(physical.memory.memoryTypeCount) {} | ||
|
||
unsigned MemoryPool::find_type_index(const VkMemoryRequirements &requirements, | ||
VkMemoryPropertyFlags properties) const { | ||
unsigned type_index = 0; | ||
while (type_index < _physical->memory.memoryTypeCount) { | ||
VkMemoryType type = _physical->memory.memoryTypes[type_index]; | ||
bool has_type = requirements.memoryTypeBits & (1 << type_index); | ||
bool has_properties = (properties & type.propertyFlags) == properties; | ||
if (has_type && has_properties) { | ||
break; | ||
} | ||
type_index++; | ||
} | ||
DYN_ASSERT(type_index < _groups.size()); | ||
return type_index; | ||
} | ||
|
||
MemoryPool::Allocation MemoryPool::allocate_memory(const VkMemoryRequirements &requirements, | ||
VkMemoryPropertyFlags properties) { | ||
Allocation allocation; | ||
allocation.type = find_type_index(requirements, properties); | ||
allocation.mapped = nullptr; | ||
|
||
MemoryGroup &group = _groups[allocation.type]; | ||
for (allocation.index = 0; allocation.index < group.size(); allocation.index++) { | ||
Memory &memory = group[allocation.index]; | ||
|
||
std::optional<unsigned> result = memory.allocator.reserve(requirements.size, requirements.alignment); | ||
if (result.has_value()) { | ||
allocation.memory = memory.handle; | ||
allocation.offset = result.value(); | ||
if (memory.mapped) { | ||
allocation.mapped = static_cast<char *>(memory.mapped) + allocation.offset; | ||
} | ||
} | ||
} | ||
|
||
// None found, allocate new memory | ||
VkDeviceSize heap_size = std::max(requirements.size, MEMORY_ALLOCATION_SIZE); | ||
allocation.memory = VkDeviceMemory_allocate(_device, allocation.type, heap_size); | ||
if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { | ||
VkResult_check("Map Memory", vkMapMemory(_device, allocation.memory, 0, heap_size, 0, &allocation.mapped)); | ||
} | ||
group.push_back({allocation.memory, heap_size, allocation.mapped}); | ||
|
||
Memory &memory = group.back(); | ||
allocation.offset = memory.allocator.reserve(requirements.size, requirements.alignment).value(); | ||
return allocation; | ||
} | ||
|
||
BufferT MemoryPool::build(VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, unsigned size) { | ||
// Create the buffer | ||
BufferT buffer; | ||
buffer.handle = VkBuffer_create(_device, usage, size, nullptr, 0); | ||
|
||
// Allocate memory and bind to buffer | ||
VkMemoryRequirements requirements; | ||
vkGetBufferMemoryRequirements(_device, buffer.handle, &requirements); | ||
|
||
Allocation allocation = allocate_memory(requirements, properties); | ||
buffer.memory = allocation.memory; | ||
buffer.memory_offset = allocation.offset; | ||
buffer.memory_type = allocation.type; | ||
buffer.group_index = allocation.index; | ||
buffer.mapped = allocation.mapped; | ||
|
||
vkBindBufferMemory(_device, buffer.handle, buffer.memory, buffer.memory_offset); | ||
return buffer; | ||
} | ||
|
||
Image MemoryPool::build(const TextureDescriptor &descriptor) { | ||
// Create the image | ||
VkExtent3D extent; | ||
extent.width = descriptor.width; | ||
extent.height = descriptor.height; | ||
extent.depth = 1; | ||
|
||
VkFormat format = convert_texture_format(descriptor.format); | ||
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; | ||
|
||
Image image; | ||
image.handle = VkImage_create(_device, | ||
extent, | ||
format, | ||
VK_IMAGE_LAYOUT_UNDEFINED, | ||
VK_IMAGE_TYPE_2D, | ||
VK_IMAGE_TILING_OPTIMAL, | ||
usage, | ||
VK_SAMPLE_COUNT_1_BIT, | ||
1, | ||
1, | ||
nullptr, | ||
0); | ||
|
||
// Allocate memory and bind to image | ||
VkMemoryRequirements requirements; | ||
vkGetImageMemoryRequirements(_device, image.handle, &requirements); | ||
|
||
Allocation allocation = allocate_memory(requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); | ||
image.memory = allocation.memory; | ||
image.memory_offset = allocation.offset; | ||
image.memory_type = allocation.type; | ||
image.group_index = allocation.index; | ||
image.mapped = allocation.mapped; | ||
|
||
vkBindImageMemory(_device, image.handle, image.memory, image.memory_offset); | ||
return image; | ||
} | ||
|
||
void MemoryPool::free(const BufferT &buffer) { | ||
vkDestroyBuffer(_device, buffer.handle, nullptr); | ||
Memory &memory = _groups[buffer.memory_type][buffer.group_index]; | ||
memory.allocator.free(buffer.memory_offset); | ||
} | ||
|
||
void MemoryPool::free(const Image &image) { | ||
vkDestroyImage(_device, image.handle, nullptr); | ||
Memory &memory = _groups[image.memory_type][image.group_index]; | ||
memory.allocator.free(image.memory_offset); | ||
} | ||
|
||
void MemoryPool::destroy() { | ||
for (const MemoryGroup &group : _groups) { | ||
for (const Memory &memory : group) { | ||
vkFreeMemory(_device, memory.handle, nullptr); | ||
} | ||
} | ||
_groups.clear(); | ||
} | ||
} // namespace Dynamo::Graphics::Vulkan |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#pragma once | ||
|
||
#include <vulkan/vulkan_core.h> | ||
|
||
#include <Graphics/Texture.hpp> | ||
#include <Graphics/Vulkan/PhysicalDevice.hpp> | ||
#include <Utils/Allocator.hpp> | ||
|
||
namespace Dynamo::Graphics::Vulkan { | ||
/** | ||
* @brief 256M minimum buffer allocation size. | ||
* | ||
* We only have 4096 guaranteed allocations. 256M * 4096 is approx. 1T, so this should be enough. | ||
* | ||
*/ | ||
constexpr VkDeviceSize MEMORY_ALLOCATION_SIZE = 256 * (1 << 20); | ||
|
||
struct BufferT { | ||
VkBuffer handle; | ||
VkDeviceMemory memory; | ||
unsigned memory_offset; | ||
unsigned memory_type; | ||
unsigned group_index; | ||
void *mapped; | ||
}; | ||
|
||
struct Image { | ||
VkImage handle; | ||
VkDeviceMemory memory; | ||
unsigned memory_offset; | ||
unsigned memory_type; | ||
unsigned group_index; | ||
void *mapped; | ||
}; | ||
|
||
class MemoryPool { | ||
struct Memory { | ||
VkDeviceMemory handle; | ||
Allocator allocator; | ||
void *mapped; | ||
}; | ||
using MemoryGroup = std::vector<Memory>; | ||
|
||
struct Allocation { | ||
VkDeviceMemory memory; | ||
unsigned offset; | ||
unsigned type; | ||
unsigned index; | ||
void *mapped; | ||
}; | ||
|
||
VkDevice _device; | ||
const PhysicalDevice *_physical; | ||
std::vector<MemoryGroup> _groups; | ||
|
||
unsigned find_type_index(const VkMemoryRequirements &requirements, VkMemoryPropertyFlags properties) const; | ||
|
||
Allocation allocate_memory(const VkMemoryRequirements &requirements, VkMemoryPropertyFlags properties); | ||
|
||
public: | ||
MemoryPool(VkDevice device, const PhysicalDevice &physical); | ||
MemoryPool() = default; | ||
|
||
BufferT build(VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, unsigned size); | ||
|
||
Image build(const TextureDescriptor &descriptor); | ||
|
||
void free(const BufferT &allocation); | ||
|
||
void free(const Image &allocation); | ||
|
||
void destroy(); | ||
}; | ||
}; // namespace Dynamo::Graphics::Vulkan |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters