diff --git a/config/Sources.cmake b/config/Sources.cmake index b8c2dcbdf87a08225cebefa3197277715845e961..0d0a37876813861c605466566eced727a70f6488 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -19,6 +19,9 @@ set(vkcv_sources ${vkcv_include}/vkcv/Window.hpp ${vkcv_source}/vkcv/Window.cpp + ${vkcv_include}/vkcv/Buffer.hpp + ${vkcv_source}/vkcv/Buffer.cpp + ${vkcv_include}/vkcv/SwapChain.hpp ${vkcv_source}/vkcv/SwapChain.cpp diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6647cbf2e2686edf615ace45e1c63c002f0fd457 --- /dev/null +++ b/include/vkcv/Buffer.hpp @@ -0,0 +1,163 @@ +#pragma once +/** + * @authors Lars Hoerttrich + * @file include/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" + + + +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); + }; + + 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); + }; + + void unmap() { + m_Device.unmapMemory(m_BufferMemory); + // TODO: mark m_DataP as invalid? + }; + + /** + * * 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; + + + 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); + } + + 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) + {} + }; +} diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 0b29819d357a7aa9494690e89bde8907a99e143c..45e555151584e60f4edd29058e5a59e076f9fafc 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -12,6 +12,7 @@ #include "vkcv/Window.hpp" #include "vkcv/PassConfig.hpp" #include "vkcv/Handles.hpp" +#include "vkcv/Buffer.hpp" #include "vkcv/PipelineConfig.hpp" #include "CommandResources.hpp" #include "SyncResources.hpp" @@ -19,9 +20,6 @@ namespace vkcv { - // TODO: - class Buffer; - // forward declarations class PassManager; class PipelineManager; @@ -138,8 +136,16 @@ namespace vkcv [[nodiscard]] PassHandle createPass(const PassConfig &config); - // TODO: - BufferHandle createBuffer(const Buffer &buf); + /** + * Creates a #Buffer with data-type T and @p bufferType + * @param bufferType Type of Buffer created + * @param size Amount of Data 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); + } /** * @brief start recording command buffers and increment frame index diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index fb9d764c6f130c3436f9b76c903e39bcd68d3ac6..3c3f91de9ac1c4490859559db2bca5bdd089ecfa 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -28,6 +28,17 @@ int main(int argc, const char** argv) { const vk::Instance& instance = context.getInstance(); const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice(); const vk::Device& device = context.getDevice(); + + struct vec3 { + float x, y, z; + }; + + auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, 3); + + vec3* m = buffer.map(); + m[0] = { 0, 0, 0 }; + m[0] = { 0, 0, 0 }; + m[0] = { 0, 0, 0 }; std::cout << "Physical device: " << physicalDevice.getProperties().deviceName << std::endl; diff --git a/src/vkcv/Buffer.cpp b/src/vkcv/Buffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..956f680064b30ac5e22ce41c622e2cb96ccecd46 --- /dev/null +++ b/src/vkcv/Buffer.cpp @@ -0,0 +1,61 @@ +/** + * @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