diff --git a/config/Sources.cmake b/config/Sources.cmake index 0d0a37876813861c605466566eced727a70f6488..6f7dc118ce3a96b1d25d91c39385c52aa2883876 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -20,7 +20,9 @@ set(vkcv_sources ${vkcv_source}/vkcv/Window.cpp ${vkcv_include}/vkcv/Buffer.hpp - ${vkcv_source}/vkcv/Buffer.cpp + + ${vkcv_include}/vkcv/BufferManager.hpp + ${vkcv_source}/vkcv/BufferManager.cpp ${vkcv_include}/vkcv/SwapChain.hpp ${vkcv_source}/vkcv/SwapChain.cpp diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp index 6647cbf2e2686edf615ace45e1c63c002f0fd457..f2ebb96a783aaa615e0808be2849b7ba5a288ff5 100644 --- a/include/vkcv/Buffer.hpp +++ b/include/vkcv/Buffer.hpp @@ -1,163 +1,59 @@ #pragma once /** - * @authors Lars Hoerttrich - * @file include/vkcv/Buffer.hpp + * @authors Lars Hoerttrich, Tobias Frisch + * @file vkcv/Buffer.hpp * @brief template buffer class, template for type security, implemented here because template classes can't be written in .cpp */ -#include "vkcv/Handles.hpp" -#include <vulkan/vulkan.hpp> -#include "vkcv/Context.hpp" - - +#include "BufferManager.hpp" namespace vkcv { - //Enum of buffertypes - enum BufferType { VERTEX, UNIFORM, STORAGE }; - - //Functions outsourced to Buffer.cpp file: - void outsourcedDestructor(vk::Device device, vk::DeviceMemory bufferMemory, vk::Buffer buffer); - vk::Buffer outsourcedCreateBuffer(vk::Device device, BufferType type, size_t size); - vk::DeviceMemory outsourcedAllocateMemory(vk::Device device, vk::PhysicalDevice physicalDevice, vk::MemoryRequirements memoryRequirements); template<typename T> class Buffer { public: - //future bufferHandle struct - struct Handle { - uint64_t id; - - }; - // explicit destruction of default constructor Buffer<T>() = delete; - // is never called directly - ~Buffer<T>() noexcept { - outsourcedDestructor(m_Device, m_BufferMemory, m_Buffer); - } - Buffer<T>(const Buffer<T>& other) = delete; // copy-ctor - Buffer<T>(Buffer<T>&& other) noexcept : - m_Buffer(other.m_Buffer), - m_BufferMemory(other.m_BufferMemory), - m_Device(other.m_Device), - m_Type(other.m_Type), - m_Size(other.m_Size), - m_DataP(other.m_DataP) - { - other.m_Buffer = nullptr; - other.m_BufferMemory = nullptr; - other.m_Device = nullptr; - other.m_Type = vkcv::VERTEX; //set to 0 - other.m_Size = 0; - other.m_DataP = nullptr; - } // move-ctor - - Buffer<T>& operator=(const Buffer<T>& other) = delete; // copy assignment - Buffer<T>& operator=(Buffer<T>&& other) noexcept { - m_Buffer = other.m_Buffer; - m_BufferMemory = other.m_BufferMemory; - m_Device = other.m_Device; - m_Type = other.m_Type; - m_Size = other.m_Size; - m_DataP = other.m_DataP; - - other.m_Buffer = nullptr; - other.m_BufferMemory = nullptr; - other.m_Device = nullptr; - other.m_Type = vkcv::VERTEX; //set to 0 - other.m_Size = 0; - other.m_DataP = nullptr; - - }// move assignment - - BufferType getType() { return m_Type; }; - size_t getSize() { return m_Size; }; - - /** - * Maps this buffers Memory, fills this buffer with @p data of type T and count @p count - * unmaps afterwards. - * @p data Pointer to data - * @p count Amount of data of type T - */ - // TODO: we will probably need staging-buffer here later (possible add in BufferManager later?) - void fill(T* data, size_t count) { - const vk::MemoryRequirements requirements = m_Device.getBufferMemoryRequirements(m_Buffer); - - // TODO: check if mapped already - m_DataP = static_cast<uint8_t*>(m_Device.mapMemory(m_BufferMemory, 0, requirements.size)); - memcpy(m_DataP, data, sizeof(T) * count); - m_Device.unmapMemory(m_BufferMemory); + BufferType getType() { + return m_type; }; - T* map() { - const vk::MemoryRequirements requirements = m_Device.getBufferMemoryRequirements(m_Buffer); - - m_DataP = static_cast<uint8_t*>(m_Device.mapMemory(m_BufferMemory, 0, requirements.size)); - // TODO: make sure to unmap before deallocation - - return reinterpret_cast<T*>(m_DataP); - }; + size_t getCount() { + return m_count; + } - void unmap() { - m_Device.unmapMemory(m_BufferMemory); - // TODO: mark m_DataP as invalid? - }; + size_t getSize() { + return m_count * sizeof(T); + } - /** - * * Create function of #Buffer requires a @p device, a @p physicalDevice, a @p buffer type and a @p size. * - * @param device Vulkan-Device - * @param physicalDevice Vulkan-PhysicalDevice - * @param type Enum type of possible vkcv::BufferType - * @param Size size of data - */ - static Buffer<T> create(vk::Device device, vk::PhysicalDevice physicalDevice, BufferType type, size_t size) { - vk::Buffer buffer = nullptr; - + void fill(T* data, size_t count = SIZE_MAX, size_t offset = 0) { + m_manager->fillBuffer(m_handle_id, data, count * sizeof(T), offset * sizeof(T)); + } + + T* map(size_t offset = 0, size_t count = SIZE_MAX) { + return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle_id, offset * sizeof(T), count * sizeof(T))); + } - buffer = outsourcedCreateBuffer(device, type, sizeof(T) * size); - - if (!buffer) { - //TODO: potential issue - } - - //get requirements for allocation - const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer); - - vk::DeviceMemory memory= outsourcedAllocateMemory(device, physicalDevice, requirements); - - if (!memory) { - //TODO: other potential issue - } - - device.bindBufferMemory(buffer, memory, 0); - - return Buffer<T>(buffer, memory, device, type, size); + void unmap() { + m_manager->unmapBuffer(m_handle_id); + } + + static Buffer<T> create(BufferManager* manager, BufferType type, size_t count) { + return Buffer<T>(manager, manager->createBuffer(type, count * sizeof(T)), type, count); } private: - vk::Buffer m_Buffer; - vk::DeviceMemory m_BufferMemory; - vk::Device m_Device; - BufferType m_Type; - size_t m_Size=0; - uint8_t* m_DataP; - - /** - * * Constructor of #Buffer requires a @p buffer, a @p memory, @p device, @ requirement, a @p buffer type and a @p size. - * @param buffer Vulkan-Buffer - * @param memory Vulkan-DeviceMemory - * @param device Vulkan-Device - * @param requirement Vulkan-MemoryRequirements - * @param type Enum type of possible vkcv::BufferType - * @param Size size of data - */ - Buffer<T>(vk::Buffer buffer, vk::DeviceMemory memory, vk::Device device, BufferType type, size_t size) : - m_Buffer(buffer), - m_BufferMemory(memory), - m_Device(device), - m_Type(type), - m_Size(size), - m_DataP(nullptr) - {} + BufferManager* m_manager; + uint64_t m_handle_id; + BufferType m_type; + size_t m_count; + + Buffer<T>(BufferManager* manager, uint64_t id, BufferType type, size_t count) : + m_manager(manager), + m_handle_id(id), + m_type(type), + m_count(count) + {} + }; } diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1bafaf6ba5ea4ab079e9514451759d1950fd8443 --- /dev/null +++ b/include/vkcv/BufferManager.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include <vector> +#include <vulkan/vulkan.hpp> + +namespace vkcv +{ + enum BufferType { VERTEX, UNIFORM, STORAGE }; + + class BufferManager + { + friend class Core; + private: + + struct Buffer + { + vk::Buffer m_handle; + vk::DeviceMemory m_memory; + void* m_mapped = nullptr; + }; + + vk::PhysicalDevice m_physicalDevice; + vk::Device m_device; + + std::vector<Buffer> m_buffers; + + BufferManager(vk::Device device, vk::PhysicalDevice physicalDevice) noexcept; + + public: + BufferManager() = delete; + ~BufferManager() noexcept; + + BufferManager(BufferManager&& other) = delete; + BufferManager(const BufferManager& other) = delete; + + BufferManager& operator=(BufferManager&& other) = delete; + BufferManager& operator=(const BufferManager& other) = delete; + + /** + * Creates and allocates a new buffer and returns its + * unique buffer handle id. + * + * @param type Type of buffer + * @param size Size of buffer in bytes + * @return New buffer handle id + */ + uint64_t createBuffer(BufferType type, size_t size); + + /** + * Fills a buffer represented by a given buffer + * handle id with custom data. + * + * @param id Buffer handle id + * @param data Pointer to data + * @param size Size of data in bytes + * @param offset Offset to fill in data in bytes + */ + void fillBuffer(uint64_t id, void* data, size_t size, size_t offset); + + /** + * Maps memory to a buffer represented by a given + * buffer handle id and returns it. + * + * @param id Buffer handle id + * @param offset Offset of mapping in bytes + * @param size Size of mapping in bytes + * @return Pointer to mapped memory + */ + void* mapBuffer(uint64_t id, size_t offset, size_t size); + + /** + * Unmaps memory from a buffer represented by a given + * buffer handle id. + * + * @param id Buffer handle id + */ + void unmapBuffer(uint64_t id); + + /** + * Destroys and deallocates buffer represented by a given + * buffer handle id. + * + * @param id Buffer handle id + */ + void destroyBuffer(uint64_t id); + + }; + +} diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 45e555151584e60f4edd29058e5a59e076f9fafc..67bc1b3903ebcb2b49729dbe736043b58de77b1b 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -23,6 +23,7 @@ namespace vkcv // forward declarations class PassManager; class PipelineManager; + class BufferManager; class Core final { @@ -49,6 +50,7 @@ namespace vkcv std::unique_ptr<PassManager> m_PassManager; std::unique_ptr<PipelineManager> m_PipelineManager; + std::unique_ptr<BufferManager> m_BufferManager; CommandResources m_CommandResources; SyncResources m_SyncResources; uint32_t m_currentSwapchainImageIndex; @@ -138,13 +140,13 @@ namespace vkcv /** * Creates a #Buffer with data-type T and @p bufferType - * @param bufferType Type of Buffer created - * @param size Amount of Data of type T + * @param type Type of Buffer created + * @param count Count of elements of type T * return Buffer-Object */ template<typename T> - Buffer<T> createBuffer(vkcv::BufferType bufferType,size_t size) { - return Buffer<T>::create(m_Context.getDevice(), m_Context.getPhysicalDevice(), bufferType, size); + Buffer<T> createBuffer(vkcv::BufferType type, size_t count) { + return Buffer<T>::create(m_BufferManager.get(), type, count); } /** diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 3c3f91de9ac1c4490859559db2bca5bdd089ecfa..469015fe2f61e35fc95a741d4fe9fe51dc3f4bd9 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -37,8 +37,9 @@ int main(int argc, const char** argv) { vec3* m = buffer.map(); m[0] = { 0, 0, 0 }; - m[0] = { 0, 0, 0 }; - m[0] = { 0, 0, 0 }; + m[1] = { 0, 0, 0 }; + m[2] = { 0, 0, 0 }; + buffer.unmap(); std::cout << "Physical device: " << physicalDevice.getProperties().deviceName << std::endl; diff --git a/src/vkcv/Buffer.cpp b/src/vkcv/Buffer.cpp deleted file mode 100644 index 956f680064b30ac5e22ce41c622e2cb96ccecd46..0000000000000000000000000000000000000000 --- a/src/vkcv/Buffer.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @authors Lars Hoerttrich - * @file src/vkcv/Buffer.cpp - * @brief Implementation of template buffer class, template for type security - */ -#include"vkcv/Buffer.hpp" - -namespace vkcv { - - /** - * @brief searches memory type index for buffer allocation, inspired by vulkan tutorial and "https://github.com/KhronosGroup/Vulkan-Hpp/blob/master/samples/utils/utils.hpp" - * @param physicalMemoryProperties Memory Properties of physical device - * @param typeBits - * @param requirements Property flags that are required - * @return memory type index for Buffer - */ - uint32_t searchMemoryType(vk::PhysicalDeviceMemoryProperties const& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { - uint32_t memoryTypeIndex = uint32_t(0); - for (uint32_t i = 0; i < physicalMemoryProperties.memoryTypeCount; i++) - { - if ((typeBits & 1) && - ((physicalMemoryProperties.memoryTypes[i].propertyFlags & requirements) == requirements)) - { - memoryTypeIndex = i; - break; - } - typeBits >>= 1; - } - return memoryTypeIndex; - } - - void outsourcedDestructor(vk::Device device, vk::DeviceMemory bufferMemory, vk::Buffer buffer) - { - if (device) { - device.freeMemory(bufferMemory); - device.destroyBuffer(buffer); - } - } - - vk::Buffer outsourcedCreateBuffer(vk::Device device, BufferType type, size_t size) - { - switch (type) { - case VERTEX: { - //create vertex buffer - return device.createBuffer(vk::BufferCreateInfo(vk::BufferCreateFlags(), size, vk::BufferUsageFlagBits::eVertexBuffer)); - } - default: { - // TODO: maybe an issue - } - } - - return vk::Buffer(); //should never be reached - } - - vk::DeviceMemory outsourcedAllocateMemory(vk::Device device, vk::PhysicalDevice physicalDevice, vk::MemoryRequirements memoryRequirements) - { - //find Memory Type - uint32_t memoryType = searchMemoryType(physicalDevice.getMemoryProperties(), memoryRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); - return device.allocateMemory(vk::MemoryAllocateInfo(memoryRequirements.size, memoryType)); - } -} \ No newline at end of file diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a07ed463125c751593117e12874fb1312e1b03f0 --- /dev/null +++ b/src/vkcv/BufferManager.cpp @@ -0,0 +1,159 @@ +/** + * @author Tobias Frisch + * @file vkcv/BufferManager.cpp + */ + +#include "vkcv/BufferManager.hpp" + +namespace vkcv { + + BufferManager::BufferManager(vk::Device device, vk::PhysicalDevice physicalDevice) noexcept : + m_device(device), m_physicalDevice(physicalDevice) + {} + + BufferManager::~BufferManager() noexcept { + for (size_t id = 0; id < m_buffers.size(); id++) { + destroyBuffer(id); + } + } + + /** + * @brief searches memory type index for buffer allocation, inspired by vulkan tutorial and "https://github.com/KhronosGroup/Vulkan-Hpp/blob/master/samples/utils/utils.hpp" + * @param physicalMemoryProperties Memory Properties of physical device + * @param typeBits + * @param requirements Property flags that are required + * @return memory type index for Buffer + */ + uint32_t searchMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { + uint32_t memoryTypeIndex = 0; + + for (uint32_t i = 0; i < physicalMemoryProperties.memoryTypeCount; i++) + { + if ((typeBits & 1) && + ((physicalMemoryProperties.memoryTypes[i].propertyFlags & requirements) == requirements)) + { + memoryTypeIndex = i; + break; + } + + typeBits >>= 1; + } + + return memoryTypeIndex; + } + + uint64_t BufferManager::createBuffer(BufferType type, size_t size) { + vk::BufferCreateFlags createFlags; + vk::BufferUsageFlags usageFlags; + + switch (type) { + case VERTEX: + usageFlags = vk::BufferUsageFlagBits::eVertexBuffer; + break; + case UNIFORM: + usageFlags = vk::BufferUsageFlagBits::eUniformBuffer; + break; + case STORAGE: + usageFlags = vk::BufferUsageFlagBits::eStorageBuffer; + break; + default: + // TODO: maybe an issue + break; + } + + vk::Buffer buffer = m_device.createBuffer( + vk::BufferCreateInfo(createFlags, size, usageFlags) + ); + + const vk::MemoryRequirements requirements = m_device.getBufferMemoryRequirements(buffer); + + const uint32_t memoryType = searchMemoryType( + m_physicalDevice.getMemoryProperties(), + requirements.memoryTypeBits, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent + ); + + vk::DeviceMemory memory = m_device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryType)); + + const uint64_t id = m_buffers.size(); + m_buffers.push_back({ buffer, memory, nullptr }); + return id; + } + + void BufferManager::fillBuffer(uint64_t id, void *data, size_t size, size_t offset) { + if (id >= m_buffers.size()) { + return; + } + + auto& buffer = m_buffers[id]; + + if (buffer.m_mapped) { + return; + } + + const vk::MemoryRequirements requirements = m_device.getBufferMemoryRequirements(buffer.m_handle); + + if (offset > requirements.size) { + return; + } + + const size_t mapped_size = std::min(size, requirements.size - offset); + void* mapped = m_device.mapMemory(buffer.m_memory, offset, mapped_size); + memcpy(mapped, data, mapped_size); + m_device.unmapMemory(buffer.m_memory); + } + + void* BufferManager::mapBuffer(uint64_t id, size_t offset, size_t size) { + if (id >= m_buffers.size()) { + return nullptr; + } + + auto& buffer = m_buffers[id]; + + if (buffer.m_mapped) { + return nullptr; + } + + const vk::MemoryRequirements requirements = m_device.getBufferMemoryRequirements(buffer.m_handle); + + if (offset > requirements.size) { + return nullptr; + } + + const size_t mapped_size = std::min(size, requirements.size - offset); + buffer.m_mapped = m_device.mapMemory(buffer.m_memory, offset, mapped_size); + return buffer.m_mapped; + } + + void BufferManager::unmapBuffer(uint64_t id) { + if (id >= m_buffers.size()) { + return; + } + + auto& buffer = m_buffers[id]; + + if (buffer.m_mapped == nullptr) { + return; + } + + m_device.unmapMemory(buffer.m_memory); + buffer.m_mapped = nullptr; + } + + void BufferManager::destroyBuffer(uint64_t id) { + if (id >= m_buffers.size()) { + return; + } + + auto& buffer = m_buffers[id]; + + if (buffer.m_memory) { + m_device.freeMemory(buffer.m_memory); + } + + if (buffer.m_handle) { + m_device.destroyBuffer(buffer.m_handle); + } + } + +} diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 41c2ac6f821a4dbd122b65fd06c3585d1fccd79d..4ddc96f34865b26990d32be6a0b7e7f06d84d6fe 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -9,6 +9,7 @@ #include "vkcv/Core.hpp" #include "PassManager.hpp" #include "PipelineManager.hpp" +#include "vkcv/BufferManager.hpp" #include "Surface.hpp" #include "ImageLayoutTransitions.hpp" #include "Framebuffer.hpp" @@ -86,6 +87,7 @@ namespace vkcv m_swapchainImageViews(imageViews), m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)}, m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)}, + m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager(m_Context.m_Device, m_Context.m_PhysicalDevice))}, m_CommandResources(commandResources), m_SyncResources(syncResources) {}