diff --git a/README.md b/README.md index 239a47f5f57ae9755e9a6e9c9e70d27d6482cc20..520b6242f86ccc3db6e1de78cf16ec26d550efb2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # VkCV Framework A Vulkan framework for computer visualistics simplifying building applications + + ## Build Git submodules are used for libraries. diff --git a/config/Sources.cmake b/config/Sources.cmake index 9d2c7b1f2595beb17212a6daf1b5331d8926d76d..cb73f57c2ca7278765ef0c8d01989c09a445c7b5 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 @@ -53,4 +55,10 @@ set(vkcv_sources ${vkcv_source}/vkcv/Framebuffer.cpp ${vkcv_include}/vkcv/Event.hpp + + ${vkcv_source}/vkcv/DescriptorManager.hpp + ${vkcv_source}/vkcv/DescriptorManager.cpp + + ${vkcv_include}/vkcv/DescriptorConfig.hpp + ${vkcv_source}/vkcv/DescriptorConfig.cpp ) diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp index 6647cbf2e2686edf615ace45e1c63c002f0fd457..5c21b1ce1d2d84afad6816fd8bf5f66a82b80941 100644 --- a/include/vkcv/Buffer.hpp +++ b/include/vkcv/Buffer.hpp @@ -1,163 +1,61 @@ #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 = 0, 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 = 0) { + 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, BufferMemoryType memoryType) { + return Buffer<T>(manager, manager->createBuffer(type, count * sizeof(T), memoryType), type, count, memoryType); } 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* const m_manager; + const uint64_t m_handle_id; + const BufferType m_type; + const size_t m_count; + const BufferMemoryType m_memoryType; + + Buffer<T>(BufferManager* manager, uint64_t id, BufferType type, size_t count, BufferMemoryType memoryType) : + m_manager(manager), + m_handle_id(id), + m_type(type), + m_count(count), + m_memoryType(memoryType) + {} + }; } diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..eb8cb178d1b844fff57892c7312ed71d325900cc --- /dev/null +++ b/include/vkcv/BufferManager.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include <vector> +#include <vulkan/vulkan.hpp> + +namespace vkcv +{ + enum class BufferType { + VERTEX, + UNIFORM, + STORAGE, + STAGING + }; + + enum class BufferMemoryType { + DEVICE_LOCAL, + HOST_VISIBLE + }; + + class Core; + + class BufferManager + { + friend class Core; + private: + + struct Buffer + { + vk::Buffer m_handle; + vk::DeviceMemory m_memory; + size_t m_size; + void* m_mapped = nullptr; + bool m_mappable; + }; + + Core* m_core; + std::vector<Buffer> m_buffers; + uint64_t m_stagingBuffer; + + BufferManager() noexcept; + + void init(); + + public: + ~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 + * @param memoryType Type of buffers memory + * @return New buffer handle id + */ + uint64_t createBuffer(BufferType type, size_t size, BufferMemoryType memoryType); + + /** + * 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/CommandResources.hpp b/include/vkcv/CommandResources.hpp index 05e848294935bcf3642e1712072acf607d153611..ffdd6d0315549c7522623f535856bbaffc8e5c6e 100644 --- a/include/vkcv/CommandResources.hpp +++ b/include/vkcv/CommandResources.hpp @@ -1,12 +1,25 @@ #pragma once #include <vulkan/vulkan.hpp> +#include <unordered_set> +#include "QueueManager.hpp" namespace vkcv { struct CommandResources { - vk::CommandPool commandPool; - vk::CommandBuffer commandBuffer; + std::vector<vk::CommandPool> cmdPoolPerQueueFamily; }; - CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex); - void destroyCommandResources(const vk::Device& device, const CommandResources& resources); + std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager& queueManager); + CommandResources createCommandResources(const vk::Device& device, const std::unordered_set<int> &familyIndexSet); + void destroyCommandResources(const vk::Device& device, const CommandResources& resources); + vk::CommandBuffer allocateCommandBuffer(const vk::Device& device, const vk::CommandPool cmdPool); + vk::CommandPool chooseCmdPool(const Queue &queue, const CommandResources &cmdResources); + Queue getQueueForSubmit(const QueueType type, const QueueManager &queueManager); + void beginCommandBuffer(const vk::CommandBuffer cmdBuffer, const vk::CommandBufferUsageFlags flags); + + void submitCommandBufferToQueue( + const vk::Queue queue, + const vk::CommandBuffer cmdBuffer, + const vk::Fence fence, + const std::vector<vk::Semaphore>& waitSemaphores, + const std::vector<vk::Semaphore>& signalSemaphores); } \ No newline at end of file diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index a3016f911fbade226f3a7d2b39764e9e409c837e..66db4776d053352d8ccb2eea5e09c3fb77b68561 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -17,12 +17,24 @@ #include "CommandResources.hpp" #include "SyncResources.hpp" #include "Result.hpp" +#include "vkcv/DescriptorConfig.hpp" namespace vkcv { // forward declarations class PassManager; class PipelineManager; + class DescriptorManager; + class BufferManager; + + struct SubmitInfo { + QueueType queueType; + std::vector<vk::Semaphore> waitSemaphores; + std::vector<vk::Semaphore> signalSemaphores; + }; + + typedef std::function<void(const vk::CommandBuffer& cmdBuffer)> RecordCommandFunction; + typedef std::function<void(void)> FinishCommandFunction; class Core final { @@ -49,6 +61,9 @@ namespace vkcv std::unique_ptr<PassManager> m_PassManager; std::unique_ptr<PipelineManager> m_PipelineManager; + std::unique_ptr<DescriptorManager> m_DescriptorManager; + std::unique_ptr<BufferManager> m_BufferManager; + CommandResources m_CommandResources; SyncResources m_SyncResources; uint32_t m_currentSwapchainImageIndex; @@ -146,15 +161,23 @@ 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 + * @param memoryType Type of Buffers memory * 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, BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL) { + return Buffer<T>::create(m_BufferManager.get(), type, count, memoryType); } + /** TODO: + * @param setDescriptions + * @return + */ + [[nodiscard]] + ResourcesHandle createResourceDescription(const std::vector<DescriptorSet> &descriptorSets); + /** * @brief start recording command buffers and increment frame index */ @@ -172,5 +195,16 @@ namespace vkcv void endFrame(); vk::Format getSwapchainImageFormat(); + + /** + * Submit a command buffer to any queue of selected type. The recording can be customized by a + * custom record-command-function. If the command submission has finished, an optional finish-function + * will be called. + * + * @param submitInfo Submit information + * @param record Record-command-function + * @param finish Finish-command-function or nullptr + */ + void submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish); }; } diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8116259757fe623f97a2814991b226ee27efca50 --- /dev/null +++ b/include/vkcv/DescriptorConfig.hpp @@ -0,0 +1,50 @@ +#pragma once +#include <vkcv/ShaderProgram.hpp> + +namespace vkcv +{ + /* + * All the types of descriptors (resources) that can be retrieved by the shaders + */ + enum class DescriptorType + { + UNIFORM_BUFFER, + STORAGE_BUFFER, + SAMPLER, + IMAGE + }; + + /* + * One binding for a descriptor set + * @param[in] a unique binding ID + * @param[in] a descriptor type + * @param[in] the number of descriptors of this type (arrays of the same type possible) + * @param[in] the shader stage where the descriptor is supposed to be retrieved + */ + struct DescriptorBinding + { + DescriptorBinding() = delete; + DescriptorBinding( + DescriptorType descriptorType, + uint32_t descriptorCount, + ShaderStage shaderStage + ) noexcept; + + DescriptorType descriptorType; + uint32_t descriptorCount; + ShaderStage shaderStage; + }; + + /* + * One descriptor set struct that contains all the necessary information for the actual creation. + * @param[in] a number of bindings that were created beforehand + * @param[in] the number of (identical) sets that should be created from the attached bindings + */ + struct DescriptorSet + { + DescriptorSet() = delete; + explicit DescriptorSet(std::vector<DescriptorBinding> bindings) noexcept; + + std::vector<DescriptorBinding> bindings; + }; +} diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp index 4ec2bc058409e9119695700b2b727be9426c2bcd..ad48a1ecbd41f75286cc33e88e62699468a5f11f 100644 --- a/include/vkcv/Handles.hpp +++ b/include/vkcv/Handles.hpp @@ -13,4 +13,5 @@ namespace vkcv struct BufferHandle {uint64_t id;}; struct PassHandle {uint64_t id;}; struct PipelineHandle {uint64_t id;}; + struct ResourcesHandle {uint64_t id;}; } diff --git a/include/vkcv/QueueManager.hpp b/include/vkcv/QueueManager.hpp index 9dc5fa1663c2428fe7d33b92aa353e313463bdca..ac043b2d351014ea79fcae0d0fc439bb64a87b72 100644 --- a/include/vkcv/QueueManager.hpp +++ b/include/vkcv/QueueManager.hpp @@ -2,7 +2,9 @@ #include <vulkan/vulkan.hpp> namespace vkcv { - + + enum class QueueType { Compute, Transfer, Graphics, Present }; + struct Queue { int familyIndex; int queueIndex; diff --git a/include/vkcv/SyncResources.hpp b/include/vkcv/SyncResources.hpp index 4456594722a30f73128a864714bf4690b2902525..c41019cc46ee1375a83323a6ecc877ecc1c1727a 100644 --- a/include/vkcv/SyncResources.hpp +++ b/include/vkcv/SyncResources.hpp @@ -3,11 +3,13 @@ namespace vkcv { struct SyncResources { - vk::Semaphore renderFinished; - vk::Fence swapchainImageAcquired; - vk::Fence presentFinished; + vk::Semaphore renderFinished; + vk::Semaphore swapchainImageAcquired; + vk::Fence presentFinished; }; - SyncResources createDefaultSyncResources(const vk::Device& device); - void destroySyncResources(const vk::Device& device, const SyncResources& resources); + SyncResources createSyncResources(const vk::Device &device); + void destroySyncResources(const vk::Device &device, const SyncResources &resources); + vk::Fence createFence(const vk::Device &device); + void waitForFence(const vk::Device& device, const vk::Fence fence); } \ No newline at end of file diff --git a/projects/first_triangle/CMakeLists.txt b/projects/first_triangle/CMakeLists.txt index 0a5d8ee574475fa24e217ff2be05202028ff641c..e7c8373e085df6497060b8d1d8164cf740dfb01f 100644 --- a/projects/first_triangle/CMakeLists.txt +++ b/projects/first_triangle/CMakeLists.txt @@ -15,6 +15,10 @@ add_executable(first_triangle src/main.cpp) if(MSVC) set_target_properties(first_triangle PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) set_target_properties(first_triangle PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # in addition to setting the output directory, the working directory has to be set + # by default visual studio sets the working directory to the build directory, when using the debugger + set_target_properties(first_triangle PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endif() # including headers of dependencies and the VkCV framework diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 7908831a464127306d90759a431cd221938b9d28..ff126193d94676ab966dfd3e4410c2913b54988a 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -4,7 +4,7 @@ #include <vkcv/ShaderProgram.hpp> #include <GLFW/glfw3.h> #include <vkcv/camera/CameraManager.hpp> - +#include <vkcv/DescriptorConfig.hpp> int main(int argc, const char** argv) { const char* applicationName = "First Triangle"; @@ -40,12 +40,22 @@ int main(int argc, const char** argv) { float x, y, z; }; - auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, 3); + const size_t n = 5027; - vec3* m = buffer.map(); - m[0] = { 0, 0, 0 }; - m[0] = { 0, 0, 0 }; + auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL); + vec3 vec_data [n]; + + for (size_t i = 0; i < n; i++) { + vec_data[i] = { 42, static_cast<float>(i), 7 }; + } + + buffer.fill(vec_data); + + /*vec3* m = buffer.map(); 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; @@ -88,6 +98,24 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } + //just an example + //creates 20 descriptor sets, each containing bindings for 50 uniform buffers, images, and samplers + std::vector<vkcv::DescriptorSet> sets; + + for (uint32_t i = 0; i < 20; i++) + { + vkcv::DescriptorBinding uniformBufBinding(vkcv::DescriptorType::UNIFORM_BUFFER, 50, vkcv::ShaderStage::VERTEX); + vkcv::DescriptorBinding storageBufBinding(vkcv::DescriptorType::STORAGE_BUFFER, 50, vkcv::ShaderStage::VERTEX); + vkcv::DescriptorBinding imageBinding(vkcv::DescriptorType::IMAGE, 50, vkcv::ShaderStage::VERTEX); + vkcv::DescriptorBinding samplerBinding(vkcv::DescriptorType::SAMPLER, 50, vkcv::ShaderStage::VERTEX); + + vkcv::DescriptorSet set({uniformBufBinding, storageBufBinding, imageBinding, samplerBinding}); + + sets.push_back(set); + auto resourceHandle = core.createResourceDescription(sets); + std::cout << "Resource " << resourceHandle.id << " created." << std::endl; + } + /* * BufferHandle triangleVertices = core.createBuffer(vertices); * BufferHandle triangleIndices = core.createBuffer(indices); 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..cb2425a51ee177d062120b0eb61ae089f6f1692f --- /dev/null +++ b/src/vkcv/BufferManager.cpp @@ -0,0 +1,290 @@ +/** + * @author Tobias Frisch + * @file vkcv/BufferManager.cpp + */ + +#include "vkcv/BufferManager.hpp" +#include "vkcv/Core.hpp" + +namespace vkcv { + + BufferManager::BufferManager() noexcept : + m_core(nullptr), m_buffers(), m_stagingBuffer(UINT64_MAX) + { + } + + void BufferManager::init() { + if (!m_core) { + return; + } + + m_stagingBuffer = createBuffer(BufferType::STAGING, 1024 * 1024, BufferMemoryType::HOST_VISIBLE); + } + + 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, BufferMemoryType memoryType) { + vk::BufferCreateFlags createFlags; + vk::BufferUsageFlags usageFlags; + + switch (type) { + case BufferType::VERTEX: + usageFlags = vk::BufferUsageFlagBits::eVertexBuffer; + break; + case BufferType::UNIFORM: + usageFlags = vk::BufferUsageFlagBits::eUniformBuffer; + break; + case BufferType::STORAGE: + usageFlags = vk::BufferUsageFlagBits::eStorageBuffer; + break; + case BufferType::STAGING: + usageFlags = vk::BufferUsageFlagBits::eTransferSrc; + break; + default: + // TODO: maybe an issue + break; + } + + if (memoryType == BufferMemoryType::DEVICE_LOCAL) { + usageFlags |= vk::BufferUsageFlagBits::eTransferDst; + } + + const vk::Device& device = m_core->getContext().getDevice(); + + vk::Buffer buffer = device.createBuffer( + vk::BufferCreateInfo(createFlags, size, usageFlags) + ); + + const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer); + const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice(); + + vk::MemoryPropertyFlags memoryTypeFlags; + bool mappable = false; + + switch (memoryType) { + case BufferMemoryType::DEVICE_LOCAL: + memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal; + break; + case BufferMemoryType::HOST_VISIBLE: + memoryTypeFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent; + mappable = true; + break; + default: + // TODO: maybe an issue + break; + } + + const uint32_t memoryTypeIndex = searchMemoryType( + physicalDevice.getMemoryProperties(), + requirements.memoryTypeBits, + memoryTypeFlags + ); + + vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex)); + + device.bindBufferMemory(buffer, memory, 0); + + const uint64_t id = m_buffers.size(); + m_buffers.push_back({ buffer, memory, size, nullptr, mappable }); + return id; + } + + struct StagingStepInfo { + void* data; + size_t size; + size_t offset; + + vk::Buffer buffer; + vk::Buffer stagingBuffer; + vk::DeviceMemory stagingMemory; + + size_t stagingLimit; + size_t stagingPosition; + }; + + /** + * Copies data from CPU to a staging buffer and submits the commands to copy + * each part one after another into the actual target buffer. + * + * The function can be used fully asynchronously! + * Just be careful to not use the staging buffer in parallel! + * + * @param core Core instance + * @param info Staging-info structure + */ + void copyFromStagingBuffer(Core* core, StagingStepInfo& info) { + const size_t remaining = info.size - info.stagingPosition; + const size_t mapped_size = std::min(remaining, info.stagingLimit); + + const vk::Device& device = core->getContext().getDevice(); + + void* mapped = device.mapMemory(info.stagingMemory, 0, mapped_size); + memcpy(mapped, reinterpret_cast<char*>(info.data) + info.stagingPosition, mapped_size); + device.unmapMemory(info.stagingMemory); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Transfer; + + core->submitCommands( + submitInfo, + [&info, &mapped_size](const vk::CommandBuffer& commandBuffer) { + const vk::BufferCopy region ( + 0, + info.offset + info.stagingPosition, + mapped_size + ); + + commandBuffer.copyBuffer(info.stagingBuffer, info.buffer, 1, ®ion); + }, + [&core, &info, &mapped_size, &remaining]() { + if (mapped_size < remaining) { + info.stagingPosition += mapped_size; + + copyFromStagingBuffer( + core, + info + ); + } + } + ); + } + + void BufferManager::fillBuffer(uint64_t id, void *data, size_t size, size_t offset) { + if (size == 0) { + size = SIZE_MAX; + } + + if (id >= m_buffers.size()) { + return; + } + + auto& buffer = m_buffers[id]; + + if (buffer.m_mapped) { + return; + } + + const vk::Device& device = m_core->getContext().getDevice(); + + if (offset > buffer.m_size) { + return; + } + + const size_t max_size = std::min(size, buffer.m_size - offset); + + if (buffer.m_mappable) { + void* mapped = device.mapMemory(buffer.m_memory, offset, max_size); + memcpy(mapped, data, max_size); + device.unmapMemory(buffer.m_memory); + } else { + auto& stagingBuffer = m_buffers[m_stagingBuffer]; + + StagingStepInfo info; + info.data = data; + info.size = std::min(size, max_size - offset); + info.offset = offset; + + info.buffer = buffer.m_handle; + info.stagingBuffer = stagingBuffer.m_handle; + info.stagingMemory = stagingBuffer.m_memory; + + const vk::MemoryRequirements stagingRequirements = device.getBufferMemoryRequirements(stagingBuffer.m_handle); + + info.stagingLimit = stagingRequirements.size; + info.stagingPosition = 0; + + copyFromStagingBuffer(m_core, info); + } + } + + void* BufferManager::mapBuffer(uint64_t id, size_t offset, size_t size) { + if (size == 0) { + size = SIZE_MAX; + } + + if (id >= m_buffers.size()) { + return nullptr; + } + + auto& buffer = m_buffers[id]; + + if (buffer.m_mapped) { + return nullptr; + } + + const vk::Device& device = m_core->getContext().getDevice(); + + if (offset > buffer.m_size) { + return nullptr; + } + + const size_t max_size = std::min(size, buffer.m_size - offset); + buffer.m_mapped = device.mapMemory(buffer.m_memory, offset, max_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; + } + + const vk::Device& device = m_core->getContext().getDevice(); + + 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]; + + const vk::Device& device = m_core->getContext().getDevice(); + + if (buffer.m_memory) { + device.freeMemory(buffer.m_memory); + } + + if (buffer.m_handle) { + device.destroyBuffer(buffer.m_handle); + } + } + +} diff --git a/src/vkcv/CommandResources.cpp b/src/vkcv/CommandResources.cpp index 451ec4f27b3bc68e6a787bb79d1dc12f59a086aa..71c990c3c222f2318c2f5744ff6295f667d9e6f8 100644 --- a/src/vkcv/CommandResources.cpp +++ b/src/vkcv/CommandResources.cpp @@ -1,23 +1,86 @@ #include "vkcv/CommandResources.hpp" +#include <iostream> + namespace vkcv { - CommandResources createDefaultCommandResources(const vk::Device& device, const int graphicFamilyIndex) { + + std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager &queueManager) { + std::unordered_set<int> indexSet; + for (const auto& queue : queueManager.getGraphicsQueues()) { + indexSet.insert(queue.familyIndex); + } + for (const auto& queue : queueManager.getComputeQueues()) { + indexSet.insert(queue.familyIndex); + } + for (const auto& queue : queueManager.getTransferQueues()) { + indexSet.insert(queue.familyIndex); + } + indexSet.insert(queueManager.getPresentQueue().familyIndex); + return indexSet; + } + + CommandResources createCommandResources(const vk::Device& device, const std::unordered_set<int>& familyIndexSet) { CommandResources resources; - vk::CommandPoolCreateFlags flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; - vk::CommandPoolCreateInfo poolCreateInfo(flags, graphicFamilyIndex); - resources.commandPool = device.createCommandPool(poolCreateInfo, nullptr, {}); + const size_t queueFamiliesCount = familyIndexSet.size(); + resources.cmdPoolPerQueueFamily.resize(queueFamiliesCount); - const int commandPoolCount = 1; - vk::CommandBufferAllocateInfo allocateInfo(resources.commandPool, vk::CommandBufferLevel::ePrimary, commandPoolCount); - const std::vector<vk::CommandBuffer> createdBuffers = device.allocateCommandBuffers(allocateInfo, {}); - assert(createdBuffers.size() == 1); - resources.commandBuffer = createdBuffers[0]; + const vk::CommandPoolCreateFlags poolFlags = vk::CommandPoolCreateFlagBits::eTransient; + for (const int familyIndex : familyIndexSet) { + const vk::CommandPoolCreateInfo poolCreateInfo(poolFlags, familyIndex); + resources.cmdPoolPerQueueFamily[familyIndex] = device.createCommandPool(poolCreateInfo, nullptr, {}); + } return resources; } void destroyCommandResources(const vk::Device& device, const CommandResources& resources) { - device.freeCommandBuffers(resources.commandPool, resources.commandBuffer, {}); - device.destroyCommandPool(resources.commandPool, {}); + for (const vk::CommandPool &pool : resources.cmdPoolPerQueueFamily) { + device.destroyCommandPool(pool); + } + } + + vk::CommandBuffer allocateCommandBuffer(const vk::Device& device, const vk::CommandPool cmdPool) { + const vk::CommandBufferAllocateInfo info(cmdPool, vk::CommandBufferLevel::ePrimary, 1); + return device.allocateCommandBuffers(info).front(); + } + + vk::CommandPool chooseCmdPool(const Queue& queue, const CommandResources& cmdResources) { + return cmdResources.cmdPoolPerQueueFamily[queue.familyIndex]; + } + + Queue getQueueForSubmit(const QueueType type, const QueueManager& queueManager) { + if (type == QueueType::Graphics) { + return queueManager.getGraphicsQueues().front(); + } + else if (type == QueueType::Compute) { + return queueManager.getComputeQueues().front(); + } + else if (type == QueueType::Transfer) { + return queueManager.getTransferQueues().front(); + } + else if (type == QueueType::Present) { + return queueManager.getPresentQueue(); + } + else { + std::cerr << "getQueueForSubmit error: unknown queue type" << std::endl; + return queueManager.getGraphicsQueues().front(); // graphics is the most general queue + } + } + + void beginCommandBuffer(const vk::CommandBuffer cmdBuffer, const vk::CommandBufferUsageFlags flags) { + const vk::CommandBufferBeginInfo beginInfo(flags); + cmdBuffer.begin(beginInfo); + } + + void submitCommandBufferToQueue( + const vk::Queue queue, + const vk::CommandBuffer cmdBuffer, + const vk::Fence fence, + const std::vector<vk::Semaphore>& waitSemaphores, + const std::vector<vk::Semaphore>& signalSemaphores) { + + const std::vector<vk::PipelineStageFlags> waitDstStageMasks(waitSemaphores.size(), vk::PipelineStageFlagBits::eAllCommands); + const vk::SubmitInfo queueSubmitInfo(waitSemaphores, waitDstStageMasks, cmdBuffer, signalSemaphores); + queue.submit(queueSubmitInfo, fence); } } \ No newline at end of file diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 2a8dddfc57f767089079aa17cc28bae5dcb3e555..0d61d2adf04b23b45fc4e1d7637a22c0ec3d9ccc 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -9,6 +9,8 @@ #include "vkcv/Core.hpp" #include "PassManager.hpp" #include "PipelineManager.hpp" +#include "vkcv/BufferManager.hpp" +#include "DescriptorManager.hpp" #include "Surface.hpp" #include "ImageLayoutTransitions.hpp" #include "Framebuffer.hpp" @@ -66,15 +68,16 @@ namespace vkcv const auto& queueManager = context.getQueueManager(); - const int graphicQueueFamilyIndex = queueManager.getGraphicsQueues()[0].familyIndex; - const auto defaultCommandResources = createDefaultCommandResources(context.getDevice(), graphicQueueFamilyIndex); - const auto defaultSyncResources = createDefaultSyncResources(context.getDevice()); + const int graphicQueueFamilyIndex = queueManager.getGraphicsQueues()[0].familyIndex; + const std::unordered_set<int> queueFamilySet = generateQueueFamilyIndexSet(queueManager); + const auto commandResources = createCommandResources(context.getDevice(), queueFamilySet); + const auto defaultSyncResources = createSyncResources(context.getDevice()); window.e_resize.add([&](int width, int height){ recreateSwapchain(width,height); }); - return Core(std::move(context) , window, swapChain, imageViews, defaultCommandResources, defaultSyncResources); + return Core(std::move(context) , window, swapChain, imageViews, commandResources, defaultSyncResources); } const Context &Core::getContext() const @@ -90,9 +93,14 @@ 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_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)), + m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())}, m_CommandResources(commandResources), m_SyncResources(syncResources) - {} + { + m_BufferManager->m_core = this; + m_BufferManager->init(); + } Core::~Core() noexcept { m_Context.getDevice().waitIdle(); @@ -124,25 +132,17 @@ namespace vkcv uint32_t imageIndex; const auto& acquireResult = m_Context.getDevice().acquireNextImageKHR( - m_swapchain.getSwapchain(), std::numeric_limits<uint64_t>::max(), nullptr, - m_SyncResources.swapchainImageAcquired, &imageIndex, {} + m_swapchain.getSwapchain(), + std::numeric_limits<uint64_t>::max(), + m_SyncResources.swapchainImageAcquired, + nullptr, + &imageIndex, {} ); if (acquireResult != vk::Result::eSuccess) { return Result::ERROR; } - const auto& result = m_Context.getDevice().waitForFences( - m_SyncResources.swapchainImageAcquired, true, - std::numeric_limits<uint64_t>::max() - ); - - m_Context.getDevice().resetFences(m_SyncResources.swapchainImageAcquired); - - if (result != vk::Result::eSuccess) { - return Result::ERROR; - } - m_currentSwapchainImageIndex = imageIndex; return Result::SUCCESS; } @@ -161,9 +161,6 @@ namespace vkcv m_window.pollEvents(); m_Context.getDevice().waitIdle(); // FIMXE: this is a sin against graphics programming, but its getting late - Alex destroyTemporaryFramebuffers(); - const vk::CommandBufferUsageFlags beginFlags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit; - const vk::CommandBufferBeginInfo beginInfos(beginFlags); - m_CommandResources.commandBuffer.begin(beginInfos); } void Core::renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, @@ -172,24 +169,33 @@ namespace vkcv if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { return; } - + const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); - const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f }; - const vk::ClearValue clearValues(clearColor); + const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex]; + const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle); + const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height)); - const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex]; + const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView); m_TemporaryFramebuffers.push_back(framebuffer); - const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues); - const vk::SubpassContents subpassContents = {}; - m_CommandResources.commandBuffer.beginRenderPass(beginInfo, subpassContents, {}); - - const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle); - const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); - m_CommandResources.commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); - m_CommandResources.commandBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData); - m_CommandResources.commandBuffer.draw(3, 1, 0, 0, {}); - m_CommandResources.commandBuffer.endRenderPass(); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Graphics; + submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; + submitCommands(submitInfo, [renderpass, renderArea, imageView, framebuffer, pipeline, pipelineLayout, pushConstantSize, pushConstantData](const vk::CommandBuffer& cmdBuffer) { + + const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f }; + const vk::ClearValue clearValues(clearColor); + + const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues); + const vk::SubpassContents subpassContents = {}; + cmdBuffer.beginRenderPass(beginInfo, subpassContents, {}); + + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData); + cmdBuffer.draw(3, 1, 0, 0, {}); + cmdBuffer.endRenderPass(); + }, nullptr); } void Core::endFrame() { @@ -199,18 +205,19 @@ namespace vkcv const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); const vk::Image presentImage = swapchainImages[m_currentSwapchainImageIndex]; - - m_CommandResources.commandBuffer.end(); const auto& queueManager = m_Context.getQueueManager(); - - const vk::SubmitInfo submitInfo(0, nullptr, 0, 1, &(m_CommandResources.commandBuffer), 1, &m_SyncResources.renderFinished); - queueManager.getGraphicsQueues()[0].handle.submit(submitInfo); + std::array<vk::Semaphore, 2> waitSemaphores{ + m_SyncResources.renderFinished, + m_SyncResources.swapchainImageAcquired }; vk::Result presentResult; const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain(); - const vk::PresentInfoKHR presentInfo(1, &m_SyncResources.renderFinished, 1, &swapchain, - &m_currentSwapchainImageIndex, &presentResult); + const vk::PresentInfoKHR presentInfo( + waitSemaphores, + swapchain, + m_currentSwapchainImageIndex, + presentResult); queueManager.getPresentQueue().handle.presentKHR(presentInfo); if (presentResult != vk::Result::eSuccess) { std::cout << "Error: swapchain present failed" << std::endl; @@ -225,4 +232,33 @@ namespace vkcv /* boilerplate for #34 */ std::cout << "Resized to : " << width << " , " << height << std::endl; } + + void Core::submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish) + { + const vk::Device& device = m_Context.getDevice(); + + const vkcv::Queue queue = getQueueForSubmit(submitInfo.queueType, m_Context.getQueueManager()); + const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources); + const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(device, cmdPool); + + beginCommandBuffer(cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + record(cmdBuffer); + cmdBuffer.end(); + + const vk::Fence waitFence = createFence(device); + submitCommandBufferToQueue(queue.handle, cmdBuffer, waitFence, submitInfo.waitSemaphores, submitInfo.signalSemaphores); + waitForFence(device, waitFence); + device.destroyFence(waitFence); + + device.freeCommandBuffers(cmdPool, cmdBuffer); + + if (finish) { + finish(); + } + } + + ResourcesHandle Core::createResourceDescription(const std::vector<DescriptorSet> &descriptorSets) + { + return m_DescriptorManager->createResourceDescription(descriptorSets); + } } diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f437ab6833edca09f58499b6d74307606bba8999 --- /dev/null +++ b/src/vkcv/DescriptorConfig.cpp @@ -0,0 +1,20 @@ +#include "vkcv/DescriptorConfig.hpp" + +#include <utility> + +namespace vkcv { + + DescriptorBinding::DescriptorBinding( + DescriptorType descriptorType, + uint32_t descriptorCount, + ShaderStage shaderStage + ) noexcept : + descriptorType{descriptorType}, + descriptorCount{descriptorCount}, + shaderStage{shaderStage} + {}; + + DescriptorSet::DescriptorSet(std::vector<DescriptorBinding> bindings) noexcept : + bindings{std::move(bindings)} + {}; +} diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bcebb142af2f29c23e77185112cbb83a827e41ba --- /dev/null +++ b/src/vkcv/DescriptorManager.cpp @@ -0,0 +1,125 @@ +#include "DescriptorManager.hpp" + +namespace vkcv +{ + DescriptorManager::ResourceDescription::ResourceDescription(std::vector<vk::DescriptorSet> sets, + std::vector<vk::DescriptorSetLayout> layouts) noexcept : + descriptorSets{std::move(sets)}, + descriptorSetLayouts{std::move(layouts)} + {} + DescriptorManager::DescriptorManager(vk::Device device) noexcept: + m_Device{ device }, m_NextResourceDescriptionID{ 1 } + { + /** + * Allocate a set size for the initial pool, namely 1000 units of each descriptor type below. + */ + const std::vector<vk::DescriptorPoolSize> poolSizes = {vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000)}; + + vk::DescriptorPoolCreateInfo poolInfo({}, + 1000, + static_cast<uint32_t>(poolSizes.size()), + poolSizes.data()); + + if(m_Device.createDescriptorPool(&poolInfo, nullptr, &m_Pool) != vk::Result::eSuccess) + { + std::cout << "FAILED TO ALLOCATED DESCRIPTOR POOL." << std::endl; + m_Pool = nullptr; + }; + } + + DescriptorManager::~DescriptorManager() noexcept + { + for(const auto &resource : m_ResourceDescriptions) + { + for(const auto &layout : resource.descriptorSetLayouts) + m_Device.destroyDescriptorSetLayout(layout); + } + m_Device.destroy(m_Pool); + } + + ResourcesHandle DescriptorManager::createResourceDescription(const std::vector<DescriptorSet> &descriptorSets) + { + std::vector<vk::DescriptorSet> vk_sets; + std::vector<vk::DescriptorSetLayout> vk_setLayouts; + + for (const auto &set : descriptorSets) { + std::vector<vk::DescriptorSetLayoutBinding> setBindings = {}; + + //create each set's binding + for (uint32_t j = 0; j < set.bindings.size(); j++) { + vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding( + j, + convertDescriptorTypeFlag(set.bindings[j].descriptorType), + set.bindings[j].descriptorCount, + convertShaderStageFlag(set.bindings[j].shaderStage)); + setBindings.push_back(descriptorSetLayoutBinding); + } + + //create the descriptor set's layout from the bindings gathered above + vk::DescriptorSetLayoutCreateInfo layoutInfo({}, setBindings); + vk::DescriptorSetLayout layout = nullptr; + if(m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &layout) != vk::Result::eSuccess) + { + std::cout << "FAILED TO CREATE DESCRIPTOR SET LAYOUT" << std::endl; + return ResourcesHandle{0}; + }; + vk_setLayouts.push_back(layout); + } + //create and allocate the set(s) based on the layouts that have been gathered above + vk_sets.resize(vk_setLayouts.size()); + vk::DescriptorSetAllocateInfo allocInfo(m_Pool, vk_sets.size(), vk_setLayouts.data()); + auto result = m_Device.allocateDescriptorSets(&allocInfo, vk_sets.data()); + if(result != vk::Result::eSuccess) + { + std::cout << "FAILED TO ALLOCATE DESCRIPTOR SET" << std::endl; + std::cout << vk::to_string(result) << std::endl; + for(const auto &layout : vk_setLayouts) + m_Device.destroy(layout); + + return ResourcesHandle{0}; + }; + + m_ResourceDescriptions.emplace_back(vk_sets, vk_setLayouts); + return ResourcesHandle{m_NextResourceDescriptionID++}; + } + + vk::DescriptorType DescriptorManager::convertDescriptorTypeFlag(DescriptorType type) { + switch (type) + { + case DescriptorType::UNIFORM_BUFFER: + return vk::DescriptorType::eUniformBuffer; + case DescriptorType::STORAGE_BUFFER: + return vk::DescriptorType::eStorageBuffer; + case DescriptorType::SAMPLER: + return vk::DescriptorType::eSampler; + case DescriptorType::IMAGE: + return vk::DescriptorType::eSampledImage; + default: + return vk::DescriptorType::eUniformBuffer; + } + } + + vk::ShaderStageFlagBits DescriptorManager::convertShaderStageFlag(ShaderStage stage) { + switch (stage) + { + case ShaderStage::VERTEX: + return vk::ShaderStageFlagBits::eVertex; + case ShaderStage::FRAGMENT: + return vk::ShaderStageFlagBits::eFragment; + case ShaderStage::TESS_CONTROL: + return vk::ShaderStageFlagBits::eTessellationControl; + case ShaderStage::TESS_EVAL: + return vk::ShaderStageFlagBits::eTessellationEvaluation; + case ShaderStage::GEOMETRY: + return vk::ShaderStageFlagBits::eGeometry; + case ShaderStage::COMPUTE: + return vk::ShaderStageFlagBits::eCompute; + default: + return vk::ShaderStageFlagBits::eAll; + } + } + +} \ No newline at end of file diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..744f557051bee3a6524abcf16822c3faa9c78576 --- /dev/null +++ b/src/vkcv/DescriptorManager.hpp @@ -0,0 +1,61 @@ +#include <vulkan/vulkan.hpp> + +#include "vkcv/Handles.hpp" +#include "vkcv/DescriptorConfig.hpp" + +namespace vkcv +{ + class DescriptorManager + { + public: + explicit DescriptorManager(vk::Device device) noexcept; + ~DescriptorManager() noexcept; + + /** + * Creates all vk::DescriptorSets and allocates them from the pool. + * DescriptorSets are put inside a ResourceDescription struct. + * Structs are then put into m_ResourceDescriptions. + * @param[in] vector of filled vkcv::DescriptorSet structs + * @return index into that objects a resource handle + */ + ResourcesHandle createResourceDescription(const std::vector<DescriptorSet> & descriptorSets); + + private: + vk::Device m_Device; + vk::DescriptorPool m_Pool; + + + /** + * Container for all resources requested by the user in one call of createResourceDescription. + * Includes descriptor sets and the respective descriptor set layouts. + */ + struct ResourceDescription + { + ResourceDescription() = delete; + ResourceDescription(std::vector<vk::DescriptorSet> sets, std::vector<vk::DescriptorSetLayout> layouts) noexcept; + + std::vector<vk::DescriptorSet> descriptorSets; + std::vector<vk::DescriptorSetLayout> descriptorSetLayouts; + }; + + /** + * Contains all the resource descriptions that were requested by the user in calls of createResourceDescription. + */ + std::vector<ResourceDescription> m_ResourceDescriptions; + // Counter for the vector above + uint64_t m_NextResourceDescriptionID; + + /** + * Converts the flags of the descriptor types from VulkanCV (vkcv) to Vulkan (vk). + * @param[in] vkcv flag of the DescriptorType (see DescriptorConfig.hpp) + * @return vk flag of the DescriptorType + */ + static vk::DescriptorType convertDescriptorTypeFlag(DescriptorType type); + /** + * Converts the flags of the shader stages from VulkanCV (vkcv) to Vulkan (vk). + * @param[in] vkcv flag of the ShaderStage (see ShaderProgram.hpp) + * @return vk flag of the ShaderStage + */ + static vk::ShaderStageFlagBits convertShaderStageFlag(ShaderStage stage); + }; +} \ No newline at end of file diff --git a/src/vkcv/SyncResources.cpp b/src/vkcv/SyncResources.cpp index 10d582a20501e1cc8d0bb9a98aa95b09d699c22c..9c27fe32452e0ae648565020d92891764ececb3f 100644 --- a/src/vkcv/SyncResources.cpp +++ b/src/vkcv/SyncResources.cpp @@ -1,24 +1,33 @@ #include "vkcv/SyncResources.hpp" namespace vkcv { - SyncResources createDefaultSyncResources(const vk::Device& device) { + SyncResources createSyncResources(const vk::Device& device) { SyncResources resources; const vk::SemaphoreCreateFlags semaphoreFlags = vk::SemaphoreCreateFlagBits(); const vk::SemaphoreCreateInfo semaphoreInfo(semaphoreFlags); - resources.renderFinished = device.createSemaphore(semaphoreInfo, nullptr, {}); - - const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits(); - vk::FenceCreateInfo fenceInfo(fenceFlags); - resources.presentFinished = device.createFence(fenceInfo, nullptr, {}); - resources.swapchainImageAcquired = device.createFence(fenceInfo, nullptr, {}); + resources.renderFinished = device.createSemaphore(semaphoreInfo, nullptr, {}); + resources.swapchainImageAcquired = device.createSemaphore(semaphoreInfo); + resources.presentFinished = createFence(device); + return resources; } void destroySyncResources(const vk::Device& device, const SyncResources& resources) { device.destroySemaphore(resources.renderFinished); + device.destroySemaphore(resources.swapchainImageAcquired); device.destroyFence(resources.presentFinished); - device.destroyFence(resources.swapchainImageAcquired); + } + + vk::Fence createFence(const vk::Device& device) { + const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits(); + vk::FenceCreateInfo fenceInfo(fenceFlags); + return device.createFence(fenceInfo, nullptr, {}); + } + + void waitForFence(const vk::Device& device, const vk::Fence fence) { + const auto result = device.waitForFences(fence, true, UINT64_MAX); + assert(result == vk::Result::eSuccess); } } \ No newline at end of file